pax_global_header00006660000000000000000000000064151427023260014514gustar00rootroot0000000000000052 comment=8c6ebf895f5a3ae9f3a646cebc7f83063fc3d7a0 openvswitch-3.7.0~git20260211.8c6ebf8/000077500000000000000000000000001514270232600167605ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/.ci/000077500000000000000000000000001514270232600174315ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/.ci/dpdk-build.sh000077500000000000000000000037721514270232600220200ustar00rootroot00000000000000#!/bin/bash set -o errexit set -x function build_dpdk() { local DPDK_VER=$1 local DPDK_OPTS="" local DPDK_INSTALL_DIR="$(pwd)/dpdk-dir" local VERSION_FILE="$DPDK_INSTALL_DIR/cached-version" rm -rf dpdk-src rm -rf $DPDK_INSTALL_DIR if [ "${DPDK_VER##refs/*/}" != "${DPDK_VER}" ]; then git clone --single-branch $DPDK_GIT dpdk-src -b "${DPDK_VER##refs/*/}" pushd dpdk-src git log -1 --oneline else wget https://fast.dpdk.org/rel/dpdk-$1.tar.xz tar xvf dpdk-$1.tar.xz > /dev/null DIR_NAME=$(tar -tf dpdk-$1.tar.xz | head -1 | cut -f1 -d"/") mv ${DIR_NAME} dpdk-src pushd dpdk-src fi # Switching to 'generic' platform to make the dpdk cache usable on # different CPUs. We can't be sure that all CI machines are exactly same. DPDK_OPTS="$DPDK_OPTS -Dplatform=generic" # Disable building DPDK unit tests. Not needed for OVS build or tests. DPDK_OPTS="$DPDK_OPTS -Dtests=false" # Disable DPDK developer mode, this results in less build checks and less # meson verbose outputs. DPDK_OPTS="$DPDK_OPTS -Ddeveloper_mode=disabled" # OVS compilation and "normal" unit tests (run in the CI) do not depend on # any DPDK driver. # check-dpdk unit tests requires testpmd and some net/ driver. DPDK_OPTS="$DPDK_OPTS -Denable_apps=test-pmd" enable_drivers="net/null,net/af_xdp,net/tap,net/virtio" DPDK_OPTS="$DPDK_OPTS -Denable_drivers=$enable_drivers" # OVS depends on the vhost library (and its dependencies). # net/tap depends on the gso library. DPDK_OPTS="$DPDK_OPTS -Denable_libs=cryptodev,dmadev,gso,vhost" # Install DPDK using prefix. DPDK_OPTS="$DPDK_OPTS --prefix=$DPDK_INSTALL_DIR" meson setup $DPDK_OPTS build ninja -C build ninja -C build install popd # Remove examples sources. rm -rf $DPDK_INSTALL_DIR/share/dpdk/examples echo "Installed DPDK in $DPDK_INSTALL_DIR" echo "${DPDK_VER}" > ${VERSION_FILE} } build_dpdk $DPDK_VER openvswitch-3.7.0~git20260211.8c6ebf8/.ci/dpdk-prepare.sh000077500000000000000000000006201514270232600223440ustar00rootroot00000000000000#!/bin/bash set -ev # Installing wheel separately because it may be needed to build some # of the packages during dependency backtracking and pip >= 22.0 will # abort backtracking on build failures: # https://github.com/pypa/pip/issues/10655 pip3 install --disable-pip-version-check --user wheel pip3 install --disable-pip-version-check --user pyelftools pip3 install --user 'meson>=1.4,<1.5' openvswitch-3.7.0~git20260211.8c6ebf8/.ci/linux-build.sh000077500000000000000000000120661514270232600222310ustar00rootroot00000000000000#!/bin/bash set -o errexit set -x CFLAGS_FOR_OVS="-g -O2" SPARSE_FLAGS="" EXTRA_OPTS="--enable-Werror" JOBS=${JOBS:-"-j4"} function install_dpdk() { local DPDK_INSTALL_DIR="$(pwd)/dpdk-dir" local VERSION_FILE="${DPDK_INSTALL_DIR}/cached-version" local DPDK_LIB=${DPDK_INSTALL_DIR}/lib/x86_64-linux-gnu if [ "$DPDK_SHARED" ]; then EXTRA_OPTS="$EXTRA_OPTS --with-dpdk=shared" export LD_LIBRARY_PATH=$DPDK_LIB/:$LD_LIBRARY_PATH else EXTRA_OPTS="$EXTRA_OPTS --with-dpdk=static" fi # Export the following path for pkg-config to find the .pc file. export PKG_CONFIG_PATH=$DPDK_LIB/pkgconfig/:$PKG_CONFIG_PATH # Expose dpdk binaries. export PATH=$(pwd)/dpdk-dir/bin:$PATH if [ ! -f "${VERSION_FILE}" ]; then echo "Could not find DPDK in $DPDK_INSTALL_DIR" return 1 fi # Update the library paths. sudo ldconfig echo "Found cached DPDK $(cat ${VERSION_FILE}) build in $DPDK_INSTALL_DIR" } function configure_ovs() { ./boot.sh ./configure CFLAGS="${CFLAGS_FOR_OVS}" $* } function build_ovs() { configure_ovs $OPTS make selinux-policy make ${JOBS} } function clang_analyze() { [ -d "./base-clang-analyzer-results" ] && cache_build=false \ || cache_build=true if [ "$cache_build" = true ]; then # If this is a cache build, proceed to the base branch's directory. pushd base_ovs_main fi; configure_ovs $OPTS make clean scan-build -o ./clang-analyzer-results -sarif --use-cc=${CC} make ${JOBS} if [ "$cache_build" = true ]; then # Move results, so it will be picked up by the cache. mv ./clang-analyzer-results ../base-clang-analyzer-results popd else # Only do the compare on the none cache builds. sarif --check note diff ./base-clang-analyzer-results \ ./clang-analyzer-results fi; } if [ "$DEB_PACKAGE" ]; then ./boot.sh && ./configure --with-dpdk=$DPDK && make debian mk-build-deps --install --root-cmd sudo --remove debian/control dpkg-checkbuilddeps make debian-deb packages=$(ls $(pwd)/../*.deb) deps="" for pkg in $packages; do _ifs=$IFS IFS="," for dep in $(dpkg-deb -f $pkg Depends); do dep_name=$(echo "$dep"|awk '{print$1}') # Don't install internal package inter-dependencies from apt echo $dep_name | grep -q openvswitch && continue deps+=" $dep_name" done IFS=$_ifs done # install package dependencies from apt echo $deps | xargs sudo apt -y install # install the locally built openvswitch packages sudo dpkg -i $packages # Check that python C extension is built correctly. python3 -c " from ovs import _json import ovs.json assert ovs.json.from_string('{\"a\": 42}') == {'a': 42}" exit 0 fi if [ "$DPDK" ] || [ "$DPDK_SHARED" ]; then install_dpdk fi if [ "$STD" ]; then CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} -std=$STD" fi if [ "$CC" = "clang" ]; then CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} -Wno-error=unused-command-line-argument" elif [ "$M32" ]; then # Not using sparse for 32bit builds on 64bit machine. # Adding m32 flag directly to CC to avoid any posiible issues with API/ABI # difference on 'configure' and 'make' stages. export CC="$CC -m32" else EXTRA_OPTS="$EXTRA_OPTS --enable-sparse" CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} ${SPARSE_FLAGS}" fi if [ "$SANITIZERS" ]; then # This will override default ASAN options configured in tests/atlocal.in. export ASAN_OPTIONS='detect_leaks=1' CFLAGS_FOR_SAN="-fno-omit-frame-pointer -fno-common -fsanitize=$SANITIZERS" CFLAGS_FOR_OVS="${CFLAGS_FOR_OVS} ${CFLAGS_FOR_SAN}" fi OPTS="${EXTRA_OPTS} ${OPTS} $*" if [ "$CLANG_ANALYZE" ]; then clang_analyze exit 0 fi if [ "$TESTSUITE" = 'test' ]; then # 'distcheck' will reconfigure with required options. # Now we only need to prepare the Makefile without sparse-wrapped CC. configure_ovs export DISTCHECK_CONFIGURE_FLAGS="$OPTS" make distcheck ${JOBS} CFLAGS="${CFLAGS_FOR_OVS}" \ TESTSUITEFLAGS=${JOBS} RECHECK=yes else build_ovs for testsuite in $TESTSUITE; do run_as_root= if [ "$testsuite" != "check" ] && \ [ "$testsuite" != "check-ovsdb-cluster" ] ; then run_as_root="sudo -E PATH=$PATH GITHUB_ACTIONS=$GITHUB_ACTIONS" sudo ip netns add ovs-system-test-ns # Some system tests may rely on traffic loopback. sudo ip -netns ovs-system-test-ns link set dev lo up run_as_root="${run_as_root} ip netns exec ovs-system-test-ns" fi if [ "${testsuite##*dpdk}" != "$testsuite" ]; then sudo sh -c 'echo 1024 > /proc/sys/vm/nr_hugepages' || true [ "$(cat /proc/sys/vm/nr_hugepages)" = '1024' ] export DPDK_EAL_OPTIONS="--lcores 0@1,1@1,2@1" fi $run_as_root make $testsuite TESTSUITEFLAGS="${JOBS} ${TEST_RANGE}" \ RECHECK=yes done fi exit 0 openvswitch-3.7.0~git20260211.8c6ebf8/.ci/linux-prepare.sh000077500000000000000000000017701514270232600225700ustar00rootroot00000000000000#!/bin/bash set -ev if [ "$DEB_PACKAGE" ]; then # We're not using sparse for debian packages, tests are skipped and # all extra dependencies tracked by mk-build-deps. exit 0 fi # Build and install sparse. # # Disabling sqlite support because sindex build fails and we don't # really need this utility being installed. if test -d sparse; then pushd sparse make -j4 HAVE_SQLITE= install popd fi # Installing wheel separately because it may be needed to build some # of the packages during dependency backtracking and pip >= 22.0 will # abort backtracking on build failures: # https://github.com/pypa/pip/issues/10655 pip3 install --disable-pip-version-check --user wheel pip3 install --disable-pip-version-check --user \ flake8 netaddr pyparsing sarif-tools==2.0.0 sphinx setuptools # Install python test dependencies pip3 install -r python/test_requirements.txt # Make sure IPv6 is enabled to avoid skipping of IPv6 related tests. sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0 openvswitch-3.7.0~git20260211.8c6ebf8/.ci/osx-build.sh000077500000000000000000000006201514270232600216740ustar00rootroot00000000000000#!/bin/bash set -o errexit CFLAGS="-Werror $CFLAGS" EXTRA_OPTS="" function configure_ovs() { ./boot.sh && ./configure $* } configure_ovs $EXTRA_OPTS $OPTS $* if [ "$CC" = "clang" ]; then make CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument" else make CFLAGS="$CFLAGS $BUILD_ENV" fi if [ "$TESTSUITE" ] && [ "$CC" != "clang" ]; then make distcheck RECHECK=yes fi exit 0 openvswitch-3.7.0~git20260211.8c6ebf8/.ci/osx-prepare.sh000077500000000000000000000000731514270232600222350ustar00rootroot00000000000000#!/bin/bash set -ev pip3 install --user --upgrade docutils openvswitch-3.7.0~git20260211.8c6ebf8/.ci/windows-build.sh000066400000000000000000000010031514270232600225460ustar00rootroot00000000000000#!/bin/bash set -ex CONFIGURATION=$1 ./boot.sh ./configure CC=build-aux/cccl LD="$(which link)" \ LIBS="-lws2_32 -lShlwapi -liphlpapi -lwbemuuid -lole32 -loleaut32" \ --prefix=C:/openvswitch/usr --localstatedir=C:/openvswitch/var \ --sysconfdir=C:/openvswitch/etc --with-pthread=c:/PTHREADS-BUILT/ \ --enable-ssl --with-openssl=C:/OpenSSL-Win64 \ --with-vstudiotarget="${CONFIGURATION}" || (cat config.log && exit 1) make -j4 make datapath_windows_analyze make install make windows_installer openvswitch-3.7.0~git20260211.8c6ebf8/.ci/windows-prepare.sh000066400000000000000000000004521514270232600231140ustar00rootroot00000000000000#!/bin/bash set -ex mkdir -p /var/cache/pacman/pkg/ pacman -S --noconfirm --needed automake autoconf libtool make patch # Use an MSVC linker and a Windows version of Python. mv $(which link) $(which link)_copy mv $(which python3) $(which python3)_copy cd /c/pthreads4w-code && nmake all install openvswitch-3.7.0~git20260211.8c6ebf8/.cirrus.yml000066400000000000000000000015141514270232600210710ustar00rootroot00000000000000freebsd_build_task: freebsd_instance: matrix: image_family: freebsd-13-5 image_family: freebsd-14-3 cpu: 4 memory: 4G env: DEPENDENCIES: automake libtool gmake gcc openssl python3 PY_DEPS: sphinx|netaddr|pyparsing matrix: COMPILER: gcc COMPILER: clang prepare_script: - sysctl -w kern.coredump=0 - pkg update -f -r FreeBSD - pkg install -y ${DEPENDENCIES} $(pkg search -xq "^py3[0-9]+-(${PY_DEPS})-[0-9]+" | xargs) configure_script: - ./boot.sh - ./configure CC=${COMPILER} CFLAGS="-g -O2 -Wall" MAKE=gmake --enable-Werror || { cat config.log; exit 1; } build_script: - gmake -j8 check_script: - gmake -j8 check TESTSUITEFLAGS=-j8 RECHECK=yes || { cat ./tests/testsuite.log; exit 1; } openvswitch-3.7.0~git20260211.8c6ebf8/.editorconfig000066400000000000000000000016531514270232600214420ustar00rootroot00000000000000# See https://editorconfig.org/ for syntax reference. root = true # No wildcard sections [*] and [**] because properties cannot be # applied safely to any filetype in general. # Property trim_trailing_whitespace should not be defined at all # because it is interpreted differently by editors. [*.{c,h}] charset = utf-8 end_of_line = lf indent_style = space indent_size = 4 insert_final_newline = true max_line_length = 79 [include/linux/**.h] indent_style = tab indent_size = tab tab_width = 8 [include/sparse/rte_*.h] indent_style = tab tab_width = 8 [include/windows/getopt.h] indent_style = tab indent_size = tab tab_width = 8 [include/windows/netinet/{icmp6,ip6}.h] indent_style = tab indent_size = tab tab_width = 8 [lib/getopt_long.c] indent_style = tab indent_size = tab tab_width = 8 [lib/sflow*.{c,h}] indent_style = tab indent_size = tab tab_width = 8 [lib/strsep.c] indent_style = tab indent_size = tab tab_width = 8 openvswitch-3.7.0~git20260211.8c6ebf8/.github/000077500000000000000000000000001514270232600203205ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/.github/workflows/000077500000000000000000000000001514270232600223555ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/.github/workflows/build-and-test.yml000066400000000000000000000503411514270232600257170ustar00rootroot00000000000000name: Build and Test on: [push, pull_request] env: python_default: 3.12 jobs: build-dpdk: strategy: matrix: runner: [ubuntu-24.04] env: dependencies: gcc libnuma-dev libxdp-dev ninja-build pkgconf CC: gcc DPDK_GIT: https://dpdk.org/git/dpdk-stable DPDK_VER: 25.11 name: dpdk gcc outputs: dpdk_key: ${{ steps.gen_dpdk_key.outputs.key }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 steps: - name: checkout uses: actions/checkout@v4 - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: create ci signature file for the dpdk cache key # This will collect most of DPDK related lines, so hash will be different # if something changed in a way we're building DPDK including DPDK_VER. # This also allows us to use cache from any branch as long as version # and a way we're building DPDK stays the same. run: | echo ${{ matrix.runner }} > dpdk-ci-signature cat .ci/dpdk-* >> dpdk-ci-signature grep -rwE 'DPDK_GIT|DPDK_VER' .github/ >> dpdk-ci-signature if [ "${DPDK_VER##refs/*/}" != "${DPDK_VER}" ]; then git ls-remote --heads $DPDK_GIT $DPDK_VER >> dpdk-ci-signature fi cat dpdk-ci-signature - name: generate ci DPDK key id: gen_dpdk_key env: ci_key: ${{ hashFiles('dpdk-ci-signature') }} run: echo 'key=dpdk-${{ env.ci_key }}' >> $GITHUB_OUTPUT - name: cache id: dpdk_cache uses: actions/cache@v4 with: path: dpdk-dir key: ${{ steps.gen_dpdk_key.outputs.key }} - name: set up python if: steps.dpdk_cache.outputs.cache-hit != 'true' uses: actions/setup-python@v5 with: python-version: ${{ env.python_default }} - name: update APT cache if: steps.dpdk_cache.outputs.cache-hit != 'true' run: sudo apt update || true - name: install common dependencies if: steps.dpdk_cache.outputs.cache-hit != 'true' run: sudo apt install -y ${{ env.dependencies }} - name: prepare if: steps.dpdk_cache.outputs.cache-hit != 'true' run: ./.ci/dpdk-prepare.sh - name: build if: steps.dpdk_cache.outputs.cache-hit != 'true' run: ./.ci/dpdk-build.sh build-libreswan: strategy: matrix: runner: [ubuntu-24.04] env: dependencies: build-essential fakeroot devscripts equivs libreswan_ver: v5.1 name: libreswan outputs: libreswan_key: ${{ steps.gen_libreswan_key.outputs.key }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 steps: - name: Checkout Libreswan uses: actions/checkout@v4 with: repository: libreswan/libreswan path: libreswan ref: ${{ env.libreswan_ver }} - name: generate cache key id: gen_libreswan_key run: echo 'key=libreswan-${{ env.libreswan_ver }}-${{ matrix.runner }}' >> $GITHUB_OUTPUT - name: cache id: libreswan_cache uses: actions/cache@v4 with: path: libreswan-deb key: ${{ steps.gen_libreswan_key.outputs.key }} - name: update APT cache if: steps.libreswan_cache.outputs.cache-hit != 'true' run: sudo apt update || true - name: install common dependencies if: steps.libreswan_cache.outputs.cache-hit != 'true' run: sudo apt install -y ${{ env.dependencies }} - name: install build dependencies if: steps.libreswan_cache.outputs.cache-hit != 'true' run: mk-build-deps --install --root-cmd sudo libreswan/packaging/debian/control - name: build if: steps.libreswan_cache.outputs.cache-hit != 'true' run: cd libreswan && make deb - name: move the package to cache if: steps.libreswan_cache.outputs.cache-hit != 'true' run: mkdir -p libreswan-deb && mv libreswan_*.deb ./libreswan-deb build-linux: needs: [build-dpdk, build-libreswan] env: dependencies: | automake libtool gcc bc libjemalloc2 libjemalloc-dev libssl-dev \ llvm-dev libnuma-dev selinux-policy-dev libxdp-dev lftp CC: ${{ matrix.compiler }} DPDK: ${{ matrix.dpdk }} DPDK_SHARED: ${{ matrix.dpdk_shared }} LIBS: ${{ matrix.libs }} M32: ${{ matrix.m32 }} OPTS: ${{ matrix.opts }} SANITIZERS: ${{ matrix.sanitizers }} STD: ${{ matrix.std }} TESTSUITE: ${{ matrix.testsuite }} TEST_RANGE: ${{ matrix.test_range }} name: linux ${{ join(matrix.*, ' ') }} runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: fail-fast: false matrix: include: - compiler: gcc opts: --disable-ssl - compiler: clang opts: --disable-ssl - compiler: gcc std: c99 - compiler: clang std: c99 - compiler: gcc testsuite: test - compiler: clang sanitizers: address,undefined testsuite: test - compiler: gcc testsuite: test opts: --enable-shared - compiler: clang testsuite: test opts: --enable-shared - compiler: gcc testsuite: check check-dpdk dpdk: dpdk - compiler: clang testsuite: check check-dpdk dpdk: dpdk - compiler: gcc testsuite: test libs: -ljemalloc - compiler: clang testsuite: test libs: -ljemalloc - compiler: gcc opts: --enable-afxdp - compiler: clang opts: --enable-afxdp - compiler: gcc dpdk: dpdk opts: --enable-shared - compiler: clang dpdk: dpdk opts: --enable-shared - compiler: gcc dpdk_shared: dpdk-shared - compiler: clang dpdk_shared: dpdk-shared - compiler: gcc dpdk_shared: dpdk-shared opts: --enable-shared - compiler: clang dpdk_shared: dpdk-shared opts: --enable-shared - compiler: gcc m32: m32 opts: --disable-ssl - compiler: gcc testsuite: check-ovsdb-cluster - compiler: gcc testsuite: check-kernel test_range: "-100" - compiler: gcc testsuite: check-kernel test_range: "100-" - compiler: clang sanitizers: address,undefined testsuite: check-kernel test_range: "-100" - compiler: clang sanitizers: address,undefined testsuite: check-kernel test_range: "100-" - compiler: gcc testsuite: check-offloads test_range: "-100" - compiler: gcc testsuite: check-offloads test_range: "100-" - compiler: gcc dpdk: dpdk testsuite: check-system-userspace - compiler: clang sanitizers: address,undefined dpdk: dpdk testsuite: check-system-userspace - compiler: gcc dpdk: dpdk testsuite: check-system-tso - compiler: gcc dpdk: dpdk testsuite: check-afxdp steps: - name: checkout uses: actions/checkout@v4 - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: set up python uses: actions/setup-python@v5 with: python-version: ${{ env.python_default }} - name: fix /etc/hosts # On multiple occasions GitHub added things to /etc/hosts that are not # a correct syntax for this file causing test failures: # https://github.com/actions/runner-images/issues/3353 # https://github.com/actions/runner-images/issues/12192 # Just clearing those out, if any. run: | set -x cp /etc/hosts ./hosts.bak sed -E -n \ '/^[[:space:]]*(#.*|[0-9a-fA-F:.]+([[:space:]]+[a-zA-Z0-9.-]+)+|)$/p' \ ./hosts.bak | sudo tee /etc/hosts diff -u ./hosts.bak /etc/hosts || true - name: DPDK cache if: matrix.dpdk != '' || matrix.dpdk_shared != '' uses: actions/cache@v4 with: path: dpdk-dir key: ${{ needs.build-dpdk.outputs.dpdk_key }} - name: Libreswan cache uses: actions/cache@v4 with: path: libreswan-deb key: ${{ needs.build-libreswan.outputs.libreswan_key }} - name: update APT cache run: sudo apt update || true - name: install common dependencies run: sudo apt install -y ${{ env.dependencies }} - name: install Libreswan run: sudo apt install -y ./libreswan-deb/libreswan_*.deb - name: install libunbound libunwind python3-unbound # GitHub Actions doesn't have 32-bit versions of these libraries. if: matrix.m32 == '' run: sudo apt install -y libunbound-dev libunwind-dev python3-unbound - name: install 32-bit libraries if: matrix.m32 != '' run: sudo apt install -y gcc-multilib - name: checkout sparse uses: actions/checkout@v4 # Official mirror of the git.kernel.org/pub/scm/devel/sparse/sparse.git. with: repository: lucvoo/sparse path: sparse - name: prepare run: ./.ci/linux-prepare.sh - name: build run: ./.ci/linux-build.sh - name: copy logs on failure if: failure() || cancelled() run: | # upload-artifact throws exceptions if it tries to upload socket # files and we could have some socket files in testsuite.dir. # Also, upload-artifact doesn't work well enough with wildcards. # So, we're just archiving everything here to avoid any issues. mkdir logs cp config.log ./logs/ cp -r ./*/_build/sub/tests/testsuite.* ./logs/ || true sudo cp -r ./tests/*testsuite.* ./logs/ || true sudo tar -czvf logs.tgz logs/ - name: upload logs on failure if: failure() || cancelled() uses: actions/upload-artifact@v4 with: name: logs-linux-${{ join(matrix.*, '-') }} path: logs.tgz build-clang-analyze-cache: needs: build-dpdk env: dependencies: | automake bc clang-tools libnuma-dev libunbound-dev libunwind-dev \ libssl-dev libtool libxdp-dev llvm-dev CC: clang DPDK: dpdk CLANG_ANALYZE: true name: clang-analyze-cache outputs: key: ${{ steps.cache_key.outputs.key }} outcome: ${{ steps.build_base.outcome }} runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: get base branch sha id: base_branch env: BASE_SHA: ${{ github.event.pull_request.base.sha }} EVENT_BEFORE: ${{ github.event.before }} FORCED_PUSH: ${{ github.event.forced }} run: | if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then echo "sha=$BASE_SHA" >> $GITHUB_OUTPUT else if [ "$EVENT_BEFORE" = "0000000000000000000000000000000000000000" ] \ || [ "$FORCED_PUSH" = true ]; then BASE_SHA=HEAD~1 MIN_DISTANCE=1000 git remote add upstream https://github.com/openvswitch/ovs.git git fetch upstream for upstream_head in $(git ls-remote --heads upstream main dpdk-latest branch-2.17 branch-[3456789]* | cut -f 1); do CURR_BASE=$(git merge-base ${upstream_head} HEAD 2>/dev/null) if [ ${CURR_BASE} ]; then DISTANCE=$(git log --oneline ${CURR_BASE}..HEAD | wc -l); if test ${MIN_DISTANCE} -gt ${DISTANCE}; then BASE_SHA=${CURR_BASE} MIN_DISTANCE=${DISTANCE} fi fi done echo "sha=$BASE_SHA" >> $GITHUB_OUTPUT else echo "sha=$EVENT_BEFORE" >> $GITHUB_OUTPUT fi fi - name: checkout base branch env: BASE_SHA: ${{ steps.base_branch.outputs.sha }} run: | cp -r $(pwd)/. /tmp/base_ovs_main && mv /tmp/base_ovs_main ./ cd $(pwd)/base_ovs_main git checkout ${BASE_SHA} - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: generate cache key id: cache_key run: | ver=$(${CC} -v 2>&1 | grep ' version ' | \ sed 's/.*version \([0-9]*\.[0-9]*\.[0-9]*\).*/\1/g') echo "key=${CC}-${ver}-analyze-$(git -C base_ovs_main rev-parse HEAD)" \ >> $GITHUB_OUTPUT - name: check for analyzer result cache id: clang_cache uses: actions/cache@v4 with: path: base-clang-analyzer-results key: ${{ steps.cache_key.outputs.key }} - name: set up python if: steps.clang_cache.outputs.cache-hit != 'true' uses: actions/setup-python@v5 with: python-version: ${{ env.python_default }} - name: get cached dpdk-dir if: steps.clang_cache.outputs.cache-hit != 'true' uses: actions/cache/restore@v4 with: path: dpdk-dir key: ${{ needs.build-dpdk.outputs.dpdk_key }} - name: update APT cache if: steps.clang_cache.outputs.cache-hit != 'true' run: sudo apt update || true - name: install common dependencies if: steps.clang_cache.outputs.cache-hit != 'true' run: sudo apt install -y ${{ env.dependencies }} - name: prepare if: steps.clang_cache.outputs.cache-hit != 'true' run: ./.ci/linux-prepare.sh - name: build base reference id: build_base if: steps.clang_cache.outputs.cache-hit != 'true' continue-on-error: true run: ./.ci/linux-build.sh build-clang-analyze: needs: [build-dpdk, build-clang-analyze-cache] if: > needs.build-clang-analyze-cache.outputs.outcome == 'success' || needs.build-clang-analyze-cache.outputs.outcome == 'skipped' env: dependencies: | automake bc clang-tools libnuma-dev libunbound-dev libunwind-dev \ libssl-dev libtool libxdp-dev llvm-dev CC: clang DPDK: dpdk CLANG_ANALYZE: true name: clang-analyze runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: checkout uses: actions/checkout@v4 - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: check for analyzer result cache uses: actions/cache/restore@v4 with: path: base-clang-analyzer-results key: ${{ needs.build-clang-analyze-cache.outputs.key }} - name: set up python uses: actions/setup-python@v5 with: python-version: ${{ env.python_default }} - name: get cached dpdk-dir uses: actions/cache/restore@v4 with: path: dpdk-dir key: ${{ needs.build-dpdk.outputs.dpdk_key }} - name: update APT cache run: sudo apt update || true - name: install common dependencies run: sudo apt install -y ${{ env.dependencies }} - name: prepare run: ./.ci/linux-prepare.sh - name: build run: ./.ci/linux-build.sh build-oss-fuzz: name: build oss-fuzz fuzzers runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: Checkout OVS uses: actions/checkout@v4 - name: Checkout oss-fuzz uses: actions/checkout@v4 with: repository: google/oss-fuzz path: oss-fuzz - name: Build oss-fuzz image run: | cd oss-fuzz python infra/helper.py build_image openvswitch --no-pull - name: Build oss-fuzz fuzzers run: | cd oss-fuzz python infra/helper.py build_fuzzers --sanitizer address \ --engine afl --architecture x86_64 openvswitch $GITHUB_WORKSPACE build-osx: env: CC: clang OPTS: --disable-ssl name: osx clang --disable-ssl runs-on: macos-latest timeout-minutes: 30 strategy: fail-fast: false steps: - name: checkout uses: actions/checkout@v4 - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: set up python uses: actions/setup-python@v5 with: python-version: ${{ env.python_default }} - name: install dependencies run: brew install automake libtool - name: prepare run: ./.ci/osx-prepare.sh - name: build run: ./.ci/osx-build.sh - name: upload logs on failure if: failure() uses: actions/upload-artifact@v4 with: name: logs-osx-clang---disable-ssl path: config.log build-old-linux-distribution: env: dependencies: git make automake libtool gcc libnuma-dev zlib1g-dev python_version: 3.12.11 name: linux gcc (ubuntu-14.04) runs-on: ubuntu-24.04 container: ubuntu:14.04 timeout-minutes: 30 strategy: fail-fast: false steps: - name: update APT cache run: sudo apt update || true - name: install dependencies run: sudo apt install -y ${{ env.dependencies }} - name: build python run: | git clone --branch v${{ env.python_version }} --depth 1 \ https://github.com/python/cpython cpython cd cpython && ./configure && sudo make -j4 install python3 --version - name: checkout run: | SHA=${{ github.event.pull_request.head.sha || github.sha }} git clone https://github.com/${{ github.repository }}.git ovs cd ovs && git fetch origin $SHA && git checkout $SHA - name: prepare run: cd ovs && ./boot.sh && ./configure --disable-ssl --enable-Werror - name: build run: cd ovs && make -j4 build-linux-deb: env: deb_dependencies: | linux-headers-$(uname -r) build-essential fakeroot devscripts equivs DEB_PACKAGE: yes DPDK: ${{ matrix.dpdk }} name: linux deb ${{ matrix.dpdk }} dpdk runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: fail-fast: false matrix: include: - dpdk: no steps: - name: checkout uses: actions/checkout@v4 - name: update PATH run: | echo "$HOME/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH - name: update APT cache run: sudo apt update || true - name: install dependencies for debian packages run: sudo apt install -y ${{ env.deb_dependencies }} - name: install dpdk-dev if: matrix.dpdk != 'no' run: sudo apt install -y libdpdk-dev - name: prepare run: ./.ci/linux-prepare.sh - name: build run: ./.ci/linux-build.sh - name: upload deb packages uses: actions/upload-artifact@v4 with: name: deb-packages-${{ matrix.dpdk }}-dpdk path: '/home/runner/work/ovs/*.deb' build-linux-rpm: name: linux rpm fedora runs-on: ubuntu-latest container: fedora:43 timeout-minutes: 30 strategy: fail-fast: false steps: - name: checkout uses: actions/checkout@v4 - name: install dependencies run: | dnf install -y rpm-build dnf-plugins-core sed -e 's/@VERSION@/0.0.1/' rhel/openvswitch-fedora.spec.in \ > /tmp/ovs.spec dnf builddep -y /tmp/ovs.spec rm -f /tmp/ovs.spec - name: configure run: ./boot.sh && ./configure - name: build run: make rpm-fedora - name: install run: dnf install -y rpm/rpmbuild/RPMS/*/*.rpm - name: upload rpm packages uses: actions/upload-artifact@v4 with: name: rpm-packages path: | rpm/rpmbuild/SRPMS/*.rpm rpm/rpmbuild/RPMS/*/*.rpm openvswitch-3.7.0~git20260211.8c6ebf8/.gitignore000066400000000000000000000014641514270232600207550ustar00rootroot00000000000000#*# *.a *.d *.gcno *.gcda *.ko *.la *.lo *.loT *.mod.c *.o *.obj *.exe *.exp *.ilk *.lib *.pdb *.pyc *.retry *.so *.suo **/*.sym *~ *,cover .#* .*.cmd .*.swp .coverage .deps .dirstamp .libs .tmp_versions .vagrant /Makefile /Makefile.in /aclocal.m4 /all-gitfiles /autom4te.cache /build-arch-stamp /build-indep-stamp /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /configure-stamp /depcomp /distfiles /dist-docs /flake8-check /docs-check /install-sh /libtool /manpages.mk /manpage-check /missing /missing-distfiles /package.m4 /stamp-h1 /_build-gcc /_build-clang Module.symvers TAGS cscope.* tags _debian _dpdk odp-netlink.h odp-netlink-macros.h OvsDpInterface.h /.vagrant/ testsuite.tmp.orig /rpm/ /openvswitch*.tar.gz /tests/lcov/ /Documentation/_build /.venv /cxx-check openvswitch-3.7.0~git20260211.8c6ebf8/.mailmap000066400000000000000000000117571514270232600204140ustar00rootroot00000000000000# See git-shortlog(1) for official documentation of this file format. # # Name # Use "Name" for any commit with address "". This is useful when a person # has commits using different spellings of their name, but with the same email # address. # # Name # Use "Name" and "" for any commit with the address "". This is useful # when a person has used more than one email address. Aaron Conole Aaron Conole Aaron Rosen Alex Wang Alexey I. Froloff Alin Serdean Alin Serdean Andy Zhou Andy Zhou Andy Zhou Ansis Atteka Ansis Atteka Anupam Chanda Ariel Tubaltsev Babu Shanmugam Ben Pfaff Bruce Davie Bruce Davie Chandra Sekhar Vejendla Daniele Di Proietto Daniele Di Proietto Ed Maste Eli Britstein Ethan J. Jackson Fischetti, Antonio Flavio Fernandes Flavio Leitner Gal Sagie Gurucharan Shetty Gurucharan Shetty Henry Mai Hui Kang Ian Campbell Ilya Maximets James Page Jarno Rajahalme Jarno Rajahalme Jean Tourrilhes Jean Tourrihles Jean Tourrilhes Jesse Gross Joe Stringer Joe Stringer Justin Pettit Kevin Traynor Kmindg Kyle Mestery Lance Richardson Lin Huang Lin Huang Mark Gray Mauricio Vasquez Miguel Angel Ajo Neil McKee Ofer Ben-Yacov Polehn, Mike A Pravin B Shelar Raju Subramanian Ralf Spenneberg Rami Rosen Ramu Ramamurthy Robert Åkerblom-Andersson Roi Dayan Romain Lenglet Romain Lenglet Rosemarie O'Riorden Rosemarie O'Riorden Russell Bryant Ryan Moats Sabyasachi Sengupta Saurabh Shah Shad Ansari Shih-Hao Li Simon Horman Simon Horman Simon Horman Stephen Finucane Thomas F. Herbert Thomas Graf Thomas Graf Wei Li YAMAMOTO Takashi YAMAMOTO Takashi YAMAMOTO Takashi Zhi Yong Wu Zoltan Kiss Zong Kai LI openvswitch-3.7.0~git20260211.8c6ebf8/.readthedocs.yaml000066400000000000000000000010351514270232600222060ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file. # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. # Required. version: 2 # Set the OS, Python version, etc. build: os: ubuntu-22.04 tools: python: "3.12" # Build documentation in the "Documentation/" directory with Sphinx. sphinx: configuration: Documentation/conf.py builder: "dirhtml" # Build all formats: HTML, PDF, ePub. formats: all # Declare the Python requirements. python: install: - requirements: Documentation/requirements.txt openvswitch-3.7.0~git20260211.8c6ebf8/AUTHORS.rst000066400000000000000000001243261514270232600206470ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======= Authors ======= The following people authored or signed off on commits in the Open vSwitch source code or webpage version control repository. ================================== =============================================== Name Email ================================== =============================================== Aaron Conole aconole@redhat.com Aaron Rosen arosen@clemson.edu Abhiram R N abhiramrn@gmail.com Adrian Guzowski adrian.guzowski@exatel.pl Adrian Moreno amorenoz@redhat.com Aidan Shribman aidan.shribman@gmail.com Alan Pevec alan.pevec@redhat.com Aleksandr Smirnov alekssmirnov@k2.cloud Ales Musil amusil@redhat.com Alessandro Pilotti apilotti@cloudbasesolutions.com Alexander Duyck alexander.h.duyck@redhat.com Alexandra Rukomoinikova arukomoinikova@k2.cloud Alexandru Copot alex.mihai.c@gmail.com Alexei Starovoitov ast@plumgrid.com Alexey I. Froloff raorn@raorn.name Alexey Roytman roytman@il.ibm.com Alex Wang ee07b291@gmail.com Alfredo Finelli alf@computationes.de Alin Balutoiu abalutoiu@cloudbasesolutions.com Alin Serdean aserdean@ovn.org Allen Chen allen.chen@jaguarmicro.com Amber Kumar kumar.amber@intel.com Ambika Arora ambika.arora@tcs.com Amit Bose bose@noironetworks.com Amit Prakash Shukla amitprakashs@marvell.com Amitabha Biswas azbiswas@gmail.com Anand Kumar kumaranand@vmware.com Andrea Kao eirinikos@gmail.com Andreas Karis akaris@redhat.com Andreas Stieger andreas.stieger@gmx.de Andrew Evans Andrew Beekhof abeekhof@redhat.com Andrew Kampjes a.kampjes@gmail.com Andrew Lambeth alambeth@vmware.com Andrew Rybchenko andrew.rybchenko@oktetlabs.ru Andre McCurdy armccurdy@gmail.com Andy Hill hillad@gmail.com Andy Southgate andy.southgate@citrix.com Andy Zhou azhou@ovn.org Ankur Sharma ankursharma@vmware.com Anoob Soman anoob.soman@citrix.com Ansis Atteka aatteka@vmware.com Anton Ivanov anton.ivanov@cambridgegreys.com Antonio Fischetti antonio.fischetti@intel.com Anupam Chanda Ariel Levkovich lariel@nvidia.com Ariel Tubaltsev atubaltsev@vmware.com Arnoldo Lutz arnoldo.lutz.guevara@hpe.com Arun Sharma arun.sharma@calsoftinc.com Aryan TaheriMonfared aryan.taherimonfared@uis.no Asaf Penso asafp@mellanox.com Ashish Varma ashishvarma.ovs@gmail.com Ashwin Swaminathan ashwinds@arista.com Babu Shanmugam bschanmu@redhat.com Bala Sankaran bsankara@redhat.com Balazs Nemeth bnemeth@redhat.com Ben Pfaff blp@ovn.org Ben Warren ben@skyportsystems.com Benli Ye daniely@vmware.com Bert Vermeulen bert@biot.com Bhanuprakash Bodireddy bhanuprakash.bodireddy@intel.com Billy O'Mahony billy.o.mahony@intel.com Binbin Xu xu.binbin1@zte.com.cn Bodo Petermann b.petermann@syseleven.de Boleslaw Tokarski boleslaw.tokarski@jollamobile.com Brad Cowie brad@faucet.nz Brian Haley haleyb.dev@gmail.com Brian Kruger bkruger+ovsdev@gmail.com Bruce Davie bdavie@vmware.com Bryan Phillippe bp@toroki.com Carlo Andreotti c.andreotti@m3s.it Casey Barker crbarker@google.com Chandan Somani csomani@redhat.com Chandra Sekhar Vejendla csvejend@us.ibm.com Changliang Wu changliang.wu@smartx.com Chris Riches chris.riches@nutanix.com Chris Wright chrisw@sous-sol.org Christoph Jaeger cj@linux.com Christophe Fontaine cfontain@redhat.com Christopher Aubut christopher@aubut.me Chuck Short zulcss@ubuntu.com Cian Ferriter cian.ferriter@intel.com Ciara Loftus ciara.loftus@intel.com Clint Byrum clint@fewbar.com Colin Watson cjwatson@ubuntu.com Cong Wang amwang@redhat.com Conner Herriges conner.herriges@ibm.com Damien Millescamps damien.millescamps@6wind.com Damijan Skvarc damjan.skvarc@gmail.com Dan Carpenter dan.carpenter@oracle.com Dan McGregor dan.mcgregor@usask.ca Dan Wendlandt Dan Williams dcbw@redhat.com Daniel Alvarez dalvarez@redhat.com Daniel Borkmann dborkman@redhat.com Daniel Ding zhihui.ding@easystack.cn Daniel Hiltgen daniel@netkine.com Daniel Roman Daniele Di Proietto daniele.di.proietto@gmail.com Daniele Venturino venturino.daniele+ovs@gmail.com Danny Kukawka danny.kukawka@bisect.de Darrell Ball dlu998@gmail.com Dave Tucker dave@dtucker.co.uk David Erickson derickso@stanford.edu David Hill dhill@redhat.com David Marchand david.marchand@redhat.com David S. Miller davem@davemloft.net David Wilder dwilder@us.ibm.com David Yang davidy@vmware.com Dennis Sam dsam@arista.com Devendra Naga devendra.aaru@gmail.com Dexia Li dexia.li@jaguarmicro.com Dima Chumak dchumak@nvidia.com Dincer Beken dbeken@blackned.de Dmitry Krivenok krivenok.dmitry@gmail.com Dmitry Porokh dporokh@nvidia.com Dominic Curran dominic.curran@citrix.com Dongdong dongdong1@huawei.com Dongjun dongj@dtdream.com Duan Jiong djduanjiong@gmail.com Duffie Cooley Dujie dujie@didiglobal.com Dumitru Ceara dceara@redhat.com Dustin Lundquist dustin@null-ptr.net Ed Maste emaste@freebsd.org Ed Swierk eswierk@skyportsystems.com Edouard Bourguignon madko@linuxed.net Eelco Chaudron echaudro@redhat.com Eiichi Tsukata eiichi.tsukata@nutanix.com Eli Britstein elibr@nvidia.com Eli Oliver eoliver@redhat.com Emma Finn emma.finn@intel.com Eric Lapointe elapointe@corsa.com Esteban Rodriguez Betancourt estebarb@hpe.com Aymerich Edward edward.aymerich@hpe.com Edward Tomasz Napierała trasz@freebsd.org Eitan Eliahu eliahue@vmware.com Eohyung Lee liquidnuker@gmail.com Eric Dumazet edumazet@google.com Eric Garver e@erig.me Eric Sesterhenn eric.sesterhenn@lsexperts.de Ethan J. Jackson ejj@eecs.berkeley.edu Ethan Rahn erahn@arista.com Eziz Durdyyev ezizdurdy@gmail.com Fabrizio D'Angelo fdangelo@redhat.com Faicker Mo faicker.mo@ucloud.cn fang fangjiannan@cmss.chinamobile.com Fangrui Song maskray@google.com Felix Huettner felix.huettner@mail.schwarz Felix Moebius felix.moebius@mail.schwarz Fengqi Li lifengqi@inspur.com Flavio Fernandes flavio@flaviof.com Flavio Leitner fbl@redhat.com Francesco Fusco ffusco@redhat.com Frank Wagner frank.wagner@dbosoft.eu François Rigault frigo@amadeus.com Frédéric Tobias Christ fchrist@live.de Frode Nordahl frode.nordahl@gmail.com FUJITA Tomonori fujita.tomonori@lab.ntt.co.jp Gabe Beged-Dov gabe@begeddov.com Gaetan Rivet grive@u256.net Gaetano Catalli gaetano.catalli@gmail.com Gal Sagie gal.sagie@gmail.com Genevieve LEsperance glesperance@pivotal.io Geoffrey Wossum gwossum@acm.org Gianluca Merlo gianluca.merlo@gmail.com Giuseppe Lettieri g.lettieri@iet.unipi.it Glen Gibb grg@stanford.edu Gowrishankar Muthukrishnan gmuthukr@redhat.com Guoshuai Li ligs@dtdream.com Guolin Yang gyang@vmware.com Guru Chaitanya Perakam gperakam@Brocade.com Gurucharan Shetty guru@ovn.org Han Ding handing@chinatelecom.cn Han Zhou zhouhan@gmail.com Hao Zheng Hariprasad Govindharajan hariprasad.govindharajan@intel.com Harold Huang baymaxhuang@gmail.com Harry Van Haaren harry.van.haaren@intel.com Helmut Schaa helmut.schaa@googlemail.com Henry Mai Hiteshi Kalra hiteshi.kalra@tcs.com Hongzhi Guo guohongzhi1@huawei.com Huanle Han hanxueluo@gmail.com Hui Kang kangh@us.ibm.com Hyong Youb Kim hyonkim@cisco.com Ian Campbell Ian.Campbell@citrix.com Ian Stokes ian.stokes.oss@gmail.com Ihar Hrachyshka ihar.hrachyshka@gmail.com Ilya Maximets i.maximets@ovn.org Iman Tabrizian tabrizian@outlook.com Isaku Yamahata yamahata@valinux.co.jp Ivan Burnin iburnin@k2.cloud Ivan Dyukov i.dyukov@samsung.com Ivan Malov ivan.malov@arknetworks.am IWASE Yusuke iwase.yusuke@gmail.com Jaime Caamaño Ruiz jcaamano@suse.com Jakob Meng code@jakobmeng.de Jakub Libosvar libosvar@redhat.com Jakub Sitnicki jsitnicki@gmail.com James P. roampune@gmail.com James Page james.page@ubuntu.com James Raphael Tiovalen jamestiotio@gmail.com Jamie Lennox jamielennox@gmail.com Jan Scheurich jan.scheurich@ericsson.com Jan Vansteenkiste jan@vstone.eu Jarno Rajahalme jarno@ovn.org Jason Kölker jason@koelker.net Jason Wessel jason.wessel@windriver.com Jasper Capel jasper@capel.tv Jay Ding jay.ding@broadcom.com Jean Tourrilhes jt@hpl.hp.com Jeff Squyres jsquyres@cisco.com Jeffrey Walton noloader@gmail.com Jeremy Stribling Jeroen van Bemmel jvb127@gmail.com Jesse Gross jesse@kernel.org Jian Li lijian@ooclab.com Jiang Lidong jianglidong3@jd.com Jianbo Liu jianbol@mellanox.com Jing Ai jinga@google.com Jinjun Gao gjinjun@gmail.com Jiri Benc jbenc@redhat.com Joe Perches joe@perches.com Joe Stringer joe@ovn.org Jon Kohler jon@nutanix.com Jonathan Davies jonathan.davies@nutanix.com Jonathan Vestin jonavest@kau.se Jorge Arturo Sauma Vargas jorge.sauma@hpe.com Jun Gu jun.gu@easystack.cn Jun Nakajima jun.nakajima@intel.com Jun Wang junwang01@cestc.cn JunhanYan juyan@redhat.com JunoZhu zhunatuzi@gmail.com Justin Pettit jpettit@ovn.org Kaige Fu fukaige@huawei.com Keith Amidon Ken Ajiro ajiro@mxw.nes.nec.co.jp Ken Sanislo ken@intherack.com Kenneth Duda kduda@arista.com Kentaro Ebisawa ebiken.g@gmail.com Keshav Gupta keshav.gupta@ericsson.com Kevin Lo kevlo@FreeBSD.org Kevin Sprague ksprague0711@gmail.com Kevin Traynor ktraynor@redhat.com Khem Raj raj.khem@gmail.com Kmindg G kmindg@gmail.com Kris Murphy kriskend@linux.vnet.ibm.com Krishna Kolakaluri kkolakaluri@plume.com Krishna Kondaka kkondaka@vmware.com Kyle Mestery mestery@mestery.com Kyle Simpson kyleandrew.simpson@gmail.com Kyle Upton kupton@baymicrosystems.com Lance Yang lance.yang@arm.com Lance Richardson lance.richardson@broadcom.com Lars Kellogg-Stedman lars@redhat.com Lei Huang huang.f.lei@gmail.com Leif Madsen lmadsen@redhat.com Leo Alterman Li RongQing lirongqing@baidu.com Lian-min Wang liang-min.wang@intel.com Liang Mancang liangmc1@chinatelecom.cn Lin Huang linhuang@ruijie.com.cn Liu Chang liuchang@cmss.chinamobile.com Lilijun jerry.lilijun@huawei.com Lili Huang huanglili.huang@huawei.com Liliia Butorina l.butorina@partner.samsung.com Linda Sun lsun@vmware.com Linda Wang linda.wang@jaguarmicro.com Lior Neudorfer lior@guardicore.com Liu Chang txfh2007@aliyun.com Liu Yulong liuyulong.xa@gmail.com Lorand Jakab lojakab@cisco.com Lorenzo Bianconi lorenzo.bianconi@redhat.com Luca Giraudo Lucas Alvares Gomes lucasagomes@gmail.com Lucian Petrut lpetrut@cloudbasesolutions.com Luigi Rizzo rizzo@iet.unipi.it Luis E. P. l31g@hotmail.com Luca Czesla luca.czesla@mail.schwarz Lukasz Pawlik lukaszx.pawlik@intel.com Lukasz Rzasik lukasz.rzasik@gmail.com MJ Ponsonby mj.ponsonby@canonical.com Maciej Józefczyk mjozefcz@redhat.com Madhu Challa challa@noironetworks.com Manohar K C manukc@gmail.com Marcin Mirecki mmirecki@redhat.com Mario Cabrera mario.cabrera@hpe.com Mark D. Gray mark.d.gray@redhat.com Mark Hamilton Mark Kavanagh mark.b.kavanagh81@gmail.com Mark Maglana mmaglana@gmail.com Mark Michelson mmichels@redhat.com Markos Chandras mchandras@suse.de Markus Linnala markus.linnala@gmail.com Martin Casado casado@cs.stanford.edu Martin Fong mwfong@csl.sri.com Martin Kalcok martin.kalcok@canonical.com Martin Morgenstern martin.morgenstern@cloudandheat.com Martin Varghese martin.varghese@nokia.com Martin Xu martinxu9.ovs@gmail.com Martin Zhang martinbj2008@gmail.com Martino Fornasa mf@fornasa.it Maryam Tahhan maryam.tahhan@intel.com Matteo Croce mcroce@redhat.com Matthias May matthias.may@neratec.com Mauricio Vásquez mauricio.vasquezbernal@studenti.polito.it Max Lamprecht max.lamprecht@mail.schwarz Maxime Coquelin maxime.coquelin@redhat.com Mehak Mahajan Michael Arnaldi arnaldimichael@gmail.com Michael Santana msantana@redhat.com Michael Phelan michael.phelan@intel.com Michal Kazior michal@plume.com Michal Weglicki michalx.weglicki@intel.com Michele Baldessari michele@acksyn.org Mickey Spiegel mickeys.dev@gmail.com Miguel Angel Ajo majopela@redhat.com Miika Petäjäniemi miika.petajaniemi@solita.fi Mijo Safradin mijo@linux.vnet.ibm.com Mika Vaisanen mika.vaisanen@gmail.com Mike Ovsiannikov mike.ovsiannikov@nutanix.com Mike Pattrick mkp@redhat.com Minoru TAKAHASHI takahashi.minoru7@gmail.com Miro Tomaska mtomaska@redhat.com Mohammad Heib mheib@redhat.com Moshe Levi moshele@mellanox.com Murphy McCauley murphy.mccauley@gmail.com Natasha Gude Neal Shrader neal@digitalocean.com Neil McKee neil.mckee@inmon.com Neil Zhu zhuj@centecnetworks.com Nicolas J. Bouliane nbouliane@digitalocean.com Nimay Desai nimaydesai1@gmail.com Nir Anteby nanteby@nvidia.com Nithin Raju nithin@vmware.com Niti Rohilla niti.rohilla@tcs.com Nitin Katiyar nitin.katiyar@ericsson.com Nobuhiro MIKI nmiki@yahoo-corp.jp Numan Siddique nusiddiq@redhat.com Ofer Ben-Yacov ofer.benyacov@gmail.com Ophir Munk ophirmu@mellanox.com Or Gerlitz ogerlitz@mellanox.com Ori Shoshan ori.shoshan@guardicore.com Padmanabhan Krishnan kprad1@yahoo.com Panu Matilainen pmatilai@redhat.com Paolo Valerio pvalerio@redhat.com Paraneetharan Chandrasekaran paraneetharanc@gmail.com Paul Boca pboca@cloudbasesolutions.com Paul Fazzone pfazzone@vmware.com Paul Ingram Paul-Emmanuel Raoul skyper@skyplabs.net Pavithra Ramesh paramesh@vmware.com Peng He hepeng.0320@bytedance.com Pengfei Sun sunpengfei16@huawei.com Peter Downs padowns@gmail.com Philippe Jung phil.jung@free.fr Pim van den Berg pim@nethuis.nl pritesh pritesh.kothari@cisco.com Pravin B Shelar pshelar@ovn.org Przemyslaw Szczerbik przemyslawx.szczerbik@intel.com Qian Chen cq674350529@163.com Qiuyu Xiao qiuyu.xiao.qyx@gmail.com Quentin Monnet quentin.monnet@6wind.com Raju Subramanian Rami Rosen ramirose@gmail.com Ramu Ramamurthy ramu.ramamurthy@us.ibm.com Randall Sharo andall.sharo@navy.mil Ravi Kerur Ravi.Kerur@telekom.com Raymond Burkholder ray@oneunified.net Reid Price Remi Jouannet remi.jouannet@outscale.com Remko Tronçon git@el-tramo.be Renat Nurgaliyev impleman@gmail.com Rich Lane rlane@bigswitch.com Richard Oliver richard@richard-oliver.co.uk Rishi Bamba rishi.bamba@tcs.com Rob Adams readams@readams.net Rob Hoes rob.hoes@citrix.com Robert Wojciechowicz robertx.wojciechowicz@intel.com Robert Åkerblom-Andersson Robert.nr1@gmail.com Roberto Bartzen Acosta roberto.acosta@luizalabs.com Robin Jarry rjarry@redhat.com Rohith Basavaraja rohith.basavaraja@gmail.com Roi Dayan roid@nvidia.com Róbert Mulik robert.mulik@ericsson.com Romain Lenglet romain.lenglet@berabera.info Rosemarie O'Riorden rosemarie@redhat.com Roni Bar Yanai roniba@mellanox.com Russell Bryant russell@ovn.org RYAN D. MOATS rmoats@us.ibm.com Ryan Wilson Sairam Venugopal vsairam@vmware.com Sajjad Lateef Salem Sol salems@nvidia.com Saloni Jain saloni.jain@tcs.com Salvatore Daniele sdaniele@redhat.com Samuel Ghinet sghinet@cloudbasesolutions.com Sanjay Sane Saurabh Mohan saurabh@cplanenetworks.com Saurabh Shah Saurabh Shrivastava saurabh.shrivastava@nuagenetworks.net Sayali Naval sanaval@cisco.com Scott Cheloha scottcheloha@gmail.com Scott Lowe scott.lowe@scottlowe.org Scott Mann sdmnix@gmail.com Seamus Ryan seamus.ryan@intel.com Selvamuthukumar smkumar@merunetworks.com Sergey Madaminov sergey.madaminov@gmail.com Sha Zhang zhangsha.zhang@huawei.com Shad Ansari shad.ansari@hpe.com Shahar Klein sklein@nvidia.com Shan Wei davidshan@tencent.com Sharon Krendel thekafkaf@gmail.com Shashank Ram rams@vmware.com Shashwat Srivastava shashwat.srivastava@tcs.com Shih-Hao Li shihli@vmware.com Shu Shen shu.shen@radisys.com Simon Horman horms@ovn.org Simon Jones batmanustc@gmail.com Sivaprasad Tummala sivaprasad.tummala@intel.com Somnath Chatterjee somnath.b.chatterjee@ericsson.com Songtao Zhan zhanst1@chinatelecom.cn Sorin Vinturis svinturis@cloudbasesolutions.com Sriharsha Basavapatna sriharsha.basavapatna@broadcom.com Stefan Hoffmann stefan.hoffmann@cloudandheat.com Steffen Gebert steffen.gebert@informatik.uni-wuerzburg.de Sten Spans sten@blinkenlights.nl Stephane A. Sezer sas@cd80.net Stephen Finucane stephen@that.guru Steve Ruan ruansx@cn.ibm.com Stuart Cardall developer@it-offshore.co.uk Sugesh Chandran sugesh.chandran@intel.com SUGYO Kazushi sugyo.org@gmail.com Sunyang Wu sunyang.wu@jaguarmicro.com Surya Rudra rudrasurya.r@altencalsoftlabs.com Tadaaki Nagao nagao@stratosphere.co.jp Tao Liu thomas.liu@ucloud.cn Tao YunXiang taoyunxiang@cmss.chinamobile.com Terry Wilson twilson@redhat.com Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp Thadeu Lima de Souza Cascardo cascardo@cascardo.eti.br Thilak Raj Surendra Babu thilakraj.sb@nutanix.com Thomas F. Herbert thomasfherbert@gmail.com Thomas Goirand zigo@debian.org Thomas Graf tgraf@noironetworks.com Thomas Lacroix thomas.lacroix@citrix.com Timo Puha timox.puha@intel.com Timothy Redaelli tredaelli@redhat.com Todd Deshane deshantm@gmail.com Tom Everman teverman@google.com Tomasz Konieczny tomaszx.konieczny@intel.com Toms Atteka cpp.code.lv@gmail.com Tony van der Peet tony.vanderpeet@alliedtelesis.co.nz Torgny Lindberg torgny.lindberg@ericsson.com Tsvi Slonim tsvi@toroki.com Tuan Nguyen tuan.nguyen@veriksystems.com Tyler Coumbes coumbes@gmail.com Tony van der Peet tony.vanderpeet@alliedtelesis.co.nz Tonghao Zhang xiangxia.m.yue@gmail.com Usman Ansari ua1422@gmail.com Valient Gough vgough@pobox.com Vasu Dasari vdasari@gmail.com Vasyl Saienko vsaienko@mirantis.com Venkata Anil Kommaddi vkommadi@redhat.com Viacheslav Galaktionov viacheslav.galaktionov@arknetworks.am Ville Skyttä ville.skytta@upcloud.com Vishal Deep Ajmera vishal.deep.ajmera@ericsson.com Vivien Bernet-Rollande vbr@soprive.net Vlad Buslov vladbu@nvidia.com Vladislav Odintsov odivlad@gmail.com Volkan Atlı volkan.atli@b-ulltech.com Wan Junjie wanjunjie@bytedance.com Wang Li wangli39@baidu.com Wang Liang wangliangrt@didiglobal.com Wang Sheng-Hui shhuiw@gmail.com Wang Yibo bobxxwang@126.com Wang Zhike wangzhike@jd.com wangqianyu wang.qianyu@zte.com.cn Wei Li liw@dtdream.com Wei Yongjun yjwei@cn.fujitsu.com Wenyu Zhang wenyuz@vmware.com William Fulton William Tu u9012063@gmail.com Wilson Peng pweisong@vmware.com Xavier Simonart xsimonar@redhat.com Xiao Liang shaw.leon@gmail.com Xiaojie Chen jackchanx@163.com xu rong xu.rong@zte.com.cn YAMAMOTO Takashi yamamoto@midokura.com Yalei Li liyl43@chinatelecom.cn Yang Yang yangyang92@baidu.com Yanqin Wei Yanqin.Wei@arm.com Yasuhito Takamiya yasuhito@gmail.com Yi Li yili@winhong.com Yi Yang yangyi01@inspur.com Yi-Hung Wei yihung.wei@gmail.com Yifeng Sun pkusunyifeng@gmail.com Yin Lin linyi@vmware.com Yu Zhiguo yuzg@cn.fujitsu.com Yuanhan Liu yuanhan.liu@linux.intel.com Yunjian Wang wangyunjian@huawei.com Yousong Zhou yszhou4tech@gmail.com Zak Whittington zwhitt.vmware@gmail.com Zang MingJie zealot0630@gmail.com Zengyuan Wang wangzengyuan@huawei.com ZhengLingyun konghuarukhr@163.com Zhenyu Gao sysugaozhenyu@gmail.com Zhi Yong Wu zwu.kernel@gmail.com ZhiPeng Lu luzhipeng@uniudc.com Zhiqi Chen chenzhiqi.123@bytedance.com Zhou Yangchao 1028519445@qq.com Zoltan Kiss zoltan.kiss@citrix.com Zoltán Balogh zoltan.balogh.eth@gmail.com Zongkai LI zealokii@gmail.com aginwala amginwal@gmail.com gordonwwang gordonwwang@tencent.com lic121 lic121@chinatelecom.cn lzhecheng lzhecheng@vmware.com parameswaran krishnamurthy parkrish@gmail.com solomon liwei.solomon@gmail.com wangchuanlei wangchuanlei@inspur.com wenxu wenxu@ucloud.cn wisd0me ak47izatool@gmail.com xushengping shengping.xu@huawei.com yangchang yangchang@chinatelecom.cn yaolingfei 543981924@qq.com yinpeijun yinpeijun@huawei.com zangchuanqiang zangchuanqiang@huawei.com zhaojingjing zhao.jingjing1@zte.com.cn zhongbaisong zhongbaisong@huawei.com zhaozhanxu zhaozhanxu@163.com ================================== =============================================== The following additional people are mentioned in commit logs as having provided helpful bug reports or suggestions. =============================== =============================================== Name Email =============================== =============================================== Aaron M. Ucko ucko@debian.org Abhinav Singhal Abhinav.Singhal@spirent.com Adam Heath doogie@brainfood.com Ahmed Bilal numan252@gmail.com Alan Kayahan hsykay@gmail.com Alan Shieh Alban Browaeys prahal@yahoo.com Alex Yip Alexey I. Froloff raorn@altlinux.org Amar Padmanabhan Amey Bhide Amre Shakimov ashakimov@vmware.com André Ruß andre.russ@hybris.com Andreas Beckmann debian@abeckmann.de Andrei Andone andrei.andone@softvision.ro Andrey Korolyov andrey@xdel.ru Anil Jangam anilj.mailing@gmail.com Anshuman Manral anshuman.manral@outlook.com Anton Matsiuk anton.matsiuk@gmail.com Anup Khadka khadka.py@gmail.com Anuprem Chalvadi achalvadi@vmware.com Ariel Tubaltsev atubaltsev@vmware.com Arkajit Ghosh arkajit.ghosh@tcs.com Atzm Watanabe atzm@stratosphere.co.jp Aurélien Poulain aurepoulain@viacesi.fr Bastian Blank waldi@debian.org Ben Basler Bhargava Shastry bshastry@sec.t-labs.tu-berlin.de Bob Ball bob.ball@citrix.com Brad Hall Brad Cowie brad@wand.net.nz Brailey Josh josh@faucet.nz Brandon Heller brandonh@stanford.edu Brendan Kelley Brent Salisbury brent.salisbury@gmail.com Brian Field Brian_Field@cable.comcast.com Bryan Fulton Bryan Osoro Cedric Hobbs Chris Hydon chydon@aristanetworks.com Christian Stigen Larsen cslarsen@gmail.com Christopher Paggen cpaggen@cisco.com Chunhe Li lichunhe@huawei.com Daniel Badea daniel.badea@windriver.com Darragh O'Reilly darragh.oreilly@hpe.com Dave Walker DaveWalker@ubuntu.com David Evans davidjoshuaevans@gmail.com David Palma palma@onesource.pt David van Moolenbroek dvmoolenbroek@aimvalley.nl Derek Cormier derek.cormier@lab.ntt.co.jp Derrick Lim derrick.lim@rakuten.com Dhaval Badiani dbadiani@vmware.com DK Moon Ding Zhi zhi.ding@6wind.com Dong Jun dongj@dtdream.com Dustin Spinhirne dspinhirne@vmware.com Edwin Chiu echiu@vmware.com Eivind Bulie Haanaes Enas Ahmad enas.ahmad@kaust.edu.sa Eric Lopez Frank Wang (王培辉) wangpeihui@inspur.com Frido Roose fr.roose@gmail.com Gaetano Catalli gaetano.catalli@gmail.com Gavin Remaley gavin_remaley@selinc.com Georg Schmuecking georg.schmuecking@ericsson.com George Shuklin amarao@desunote.ru Gerald Rogers gerald.rogers@intel.com Ghanem Bahri bahri.ghanem@gmail.com Giuseppe de Candia giuseppe.decandia@gmail.com Gordon Good ggood@vmware.com Greg Dahlman gdahlman@hotmail.com Greg Rose gvrose8192@gmail.com Gregor Schaffrath grsch@net.t-labs.tu-berlin.de Gregory Smith gasmith@nutanix.com Guolin Yang gyang@vmware.com Gur Stavi gstavi@mrv.com Harish Kanakaraju hkanakaraju@vmware.com Hari Sasank Bhamidipalli hbhamidi@cisco.com Hassan Khan hassan.khan@seecs.edu.pk Hector Oron hector.oron@gmail.com Hemanth Kumar Mantri mantri@nutanix.com Henrik Amren Hiroshi Tanaka Hiroshi Miyata miyahiro.dazu@gmail.com Hsin-Yi Shen shenh@vmware.com Hui Xiang xianghuir@gmail.com Hyojoon Kim joonk@gatech.edu Igor Ganichev Igor Sever igor@xorops.com Jacob Cherkas cherkasj@vmware.com Jad Naous jnaous@gmail.com Jamal Hadi Salim hadi@cyberus.ca James Schmidt jschmidt@vmware.com Jan Medved jmedved@juniper.net Janis Hamme janis.hamme@student.kit.edu Jari Sundell sundell.software@gmail.com Javier Albornoz javier.albornoz@hpe.com Jed Daniels openvswitch@jeddaniels.com Jeff Merrick jmerrick@vmware.com Jeongkeun Lee jklee@hp.com Jian Qiu swordqiu@gmail.com Joan Cirer joan@ev0.net John Darrington john@darrington.wattle.id.au John Galgay john@galgay.net John Hurley john.hurley@netronome.com John Reumann nofutznetworks@gmail.com Karthik Sundaravel ksundara@redhat.com Kashyap Thimmaraju kashyap.thimmaraju@sec.t-labs.tu-berlin.de Keith Holleman hollemanietf@gmail.com Kevin Lin kevinlin@berkeley.edu K 華 k940545@hotmail.com Kevin Mancuso kevin.mancuso@rackspace.com Kiran Shanbhog kiran@vmware.com Kirill Kabardin Kirkland Spector kspector@salesforce.com Klemens Nanni klemens@posteo.de Koichi Yagishita yagishita.koichi@jrc.co.jp Konstantin Khorenko khorenko@openvz.org Kris zhang zhang.kris@gmail.com Krishna Miriyala miriyalak@vmware.com Krishna Mohan Elluru elluru.kri.mohan@hpe.com László Sürü laszlo.suru@ericsson.com Len Gao leng@vmware.com Linhaifeng haifeng.lin@huawei.com Logan Rosen logatronico@gmail.com Luca Falavigna dktrkranz@debian.org Lucas Nussbaum lucas@debian.org Luiz Henrique Ozaki luiz.ozaki@gmail.com Madhu Venugopal mavenugo@gmail.com Malvika Gupta malvika.gupta@arm.com Manpreet Singh er.manpreet25@gmail.com Mao YingMing maoyingming@baidu.com Marco d'Itri md@Linux.IT Martin Vizvary vizvary@ics.muni.cz Marvin Pascual marvin@pascual.com.ph Maxime Brun m.brun@alphalink.fr Michael A. Collins mike.a.collins@ark-net.org Michael Ben-Ami mbenami@digitalocean.com Michael Hu humichael@vmware.com Michael J. Smalley michaeljsmalley@gmail.com Michael Mao Michael Shigorin mike@osdn.org.ua Michael Stapelberg stapelberg@debian.org Mihir Gangar gangarm@vmware.com Mike Bursell mike.bursell@citrix.com Mike Kruze Mike Qing mqing@vmware.com Min Chen ustcer.tonychan@gmail.com Mikael Doverhag Mircea Ulinic ping@mirceaulinic.net Mrinmoy Das mrdas@ixiacom.com Muhammad Shahbaz mshahbaz@cs.princeton.edu Murali R muralirdev@gmail.com Nagi Reddy Jonnala njonnala@Brocade.com Niels van Adrichem N.L.M.vanAdrichem@tudelft.nl Niklas Andersson Oscar Wilde xdxiaobin@gmail.com Pankaj Thakkar pthakkar@vmware.com Pasi Kärkkäinen pasik@iki.fi Patrik Andersson R patrik.r.andersson@ericsson.com Paul Greenberg Paulo Cravero pcravero@as2594.net Pawan Shukla shuklap@vmware.com Periyasamy Palanisamy periyasamy.palanisamy@ericsson.com Peter Amidon peter@picnicpark.org Peter Balland Peter Phaal peter.phaal@inmon.com Prabina Pattnaik Prabina.Pattnaik@nechclst.in Pratap Reddy Ralf Heiringhoff ralf@frosty-geek.net Ram Jothikumar Ramana Reddy gtvrreddy@gmail.com Ray Li rayli1107@gmail.com Richard Theis rtheis@us.ibm.com RishiRaj Maulick rishi.raj2509@gmail.com Rob Sherwood rob.sherwood@bigswitch.com Robert Strickler anomalyst@gmail.com Roger Leigh rleigh@codelibre.net Rogério Vinhal Nunes Roman Sokolkov rsokolkov@gmail.com Ronaldo A. Ferreira ronaldof@CS.Princeton.EDU Ronny L. Bull bullrl@clarkson.edu Sandeep Kumar sandeep.kumar16@tcs.com Sander Eikelenboom linux@eikelenboom.it Saul St. John sstjohn@cs.wisc.edu Scott Hendricks Sean Brady sbrady@gtfservices.com Sebastian Andrzej Siewior sebastian@breakpoint.cc Sébastien RICCIO sr@swisscenter.com Shweta Seth shwseth@cisco.com Simon Jouet simon.jouet@gmail.com Spiro Kourtessis spiro@vmware.com Sridhar Samudrala samudrala.sridhar@gmail.com Srini Seetharaman seethara@stanford.edu Sabyasachi Sengupta Sabyasachi.Sengupta@alcatel-lucent.com Salvatore Cambria salvatore.cambria@citrix.com Soner Sevinc sevincs@vmware.com Stepan Andrushko stepanx.andrushko@intel.com Stephen Hemminger shemminger@vyatta.com Stuart Cardall developer@it-offshore.co.uk Suganya Ramachandran suganyar@vmware.com Sundar Nadathur undar.nadathur@intel.com Taekho Nam thnam@smartx.kr Takayuki HAMA t-hama@cb.jp.nec.com Teemu Koponen Thomas Morin thomas.morin@orange.com Timothy Chen Torbjorn Tornkvist kruskakli@gmail.com Tulio Ribeiro tribeiro@lasige.di.fc.ul.pt Tytus Kurek Tytus.Kurek@pega.com Valentin Bud valentin@hackaserver.com Vasiliy Tolstov v.tolstov@selfip.ru Vinllen Chen cvinllen@gmail.com Vipul Ashri vipul.ashri@ericsson.com Vishal Swarankar vishal.swarnkar@gmail.com Vjekoslav Brajkovic balkan@cs.washington.edu Voravit T. voravit@kth.se Yeming Zhao zhaoyeming@gmail.com Yi Ba yby.developer@yahoo.com Ying Chen yingchen@vmware.com Yongqiang Liu liuyq7809@gmail.com ZHANG Zhiming zhangzhiming@yunshan.net.cn Zhangguanghui zhang.guanghui@h3c.com Zheng Jingzhou glovejmm@163.com Ziyou Wang ziyouw@vmware.com ankur dwivedi ankurengg2003@gmail.com chen zhang 3zhangchen9211@gmail.com james hopper jameshopper@email.com kk yap yapkke@stanford.edu likunyun kunyunli@hotmail.com meishengxin meishengxin@huawei.com neeraj mehta mehtaneeraj07@gmail.com rahim entezari rahim.entezari@gmail.com shaoke xi xishaoke.xsk@gmail.com shivani dommeti shivani.dommeti@gmail.com Wentao Jia wentao.jia@easystack.cn weizj 34965317@qq.com 俊 赵 zhaojun12@outlook.com 冯全树(Crab) fqs888@126.com 张东亚 fortitude.zhang@gmail.com 胡靖飞 hujingfei914@msn.com 张伟 zhangwqh@126.com 张强 zhangqiang@meizu.com =============================== =============================================== Thanks to all Open vSwitch contributors. If you are not listed above but believe that you should be, please write to dev@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/CONTRIBUTING.rst000066400000000000000000000024201514270232600214170ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============================ Contributing to Open vSwitch ============================ As an open source project, we welcome contributions of any kind. These can range from bug reports and code reviews, to signficant code or documentation features. Extensive guidelines are provided in the docs at ``Documentation/internals/contributing``, or `online `__. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/000077500000000000000000000000001514270232600215715ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/_static/000077500000000000000000000000001514270232600232175ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/_static/logo.png000066400000000000000000024407271514270232600247050ustar00rootroot00000000000000PNG  IHDRNtr?gAMA abKGD pHYsaa?itIME  $3M^` IDATxiz_ IDAT<Ǽ IDAT;<<;<<,--,--,--:;;,-,;<<݊ IDAT,-,;<;,--+,,,-,JKK,--;<<,--;<;,--IKJJKK,--;<<,--IKJIKJJKK,--:<;YZZ,--giiXZYIKJXZZU8 IDATgiiXZYiz:;;JKKXZYgiiXZYWYXJKK;<<giiXZY+,,YZZXZY:;;giiXZYax IDAT|yzJKKgiiXZYIJJXZZgih|yzJKKvxxXZYWYX:;;vxw,--Y IDAT|yzWYXfhgvxx^[\:;;uxwJKK^[\+,,:;;^[\+,,uwwgihH9 IDAT^[\,--mjk:;;^[\PLM2./^[\OLMnkl_\]^[\{yymjk:;;9S IDATPLMfhgvxxPLM2./|yziznkl_\]{yyWYX2./mjk{yy- IDATmjk^[\IJJnkl;<<mjk+,,A=>PLMOLM^[\,--_\] IDAT;<<^[\^[\|yz^[\mjk^[\OLM^[\OLM IDAT^[\,--IJJ:;;,-,^[\,--,-,,-,,--WYXOLM:;;,-,;<<;<<fhgvxx{yyIKJ;<<;<<WYXB>?XZYXZZ+,,;<;JKK,--uwwgihgiiXZY m IDATXZYvxx_\]IJJ,--IKJ,--A=>fhgvxxmjkIJJuxwPLMIJJ;<<XZY_\]+,,,--{yy;<<izOLM:;;IJJ{yyWYXXZYvxx+,,vxw;<<uwwgih2./WYXIJJuxw6 IDATnklgiiXZY,--PMNWYXPMN;<<|yzfhgXZYgihfhgmjkIJJ,--uwwfhg;<<mjkfhgvxxvxxWYXmjkgihWYXIJJvxxIJJvxx:;;+,,B>? IDATvxxXZY|zz^[\IJJPLMXZYvxx|yzXZYXZY{yyIKJvxxIJJvxx+,,,--;<<JKKXZY:;;|yz:;;,--+,,|yzIKJgihfhg;<<WYX p IDATWYX;<<;<<WYXIJJvxxPMNuwwgih_\]2./;<<{yzgihvxxgiifhg;<<JKKXZYgih+,,IJJvxx,--T IDAT;<<,--vxx,--;<<fhgizWYXXZYvxxIJJ+,,- IDAT+,,;<<,--vxxuww;<<XZYfhg,--WYXgih[vxx:;;c2 IDAT|yz+,,?m:jXZY,--Kwյ,--WYXIKJ|zz{yyvxxlkgii9 IDAT+,,{yyXZYֶTWYXXZY,--JKKJKK,--3/0{ƛϪ G}2&B>?_\]B>?;<<PMNgiigihXZYKwյWYX;<<+,,:;;;<<,--OLMvxxl{ƛgiiYZZB IDATXZYIKJ_\]{yyXZYϪ[WYXXZYJKKJKKB>?{yyvxx?mgiigiigii,--B>?{yyXZY:jWYXXZYXZYIKJB>?^[\izvxxKwյgiigii IDATgiigiinklA=>XZYlkfhgXZYXZYXZYB>?^[\vxxֶ:jXZZgiigiivxx|zz2./IJJ.`+,,XZYXZYvxxPMNOLM,--Kw9dc+K;<;XZZ;<< Z IDATgiiXZY|zzIKJJKK;<<nkl{ƛϪr1V:e_\],--2./;<<_\]_\]XZZgihϪ[;<<XZYXZZ;<<:;;$ !mjkIKJ?m;<<vxxgih_\]A=>;<<:j;<<XZYXZYvxxgH IDAT|zz2./_\]{ƛϪ@pc+K$ !2./B>?WYXgii;<<lk+,,XZYXZYgihWYXB>?{yy;<<ֶ:j;<;vxxvxx:;;B>?{yy:<;.`JKKXZYXZY+,,!@ IDATB>?+,,Kw,--XZZvxx2./;<; G}!IKJWYXXZY,--2./;<<1%NB2giivxx,--+,,OLMA1T2&,-,WYXXZY,--+,,PMN< IDATiz;<<b*JOgiiXZZWYX^[\{ƛ9dAq,--WYXWYXfhgPLM:;;JvϪ G|s2Wgiigiivxx^[\?m NS$?,--WYXWYXXZY+,,PLM'A3 IDAT,-,Kw1%[2&giigii,--fhg|zzΩ\b*JV:;;WYXvxx+,,|zz{yy;<<k|ǜ9dAqgihgii,--fhg{yy?md+LC3ֶ @pS$?;<<;<<:;;vxxWYXvxxJKK{yz IDAT;<<Kw!! G}2&gihXZYgii,--OLMյ;k1%[2&;<<:;;WYXXZYfhgOLM{ƛlb*JVgihJKKvxxfhg2./JvϪ9dAq;<<:;;:;;;<<vxxWYXOLM?m/H IDAT G|s2WgihgihWYXKw NS$?;<<+,,:;;;<<fhgΩ\1%[2&YZZgihvxxfhgk|ǜb*JVXZY:;;;<<vxx+,,{yz?mֶ: IDAT9dAq,--giigihXZYWYXmjkizT G|s2WXZY:;;;<<;<<fhgյl{ƛ"c+K G}2&gii|zzvxxgihvxxvxx;<;,--{ƛϪϪA1c+K1%TXZYgih+,,;<<XZYXZYJvϪb*JVM% IDATgiiYZZ,--fhg,-,?m9dAqXZYXZYvxx:;;KwG|s2WIKJgiiJKKWYXΩ\s2WS$?;<<vxxXZYfhg,-,k|ǜ>73 IDATXZYgiivxxfhg;<<?mֶvxxXZYvxxfhgT+,,XZYgiivxxfhg:;;|ǜvxxXZYvxxIJJOb*J,-,] IDATXZYgiiAq@pvxxXZY;<<fhg,--c+KN XZYIKJvxxB2N1%;<<XZZvxx;<<+,,;<<2&TA1WYXﶃ IDATXZYWYX+,,Ob*J:;;giivxxWYXiz;<;Aq@p,-,WYXXZYIJJ,--c+KN JKKgiivxx+,,JKKB2N1%,--WYX IDATXZY+,,2&TA1;<<giivxxuxw;<;,--Okֶk,--WYXXZYJKK,--Aq@p;<;YZZvxxJKKc+KN ,--,--:;;IKJ_- IDATfhg+,,B2N1%;<<gihgiiJKK;<;2&TA1,--;<<:;;WYX,--JKKOb*JIKJgihgiiIKJ,--Aq@p:;;;<<:;;WYXp IDAT^[\$ !PMNJKKIKJ;<<c+K![JKKgihgii,--XZZ<B2NlXZY;<<:;;WYX:;;+,,XZY2&TOyWYXgihgiiYZZ|zzXZYOkֶ;<<;<<WYXJ IDAT,-,XZY|zzvxxAq2&lJKKgii|zzgiiIJJJKK,--c+K![XZYXZY:;;izIKJ{yzB2N1%^[\JKKgiigih$ !_\]JKK^[\2&TA1mjk,--XZY:;;;<<,-,P IDATXZYOr1V{yygiigiigihvxx:eXZYXZY:;;;<<;<<XZY|zzgiigiigihvxx^[\XZYXZY:;;;<<,--De IDATXZYmjk^[\giiIKJgihvxxmjkOLMXZYvxx:;;;<<WYX{yyXZYXZYgihgii{yyvxxvxx;<<,-,+,,WYXp[ IDAT{yymjkXZYXZYgii;<<OLMOLMvxxvxxXZY:;;,-,gii2./^[\XZYgii,-,gih:;;^[\_\]IJJ;<<vxxXZY;<<;<<vxw|zz IDAT{yy|zzuxwIKJgii;<<;<<,--fhgmjkOLMXZYgiiXZY:;;|yzIKJvxxOLMA=>+,,vxxWYXgii,-,;<<izuxwPLM^[\vxwgiiXZY;<<JKKJKK:;;nkl^[\4k# IDATA=>|zzWYX;<<WYXgii;<<^[\mjkIJJ_\]mjk^[\_\]giiXZYXZYmjk^[\;<<B>?{yy{yy_\]WYXXZYXZZ^[\{yy:<;XZYvxx{yyuwwgihB>?^[\+,,{yy+,,vxx+,,2./2./bY IDATIJJgihWYXXZYXZY2./OLMJKK2./giiXZY_\]A=>uwwgihOLM,--|zz$ !mjkmjk$ !|zz;<<:;;XZYIKJ^[\2./,--+,,JKKIKJXZY_\]B>?WYXgii|yzfhgvxx^[\IKJJKK,--WYX<@ IDAT^[\fhgvxxmjkWYXYZZ,--OLM:<;;<<;<<+,,_\]_\]:<;;<<JKKXZY2./,--+,,^[\fhgvxxA=>:;;,-,,-,,--^[\+,,_\]nkl;<<,--d` IDAT;<<:;;WYXJKK:;;:;;;<<,--,--XZYgiigih,--,--JKKWYXXZYWYX;<<XZY,--giigiivxx+,,giiizXZYJKKrpo IDATXZYWYXvxwfhgXZYXZYgiigii;<<fhgvxxXZY,--XZYWYXvxxvxx,--+,,,--giigiiuxwfhgXZY,--+,,:;;XZYWYX IDATJKKIJJWYXvxx:;;WYXgiiYZZ+,,+,,WYXfhgXZY:;;,--+,,vxxXZYgih{yz:;;2./:;;y IDAT^[\JKK,--fhgfhgXZYgih,--JKK|yz;<<2./:;;|zzWYXJKK:;;IJJ;<<XZYgihXZYWYXgiigih2./:;;O˅1 IDATfhgvxxvxx:;;WYXgihXZYgihgihfhgvxx|yz+,,vxw;<<2./:;;mjk+,,;<<fhguww;<<IKJXZY;<<:;;WYXfhgvxxB>?H( IDATuwwgihiz:;;:;;vxxWYXgiivxxgihWYXJKK;<<B>?mjk+,,;<<WYXWYX;<<WYXgiifhgfhgXZYXZZB>?XZYvxx@< IDATXZYvxx{yyXZYXZYvxxIKJ;<<JKKB>?XZYvxxIJJ;<<XZY{yyXZYXZYvxx:;;JKKXZY_\]|yzXZYvxx Q IDAT:<;--.:<;^[\uwwgihIKJ,--JKK:<;--.:<;_\]IJJ,--IKJ:;;--.:<;^[\fhgvxxIKJ,--JKK:;;;<<_\]:;;,-,IKJX IDAT,-,,-,;<<^[\fhgvxx;<<;<<mjkJKKvv IDATiz2 IDATs4 IDAT1ѩ IDAT)# IDATiz( IDATXZY$ !:;;]y IDAT;<<:;;--.:<;:<;JKK;<< IDAT+,,JKKIKJgiiXZYJKK,--$ !:;;WYXgii{yz+,,,--:;;$ !:;;WYX|zzJKK+,,$ !:;;PLM2./|zznkly IDAT^[\mjk|zz^[\,-,;<<A=>,--|zz:<;;<<,-,,--izIKJJKK,--JKK^[\,--+,,gihJKKJKKmjk;<<|zz:;;vxw,--;<<nklfhgvxxmjkIJJOLM,--,--<~ IDAT$ !:;;_\];<<,--{yyXZYA=>;<<mjkXZY$ !:;;$ !:;;mjkfhg;<<JKKWYX^[\IJJXZYfhg;<<,--uww:;;:;;WYXXZYOLMXZY|zz+,,_\],--$ !:;;$ !:;;XZYXZY+,,JKKk; IDAT,--JKK;<;|yz,--;<<fhgWYXXZYvxxIJJ,-,[ IDATgiiXZY;<<XZYvxxIJJgiiXZY;<<|zzXZYvxxXZY:;;A=> IDATIKJ:;;JKK+,,,-,;<<JKK+,,;<<XZY+,,;<;JKK,--vxw^[\_\]:;;JKKXZY,--JKK:;;,-,,--JKK+,,;<;JKK,--2./XZY_\]fhgvxxXZYvxxIJJuxw$ !:;;|yz:;;vxw,--^[\B>?fhgvxx|zzXZYXZZA=>+,,IJJ^[\_\]fhgvxx^[\;<<IKJgii$ !:;;|yz:;;vxw,--XZYIKJPMNA=>A=>B>?_\]PMNgihvxxXZYB>?^[\giiXZY^[\_\]B>?_\]PMNiz+,,mjknkl{yy{yynklOLM^[\|zz;<<:;;IKJXZY|zz2./XZYvxx_\]^[\|zzt IDAT,--^[\{yy{yyOLM{yy|zzgihgii,--WYX2./giiXZY^[\{yy|zz,-,{yy:;;:;;+,,^[\;<<:;;WYX:;;2./XZYvxxA=>IKJJKK,--;<<;<<XZYJKK,--JKKWYXJKK;<<giiXZYgii;<<;<<nklgii|zzgiiXZYXZYOLMIKJ,--WYXJKK;<<giifhgvxxfhgvxx_\]fhgvxxA=>IJJmjk$ !+,,{yyuwwgihA=>WYXA=>WYX+,,A=>fhgvxxB>?;<<_\]fhgvxx_\]+,,vxw;<<{yy;<<$ !+,,{yyuwwgihvxxs IDATXZY+,,WYX;<<+,,IJJgiigii,--uwwOLMgiiXZY,--;<<;<<;<<;<<;<<;<<;<<;<<;<<fhg;<<gihIJJIJJ,--+,,IJJvxwXZYXZYWYXgih:;;OLMXZYvxxvxxfhgXZYfhg,--,--fhg+,,;<<;<<giigiiXZYXZYXZYfhgXZYvxx+,,;<<;<<,--,-,vxx,--fhgXZYXZZJKK,--XZYWYX;<<fhgvxxvxx:;;vxxXZYJKK:<;giiXZZXZZ\웣 IDAT:;;;<;giigiivxxXZYXZY;<;;<<XZYIJJ+,,,-,,--XZY:;;;<<vxxvxxWYX,--+,,JKK:;;;<<,--JKK^[\;<<JKKgii|zzXZYgih,--fhgJKKXZY;<<|zz,--XZY;<<JKK;<;,-,,--;<;XZYJKK:;;;<<vxxJKKvxx,--+,,XZYXZZJKK,-,,--JKK,-,,--k IDATXZYgihIKJXZYYZZJKKWYX+,,JKK,--vxx:;;;<<;<<vxxXZY;<<+,,giiIKJXZZXZYgihXZYXZYYZZWYXgiiXZYIKJuww,-,vxx:;;;<<IKJvxxvxxvxxXZYJKKgiiiz:;;vxx{yyWYX;<<9\ IDATXZYgih,-,XZYvxxWYXIKJJKKWYXgihgii,-,vxx:;;;<<PMN|zz,--vxxgiivxxgii,--;<<WYX|zzfhg,--XZYXZY:;;XZYmjk:;;WYX_[\WYX^[\mjkvxx,--vxxJKK,-,,--XZZgihgiigii;<<vxx^[\^[\:<;mjk^[\IKJgiiJKK IDATIKJWYX;<<:;;WYXJKKXZYfhg_\]|zzfhg;<<IKJ--._\]{yygiiXZY,--+,,+,,giigiiXZYgiiIKJvxx+,,|zzmjk^[\WYXvxx{yzXZZXZYmjkPLMWYXgiiYZZOLMmjkJKKWYXXZYJKKWYXgiiOLMOLM+,,B>?{yy{yyPMN+,,OLMIJJXZY{yy^[\giiXZYXZYOLMOLMgihgiiXZYgiigiiWYXOLM2./+,,2./{yy{yyB>?+,,^[\{yygihvxx{yy^[\IJJ;<<WYXgiiuxw2./A=>gii` IDATWYXXZZXZYWYXA=>PMN:;;nkl2./mjkmjk$ !nklWYXPLMA=>fhg,--A=>^[\_\]+,,giiXZYJKKIJJ|zzA=>OLMnklIJJXZYgiiWYXXZYgii|zzA=>2./fhgvxxIJJ+,,;<;vxxJKK|zz;<<WYXgiiOLM+,,WYXYZZXZZIJJ;<<|zz:;;vxx,--|yz;<<vxx|zz,--giiXZYOLMgii:;;,--WYXXZYXZYJKKfhgfhg;<<XZY,--WYX,--WYXgii+,,:;;WYXXZYJKK:;;;<<Sf IDATvxxvxxWYXWYXvxxXZY:;;,--fhgfhg,--JKKXZYfhgfhggiiJKK;<<+,,gihJKK:;;fhguww,--IJJvxx:;;:;;vxx;<<:;;XZYvxx:;;WYXvxxWYXgiiYZZ:;;;<<,--fhg:;;,-,JKK,--_\]fhgvxxIJJ;<<XZYIKJJKK,--XZY;<<;<<mjk+,,;<;XZZPLMA=>;<<;<<;<<XZYIJJ;<<XZYXZY;<<;<<;<<_\]fhgvxx,-,;<<,--,--;<<,-,;<<,--;<< IDAT;<<iz_\]fhgvxx<@f IDAT& IDAT IDATR IDATSVi IDATq"IDATmVrkIENDB`openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/_static/overview.png000066400000000000000000006145071514270232600256100ustar00rootroot00000000000000PNG  IHDRRUt9IDATxpօf63333=33; ff3ef;fz)ww^mv$y,\Sծ$O9{1YB!B!$/B!y!B !B !BH^!BB!BB!B!B!B!B!$/B!y!B!y!B !寿pGtǿ B_K=-$/49 Ӆm:|I0]Ba^8"a|lJ# !y!H_X>aZa5kL0?۩S6!\ÕyN4i [wIS:cNZg^D!ovÆ +WQGuaqAe!xYrq'/_cĉ}/xjk㺈B T.p RF~m4| lq+ҥK/_t@-[ !-C=tG3D|KM)RV!Cr-HfΜ9[dV̽<3<78|p* ^mq#}Qw"$/L[݇ͥM0s w {ݺuSD|{h > ^h^TgM:\B ͚531i<ߢ^zO?4 t"F4BA7ԃիWuM߳`UH^IwP~ -*Bp]~ay#ľR)~ Dv{/w0RQ<9+xU !,˵ ݻwRJ+WdO!r ޼Y#1˝!1YzuŊqC'o|kEL$/ȵ @8@޼y^9w֭;wRԵdL^MS/}"0Sn~D飢 !r0PoM7݄fh (\ 8_p U謳Z`$2E{1gP^B 4n۶ɓ`.zރ@[ŋߵk%WH^/ P?[!3/9hI1Lna8O>>3[ /8#G:?݀BB܁mVhQb J 90D &'[nٲ|F,aec.4OJq?EX+EH^{.M0!FRi|ȣ95kYw·~KOFzPˌ2)y!D.oڵkro&$I"f3͂m4M+!> tDaÆN8( رc;_xn?\Ĵ}RSsaŒ56o~ΝvfT\> !{ q8S׮]f`a5.]xeaѣg̘r bǏ1n1 ySIPL3Jaϫ& `8(ӧ#DN !r,"P &AKhK*y桦/(mYZGhעE ?I{z5 Jxhq" ` ? 0) !y#49{.]Hs”! ݇9j&8+ COnvj0N䫮 ~j 0 a5lYF !r}WnEs]Pj9_~_|Au_ !brQf((PuT"WCuv& .3զJ^ć$̏>L)*=Fڳg5L7y:Ofa>E{\qA #I41-2(;{])@A//?Cg_}~BBd ^bE2e`t0a9U!"b{,_L`I68i(na}YgBU /d˗/_#X !f2{11>#,P_^C^}xL[@n̙3g͚BмswΝ;mڴg}[16QF0 SWa9>i;N@ɨlRoɟ R u>6`٥-BTh1N8' fSNS]j1pj<,д`P#u 7kJ зo_kr^l'l"SuŅ^ROFKB&`O?]BH^_ ,N˖-iAx_O?t~oܙFv>.O>$8y;$e.A+!#,vװ`F͸wG8ɘ|=ѡ3QD?"B@Uv]6m7b7Pа2L|p_"׸Bfo>T҄3,@|0Lkt 5kd&/n݊]FDEH^Y[k8!0M,hLC <]ӻ5"ǡzHrC\/^3A FhJסjR {֙ Bjҷ18#ŋ߼ys !y!4N7oΏJnҽ/I9 +&bIp?"-qlޚh4F};b1ɷγrc !|S5>!-I))+VJq0: 8H.Pn Snm֭vHeo.]ԄfP{^1򂯃J^H^fa.ڝߥýC LZofl :[PKK*5\yz>ʕ+3^8yJ/&$/R5y2r ˆa.'A)c$u0a!,le`-\)fćAСCGlBBÖYůil2DO>dӍz/ăikӦ qt'"RLׯUqhDM#IR)yロfaQ T^<ܤӧOwߞH@ -GW^ !8/Dݻ1<bz]0g7udc%E8l,kpWSuxfذai^ĮS 7ɶmm3䅐Q ߟSu#7qh20x/0"13A)Мm۶'ND"uvԛps9l:(a_N:{뭷PBYBH^/}^e4"353fZgdF!Q,0)=`Qڒ"I'Uɝ֬Yqe0 CsN^ !A&MTJcp BȠHNG;rFwuڵYi NbSt&^pѧOİ@"R;E;  F@hC3r!Xr ݱcwFFx~ر"&{6&ɋH^؉ʓy8\8!mN00Gel P,Ό(hV_'n6ydfRjKnRСC1dxH^DEBXaϟߘ]c%KOdޏfF4xO/nbệ+VpoM :u`;N>K=y7g$! /\;UH^eZS7.be$ț7/?b6>^z 蝾:Çgݬ[8kHvOy ɀ%3"ёvvkiРA8[Kۇ44ظq#_3Ejg+ M,Yr뭷/l Ra7Tx\P\ P^k .y!y!D`|`/SL :nZP*Þ!m+>c(WÞwbpë!'&|@I"E p٣n"+aKo|rB`U?GQn!Dy*ΰsvڵ<3,([XKBUa2=_|EiX_>< "mM , t6İqXO!D|lxgi * Rpу! ?  n1'ުx(wyJ%/ľBKz.[!8[ wwٸ-Ayˏ^~7]tq|} /*(~{'3=BHa 6l۳'x"n"XD)'O8=nݺ1ɋX y! 1t'YZXq] ' <QW_};/N*W$/ľB0, xyE(]8[p .NzW*xt ]>`{(AE}AP\9t]\1BBH^8s?QdSJLA},ݧ-Kk׆AѢE?SJ^ !#vFXPq_AhEmkGj^DQ0 }'/J^ !a=(gmw5B |8x`]yMuVwBqƙh.CI s D]Tn"/_>Ԙ #myIwJ^ ZJZ)u I3B$On:uJsa7i*/T $/#8-F$k|4!D^[3QL -[Dz[Gܧj$/h,Xlذ!T!(50.{ ᎅKDkz]^ Sދ!y!jԩSB^4!$/ZP03aM2#i `"vsF /;$olwPwW4R@!]m-֤I?uYM5KPp$FH^ DF,Y0` #B"jS<|5PEDBH^p>*\d>cV^mŋ#A8FB" y!$/\dseNaÆ\H^)Pb+P?"yېӘࡅm۶0y$^sm!?,N)[q`GfEABj"er˗5k3҅䅺ؽ{w_pqUW!Aɋ܉䅐ꫯϟ>Vs$,H[T.-p1bݻщ_\Fl't͊}EBp_yQܹ36~!y!$/D5yq'LtLsZ"X`1Gʝ?+QTn~upLdCITfO24<_0TH[) oۘ<$^Y"w"y!$/6nȔ  ~߿SRo X~k5luxدC8WH} L4)TYf_|nے !BD `La׮]# ,Ѻر'B6ogpr}/7;l_:d&^w /y5klڴ#ؿ|Lo o'Oo+6m#ܶ%H^ He!_ s[vayEum}-{Z[2 m( CjFSL`W*D{o8wx}_Yxf>{>~g0) 'i{*++{; 3˄` 0(DVK9k֬V¥whчIGhD-̥w}/śoA~)^:uJŋox8-lQQ:D*j%vx<c w0s?qX&ny)RD` LƐxՄO6N0.ݹs2g̷ IpxXE/z=3Nl|3wSUPďv1xfeQo1 8^U?1:ll]y%K%: }Q"J^&I PV/8^?x q6WS`"Kjb) ^+#Ԝ l 0V4Ċ1b}!Vo'Y 1:Po"ε5ɍ̖Ps⏓|q?Zjc W83owishfcpaxU_<39[";Cף39۲yfs*cin BXǀzW(t3ꋦ׹g- /1; I?X垝Q4; G&Z̥ v<ĘD2 /VSdVl^2^.fjxcVn@H?OkRb&թZb `GB*9| ./p+Vo OӰ{}4A+,= O4h !NK0{x;ͻSDOP3I1oO1oM%I⽐ͮDYmd*UܹP|kmڜ+X/:Vxམʲ.ҝahY_0䞕嚳 6˩1Epsqzpqs2iǃűW 2ethL}<}la.C) L# c^ J_I^X}@ gu}GTբ[?lцdK=i6ּ}@tGg)I~1VH=:{bkCba6kTV4So ? /pB#~CQ{O媽W;'HqߙdO"Z M{l>A:]*W,yO)GڔWG%[H 6piLUTIr&I.[4®^ܧ^,zxÁkhzlef^yiC>x^+Ms3\ѼAS4 $B%,i(\Xřw\HBQ""ZQګ{y+Kko@ɼbmOGWڕw[V,ę1FD0jS>>7E؆ R K|BtBkL&۫Y|D:J!r ]R[d[8*Ѵ0VFj<.\8^$ /f U2x a&56YҦvBWPPL*^~3;uV?L3fhmCWY30xdvX8̺^D1C:YZSaELϋ40taHW%"XE9Dv5]~_yrnG=*Z_͙k3vL6@΍_GKbmMqr3I) 5_^'L?D&M[yEN69\ ҏ+lI %VI 0^Ù q/p½ / ?SIL{F~]gM,؀| nkedcRbP龦񤙫t4$׿vn|_ݾg hCދ8XuQj܂LgLz͟6+=@^T8>wt4Vg/ʍ!ep`2tAܟW`dyH֖ t[J7+}tk۞7LxiiN%+ q7J͈pȯ93?l5߸KCWF.7VPP8 B-{"86{wb]gЈTP1l/8^prwϽㆃ#$ O\!|b^D9Y9zI!B4Eo=pkh&7Yf*Չ "Iٞ.jLkkRqwH" (pOI/L^_йc bL/~}ܚK?kpɥܬ%վԣ (7*$Ǔ!2?ُ}}]}}J]i&gPս S*Uk;bw-ރ4QyrRyi"U2}@20x{`V5֦KV]bN ,2‚g. <8.wGYz,؄t݇:OǧY&$DQ.Ȉ~bMm=tZ~poqF\%Tm[v ov3'/.\b|E[ŋL{Fz̢)ESH/Ԥ;u?4CI&TGWگYk^` z~e^9wcw1NsF_̹J8#ͧ/^h/<#xJ]mEzu&V$❶&[YR%ϑ.W|/KVTo,G!RZ80GE]xH[ $(\HDOx [)+ ֘7xN Px}{+k#&Wl +BNj Ap$m *ϸKSͻ46d𕿼:B_qs8²C+zW"C EGET/P"CUMsvX0o]Ck5Sc/o] 䆧/{UEWVwudsTӖ;4/d9ۅڱse,TqBgoYLA}+'[*0M78zѬSPVq |t*-IcWX^MV9_Di竕8w_)( v w(> ydp[O:noK;sRR1&7zUs}ʵ!/tm"r!oFսkX~l|pe? _6]dӄr\-IO'I/ E^&-*\Q.Do(f[uPV^'M$xA#]a(}>V5^p 8`{1&— bP9E9t(ܸ4_\dޞ,LHw~A[w8U4O^`7M\k}q]xHAH%%q"F -}A.fxm`Y΅{EF.瘞}$>b9KX.Xrb]d+Hu%Z6ˍRA髧z1ZFK"麮dI/~ -[Ru:^ermtTfƒ"/^xeK 5f{b%RQnNV] {wX.u߀Ct*}Q[QkOQd7-_"_zDޚ,m=txHBX8LK-1 xU;B DFp \Hw'_κVO[W=v":<, }ڹ|ݴdϟj]y\/l!3>}]̋ wAp[vWhս*QQ-5szw\PwbNSRmH鹪}\~SNwh$wepz M(IPW" \Q>lfs5 9u #8/Βוęk^!8H"}l/JQt!w %) (6E^DQ+Nl1ͺ@dx,[Dup. Rἧž!,'Y 7:cV.R\?ËLy4CWTznuj;T ގ7+N_'o#d{, &_/>8xg =*IbekA"ΗXJgy3kx!rʉ+4vcb k%kȥ(׳ֲKhs _:ZKsF՝d?Nl  іO.=b~k*Qb2kɁiΚèɘ"U>  P~}mi[ݧKQ'+GFK ( -tGdSSW6ޫ.% \9|.F*w{¹\˞yqܴ5deLO} L.Y6Gg/=ukx/0՞?5Bu`xldk\yZ;lhcޱ\x#%ۋda+/t֏pfhG{V%WKoV-Қ+w~ZPxIx ./Tg@YnG#R 5P%j)!ozTTًXkhv|pQ!o^= sN3eT,CͮEP%*'Nj'nud6g'[~)^Z4" 쩾pzF~A@lhQy\[*M];n[ ^t46hysO^qS@kդˎpxM2N\mcu/lyS /hmS,9tU菱vR+%I{.LWVɯJe їnogIVxyP W-X,Xzt?c ./p`X TR}r6t>Zڇ +D[ee'\'za!0p;UT>]$$YY+w|G0ѻU In5V'WIW,*1Wb5Ѣw6dPo*maʌ=Xj#>Dŀl/*k"=%kVZ.nK B78|r -*RE hnha 6cHT|z5uHiW䎵𻦤;Vq^mzW etwF;vWs7/>Rl f9f8u'{*_^`2vjv="Q`I<9xI6M M޻zr0eum{5?6-[ *\%a~pPU(8 ./ uR `bBN-l<p4qʍ%+7J|}>~ a!tm릺ޅ=IV=#0Vd?j۝jܖ&~JI1lku /в1 U,dKgˏBNstSaasDF EI41?Vt S1Ẑ$i+ސa)E\uDW.N6Y O6JŅ*||Ӟ'6Nѿ@nH$?Ql_㱼a|{si-{ދ&@ i'Rpt6_UP8Ig ./p2p/FZ+p %a)x)eu lJ cHĕ+Ҁ=RdNk!eţER;w6/B-1Lf QcoYPrcT퇫/b:#Y\99[p2c3{u@o¹x5g]2к-9st^;茔 pdX,נ z&m\$Rb 4^ *E9^px 9jx, lPMAJmP0UH V?^H4"v,nc/ ZĤojoFD&]' 60CўQ=MC1 +B 3p:* = '9\n, (o}165մyp641EP{N^=L>#XZqEυe%dG dRry4DY: psp72^=G?^pxa ( (^BH{c[Ey0A52]j#Jd# ^nÊv6>UF.r/Ӌht1ePtX.WS/^nTD(lFe>⥨ lE8Ncj' ./p:i tLVO1*`H{"JCaeI fز44>M s⅋DFba0P-P2*!p.:X 4R;&T_(cہEuW I0@'eU#bWxlg)/:#3X?<8`J'j 3`"#ी[a5.C'P^a-ު-n((m(aDd *#ځLJ1~ ١OU4}p؊$6q#8Kr4^eh/8^prx/ ̴^$/xcf7hbBaB(pL# Hp ؏U1cxno /x#]_@^= /x+AUs) 1b#3Gl~2P#lL ./Qxzi#1-fH^0"PgpO60*Q3(Ǩ ŋ@J2[02)Up팗F{/T/S`Ë''3Un/ z/zƁ$cGv0&XI/pWj`H]a Mf0F"^2Ō '^ ,}kp4/htcTΙ^XT A94Ob ̽*KQN׀f PʑȺ` .\ЌzqJF)aC3 c)'p%p%?`/8bWr. 7e^!^ |&LsËD5fch|]#}I ^`CTEFvU N$`U`o4h6ɞx̌+ ./ËgW\5X!~];d>bc"t{yĊxA̿Z碅z8T  MHWGBR\}":;>BPǀ1=@G 5% ag hE8sW -I(kd~*}jiL ?xK"+'gHK!_yA&4ƋXLAGQQx {D \8^px!s駞xb2N zn]'VDsܪ J`~oT0 L*hhQggH/=QV5B<YPn+Yy2B&M$%O7+I&XZ/oS)I j#^ؚR H5O=A+TcTB '8;Vф-NQcd܄B0CCAH 0]]]r7Uz) .9+Ҥ"Ӫ{۩B$pPB 8`dx~ Hkr9:m.WU#(FVEe${9kx̙W⋳9x ./ËsXא{#KoΗvrObvh\4]in$,V(imLz7<2L8q_; lҰbib@,m~y3| ιF!jRK;xzbB(?aj1p惬y\eKE?C4 ߝLD2/`PMxK_<&yͦua] OA8~`Rd.;'/_)1f8`o3N[٘x)aMn\g#+C&OO'O aN/8^pxc,8-2$ev? 33 03L1dƉQ`]m[؞Yժ4cS#8khn-ͯ ڐ:Wxz^aWL|FǢgJ 7(yb{(/8smَA"LPhaϟOK܉tp:u;~T)+]٭w}ݻ;ѣn۶05FۘʐJz{v~=wB*ɢ={ճ9S^؛SJKI{=_0TXԿoniapA))塲={է{t/&wWo޼$x3Ny2Aо*۫sw P/)..zY=n"p=#AԪ<j4~V09= /^N@IXD"V~s,>bLY!e4hXYiYIiIY)!d0οA%|{.+܉2w?,L͑2:8 򲁥%ϔ ,))5zWڳgWsT"&`&h^ y…'>|py3Z>l\Z^^>x0%Jm2t`8)-H ?g ;X22ɓ'O0aر`!7G=qD@8"GdapL8k nܸ),`,SYc?ΊPp-*Nb:dTnIcǼo9{=eÆN4X4sy+V ab^xz/'^dM#BөG_qTv k֬ZVU\SeU*W_`O|StQ'*r[K͵et1E˳|劋.XcO;Kˠ /Zt ],)<'^|f^xzIceCﯭW[_ǚǺ QB~6Ib^X|̙3Gw3Ψ$wAΩ=:7j71.B֋)F?T_wp]uUɂ*Iʡ4#go/1e淼-]Of7mu55UH4ԴHA(wСjY"tUihj-Jv{5ֵ瞔ZG k \a32~"_S[n̉R]BB:\9"MКiEo8 ‹^7GD,2^)hnl@&'R͉x9;xg%c5CS=;b\p!5Aַڱ4!-[FKZҩ$~4〬[3kZU^ HUI!cGmM D)h#tR-Pri;dSK3; }aQg,4tcs´v U U&F A[U6NB@GJG+GjKZxUdMvg\4` kWQګ5c܃Js uzHÁdR酧^xr*$k'[?2q\rndx[8j< 8oD1jt,E<9t,L`qq$U <#I Q&*D3(]kpRo{6;;ZFKbr`Ԋ9)[ݼiiuT?,[{nthN?ޏLrmν=PD=7v O/<‹@/fsbx\+ )vo_XpɠTs,XkpFC11@J*;z4N>|zP~C$cJ!lz 2t"D5հYt]UtUݚPK-/ӫEgY|տtahs"!'^xz /^!S0j(b=dP`JuXG2 QrV}׽{w Cw:c\DfY 2ځtP5L/J ۶l? U , 3S\ u] I|[ֶ*A fW..1mgլ;vmuwf#kr4xz酧^dza~^fU?PrZӸ<|hO^ܵ 08ɧbű٦͐FB]a;L/$֚V V'@/.E˖,e-F/LBw͜sЋ<9+Guhs^>v.ZxӖ{kkX9fmܶ͡#~ȉ^@^P;_~~S P^xy /^!h(ku\& o:l2jmD$4GcO4&.frf#2Uv{%͛G5 D5-p6^"2薅v1Աs( N zF2vmp Ћ/dtBU *"R˕}xGÇYg|#1녧^<?z#8us/"UPC0w]hȹA"eas[LqCRG>7+|LjCِlP.fc3@D}"Zrcɉ !Ghũ< KC->-"GM|Ȑ!fbO ?9 /^Z5}Nb1 pEz_@0'),}dWa" !`-׾\4 N5"e.E,fSx: oxäI8|ncT!zc*nYJnr%/r ea#/֨zzO/xzώ{ҜĆ{Lмy*yX3 0\j!%2ɶi$B\bz oڋ|;겡CΡd-V0bakK, *oQ$*Xs nDi\@,U` X,"$iT Fé)18KtjakA :(L%LM d|m̘1|fȑ|r!`>SO=‚Ӈz|~a׮M4on׿'ag>BؘJ1ykQ<!`& DT 1;GEgh>^F2; !UY$! @8!" Y| uȁm H ('A1g/ ui<|9#kwƸVL>=3 O< FK(~#U`@-rwznsuf >~oM ~_g?/~! ]4w]ͷꪫJ<ʤY܈!v85*ӯ?O 駟DvD˃`p'/lërW3eY'reЪc-Оc,;ZfQ$6A\Ӌ^^ht'71"eeec:.rK3pJ{w3To7 C~`Kӟ !6<\sp"GxS3~{à ^]{>YXAoUY2 6̶}Z+?* \C5.f$p_/^RJ_?3, 1:Iр B9@åR?Uުկe(DQvFd!Gh[RlF)PSThrTU(]v/wn;w,5THlKD%$@uMY#zaD܎M0r`rUF7~p}O" B應bʕLw,jﯽZX f@k0K =]„`" iU.vI-;Vf`uaa G8)yVX(vt Uoɚ3fnMe*L]fO/ + |kv6ܕ0Ń[,W5X/\Z7@ oFL kcl fݰa`>!,`*̀@^pTY3|q.'UD c0b1CHLA(O<$P]OrLjmؐhn\t$@ D8<sm '%fy8 Ks:W!5N)'@K.4VUUAl}F2Xxl }{ =ɑ"Q;*V "8ĥxdDԎw3JL,㡡pBI4K.ĢxeGgoh"jsgas6bQCxzUK/mі=z`Eqq1@5`g`exoy!])0|3 ->`FK)}NM0"cgg8JH{ ~0OB GHGW>JI￟k044SM l:Ԕb3‚*'U<)`ψQB4V* p]%hLLn9 Gp##?IZgX-@(,$$ᅠ-; :-CEQEѱ~Tx GD* 0CKqA .e(3Nʔ"]AY0$LqAxN 5f%PN:rBI?XjMItи;"uҤ 0tEcȎǀt=aପ%3]29B4^< _N32"^{xc` rCЂVjLCp P|eM!Ҕ?[#<%69Ym.~`*zYg5BH٭MqŖ NSm4T㍟+c.IA#kHLBDbђ\@ 7!טBV\!.ȎP*2 I4#E)\(`@/(9;)x@&AЗ " t=X߿?ؖh[Rt+xhRRk/TvE%.%"߄FF+PZMBDI&!ך0`&= ‹J;Gl]kE@5 `leLd1BvnB,@CP/ -H LF7_mJ4[0l(e/Ȏ@zx 1c>9mUp_dmB[3ŅVLC\" ht2SAH ?]\Z z4%BD4q`T(SY*9?Iv]!|%BDtDDH]hb =v?' '_5WHktWD+ x/ }D-=~!e ڕ_N^JJJI@>NO/xzUK/x M1co /* @L<Bx A%CЈ`:vdƈQRQ,"}6 "h'<IY>\Bh+هZ5%y- e(BT,HQBJBwoD-S]%Zk)(˜ X =f;wf LIGiXY{ns}~< l7%U7#7Xh;գUc_op`Sx胋."5J%0:b_HP lHjM01‰a UIB ]}2fiZW:.z,c=oXD|dFYr}:\I]88;op3sj1Ūve(Pwy͔ l(=NoFes[kd0kx"]^@:)k^x‹IgGk  Aq^WPŊtp{Ι5+k׮GHSDK-M؊pe*>JrPMo.uhiǻ䒴 K#ˇ_̙UY9dԼ׍;nْ[6m>wz*G gS+2j讧^x'K$dF(TahY=ʓδcBeʊe+MwSa~х翮f?9P)$s24O<` @_g%A/PxQ-~B,h1|K¡ 7މT5~WT$^M1yzC<oȗB/M1 pTe>q`˖m ,4Χ-:w*|;p0x"Յ8ck9>3G?޽s:!'-fnnl;Ur 0v$Pǚh Vش$XQ#-^xz /^dZzO822ɦ]{voٶ(Oxh_gĶ"E"-/-w5#q/_7=F_,cQp aezeu{=1Mai} QcsSe_XӿYKvӾu" ;=na]=_77wsJ~~G7k>͹VgN˿~g`jyxha<yF\/c3z$`%U".G3/>cK~8KbϺ+I -*xqS#HX, {:O'<[׋W ՒV/WUEmaml3&ŗ ʊZݦKÏ4qXg<' ̆4WT(qaPC6˼55ZU[ Ail6^Fy "$eq"t:7 oo.\ PDV&)x<ǁQxu$>11AoBW% , <1Z+ yVѺ^0uԍ:QE5e"./qp bH3TnM ,ԘàY60H9d$[1H_q#)+`"d:ٔ{7Hȃ6kÛ& ȦW6kp_6!7gkmʄ_ΆHIeR w|FFnVH ^( p. ^$~%VPG0݈DC'?+ g)l^B 'Xb'w[Vʘ)cP >NdIhn،iٱa$9Mf,,4:-+/u4ЍtX 8<'\bbܬ+P9!1{"}_ CK[ŵ'/c?asO,v.AdlV#{L\&1BLsXTfZaI`F$I$IvIHX!$Ym6J"Rrbaβi35RH4,t֒ҐbF, nq Cgn.sdEv=mi&acͯ>: /Hshʓ#t__g q<+ 'Q' ^(x8y?8*м[7"/ @*pI?L_o\z׫~Rp6~0u%vdDrk`R~;=<9'--ot&nxIx" VaʎYZ@MxsaMLȤ3FA?2_J.H +װt! JnELt-ܔcCQs5 by&]E*1mslz$a . q dCTz%!#L%aOT%>SZ~+x,E%/yLR'AK/UIUBI+e(rgCã>bja6;G2G< ΄ Q$!cb'[?z QiB:&N! *+P.r "#&#H)UE(Ʋ7ӻǂǟO M$n7ىx^H:D@˾3@9G|L/4 b /4rH*gx9x+wNxVK|4 T$#ݩi[d[ܼ4]3~Z* XL et^.)9{ٹrħuuumZq L %)xE}EpԱltn&<=05fWעÓ33ѹuυO_'(A 3ף3 WQThr:|m:2=tʱ~"?) _NOF&~ ߸ NGõ&Ggb4ZޟSq/u ^؂wkH1G8,ʲ)md QlJR} xa[+Fm@v~=v][8r%֤b-FhG`$1b$Dw'Cfa3ϞA<lٚYJ<vw8,Ů^(I %!,/ }{׿## _^ };7|q|ȅq5G/}46zi|މ@ȮO{?_kKq×F/1)[g΍ ^pp?jΌN   @3MߘaS/e̥+:O;BbxAl{@hMf_B $ ˪)^VQp%0J7nmB|4!r&fAՐ_Hp)62U麢&b|D{w/**;g''آ)K?S?؜AFyy9ԂW %)xE//rwޞC;}ݽ]]GÀojܽ:K]n 56F|#ucmuM m퍁@kp/V78!;:l^Ar)춵ڽ #jB@oWl:Mξw_w}yH2C>:go]~9"E(a /N_ |3^$/;${42`{qb/, d%񂔈)fVn&J͘BQn[< 1TIxAlaZTB7u@8p ,ᅂt*EG@0Ǟ={.^C/PBI ^(ubzwd _NL|zjЎͯ5}{TZ͇E!~26׺ve z6E_^$RHw\lC¢H5坳:K3}^X!mkhXW9b4L6XNP'1eʱ>Bn5 %H {xiq$EM $|KW|0W-"1Bg.:طcg9w{ Dnҹ^zhu7;w4vyXx۾)u>[Auuoӎƶ탢@[,L#ƺڎ~sshi}EZ~~ްzsDx \o;{;<kSݸ{[V}?%]eW-D1- /p@x[^@sTABxM@MАx KVd~ȣxb|"dD xG!=C=bGN0(%?hX1Z!j s$^ux{ 卩.‹wy?_ؾǏ??plc=ࢺ|fQHPDl({QP`zg{TT{FĞEA@:7̙} gzֳ]~٫ޣ~j/n9{kNY8Zȋ=z]D|?_-gx q1aQ7*/(; >P9;%<)*$!,41< :8&;dISE}%ݥKV|xdUO_PlhP|XpB8+7(|A.޷4tlmcFCԩQTMr/9[B /V^/ĢS;鍠3h`_xȘ@߿ejt_ 9@?s~ AkvMx1GB&PF2g+9KWH'p /J?\P^S`k5yoC -S /_ /] iٻ7?~gHz6-O|jrgKI&S'O65oRD E5=% na7~Ęד뎥 ,f)a=^E%Kkɓ'O:nI.6veo:YP\=wݱmGϏo %dF =+de)Kŧx{'K4sJCk֬ U9#(ŋ^M۴BAim~ȹs^twIl`G6{R '8&bjee4|x__WnݵWdΞkSQ6BO^9t{݅;3q4I8m̭ǙΞangc9ڂ⭙%[ 3728r$ksW73iuL~y:f&іRz mjo] Pf6-ж]9#m/{o߁'im{@/Z.Ѯ%;mܓ7oꛏ56kbd<ά&j_梚y[.+.ɬؓ|{Z|Bhq&cMꍜS, ;{ailmelxĨiQ}bp$,*mb"x !aa-qB^-G BR<"e@~@(R‚s/F|駟g~ g(FKߐW)au{Od}I:;N-V썛>cE 6Kv? "?|cG}g|<_}Hpƒu9`Sg2.~ ?`kbn>ۧ{nIa8qnDgvM?:p!'L*J>84ԥy+ɅɑKOn=~\p eIX!x$p yuu[Ia}ݴyyb%ϥRA~Rz|p@Y&] / z/MѣIP B{{0Պwy=z:ދm !K55[wKn^-|5r$Oޚsm(ߟ%ZW;~[ooo}6U[ cRm?~wȯpt_Y,[qxWw|?- \Ts(qO;qJ|띷w^R[~kvaaX(^,,x5T)iBn+6>YMM&.浟iR veW C`h NX&  { ѽdTJ ``d(T́E \a+2^ .ǵ aB.. ƴLƘ`8 BւêL7PO&NX7 2Z,xtcBrDyaX#+DPΔK-\ISAH? hF̏ٶG'sx ?IE 4?K)\ @"GU؝X )O NQWW70D [J@(H'_x1,2Be7bXIZ.Dͺ%S3FMLJ~}v]`=iᅖ s{Бʪ{;w7570܋s|ref*I]6~~E@)N3{;LwO[ZPP2VIc7,ݔ5kK 'i[s7'-K_<)gMj,7[ɞ֎v1dtG'di)S;L 5nf9~܄&xzf\``<:HW'(b\0'I,%TB3y "GAvP7)Xth-# اjj?/u E)?Q̍ ¸겸JI(8$`Q b%8 I)Isp+d``HB$`1;2a8eH7n" ܀N\$0Ҥ,Pa `@ )AbNTE<_}wO <|~\js *K+Ř0^]0+8ѕtIiaKr!eSYƍ "9dɺe%D9;\ʏeڟ< >sD *>rB-BQ.⸊yavs8׼jA9jy*C|22GKaiiyaދT.ܹM!_۴BTߟ[rǏ;|sW\ٛu l$i 7{b,͔d6r/+Qd(*ڸ$L eȨIp<,E_^t7VS$ SJC##ƒ! E59 秉C|l"fnan98XwlUSl\w^Y>SçMcnnQAcBM LJw=~Q 7:SOJt`a2ֶ!, kgO.ᥭPʙܹyB(sUݝl(S|Wu^ܷckm>'LՃC=P = [  ~8[?FTgV2k@f&lN[y\$p0ߟwLvzx_:U*%2^UƏrAn9\wmscoM6p ||5e]/.ȫ+?~㕰Uگ^~wd(fo-p =Q۰{oJB^S/Ԋ'pHwI5UPXl(jر hP'>C~ UPhU5b(g0bX0x9 5S̙-X A yGB4'[n/ #ؚ^^G'=o Nmw[Tc%%/JȌ'%Te#gH%+22V,9,O,ړ+'* 7 36yqY(/A/?O Jp''Nl`k4qVzc MŒLJI2sY 6:FL9{|?]]=?}?YO(8[(?^wwtqqRZ~+/SԒ<ǂ?C*Ys`Q3kv㌴LVCa7$G/"dm0Nb8_bL2~(xOQ@\KfpX끻ՂJ~EօNB|ˑ*EuZ^nՂ>b1mKAˁnZq=7:{dͪTYg5/\ri |J^zNt>v}8ݴu.arrcUC܇ GsۣVRLY@.!.[*h,ɼו^8r}ppx1\\Ͱcy/xN8$jfif)F{QXALLbw/4kE^87*Ƌf]pM>&9k 8t"Y04% EòW/*X\-/<ؾϲIl4}Uٶ [L3^IAx zzz&&flsff88c0 "&k'X훮p W/H&Hcx1ܵKVd(r+7biҠв貘HI ik\zq9{uO=!FP]B7(Nv=0x^n~8>uȞ99mE&8n9{k(_]^>|7PLĥO?{U7 {xGF؛׵0"5:͖I(0( wx1X3$Br+ȓùœ55)MI.*j 0[aBx:ZV7lQ7ߪװ~Cm9{@\ŏhkoaQ) y%/3|w\]p}%r)D;w0`=>fY;) qƇ}~D.h-OC(.~;ܺ41? k~캐W}j{[NL 7z: _K==X.ݰ)w)E ~>l'x]s "*fҔictLJ;:rE K ̣&9d16K /KWL<-0#KV岋Œ8$C6S^20 TvL0Mw[P%pV\W\"3n#9< v4Gd(~^d= ['ۻ}9X0C H'zUV(-LA1M  1 wUa;>"7 r 0/\{~R rAع[Ph"7jBŸn؍do*nwo'P<lS?W*=8Υ-ߵkF`+nQNg}$vwJ.K fh{DRxT ORZ% ö7Ddi q˲R1'g:^q{2{r  5#<^Sz;p# ڭ9QpDIPUcAϸ3@&ƎM /dh=rx_jr)V6ż4ƘH K%MEs셛 {ĔFH= % جr,Dg̱77Z:1eV-7jlW?dDgeۛZ:͚0!.m"fac [a9cR 5Ltz //>Ɍ W%/4|~R;iHMEׄBL4be`=: Pls/vڟ:淼"qeT$@w!J!ڴo ƻ0LzU*m#o*ĉOZ0`(51ֻuDCSG(HZl(48DpQWGBVX_dCc;r#ڵ$য հd|w] ぞf.Bwi[_ ڹsS?R[\=~y1bp;B ۚݤYrydPJE[Ver.bJgRgZYLl?)fyаv]Bgg˂eYM`:6,l}_cdAV ECv{7{jCƒa4dCVK^"i)J; nw?A(5rD7ġ"O3?nݰxގ3PvdQXYU|ߎTUU$ZHVfV %=Bp YNsrh$ɴ; ep]4DV[V_)B%?@ʎڽ1.]VJ2J}ߟ:E*)@;lg}-V_€RAÎ'jٻuKԖ@t_S°(| ( 96o0mʄbO'uǮZ\@EͲd$Zx1߀xAow֭_MpՔs92 &4ui-‹E‹~JS߻w9N3${utL) .u -O+ 3̯Jr Ҡ0n<IVӍtLGWE gY`s. d}WY\SY͔#,mmS}lW}~HQ#L3挏&ap/f:ןU\Zr' %T* :bB_ "īVZz5gӼiӦZdؙ3_!8GTsp$,(k[c JKAL| n(D:V;^QAsW1狳;$\x}R~أ#_6T4ɝ3Cxarib2c%咴g/qWP"(+W4W9M0iJ@JJ^Z\#9 XC/Ktեu N(1v=[m Ic^c"nk7ȏڊ^HZ'x+cJS}y渹8yJ" , JBya,1KwsٓL GO011koo[{~#?е. 9+mYɩV)SGi3z/&tחFGO0 M x1G da谌2\ӗd1__5|_;;[G^hp/d-Y/觴ؗ*IlTƄzx{m!oO'o/DQ\E$MR}|M$Ll~m;߸|;[բD*;ZǓۻPR*yD1/-gϫՂxA+ɤGat4s tljYPY{f"5kM:%#Wj c9u 7b2Ek0 9N(߾5hJRCئj4'wbeJRxo'kڊ;:-Q[PwԯGKtksuFN /??lpE(@H)iT{jj]hP+~{x|p9-@:&mZxmת#Dݻ;53oĈOPuF5<:#gkBTef'rVlD*eϞ)svs37jb:c x20 uv4iab0rNDPfдmm3& /M 40ؙ LY_F$9x :+7ԩvMMmmT#I~8߂96Ba`z|ŀN[70'+ 0||'>:c$DlCZ0o (JAl?3ɽ;CWHъ\ T<#LQWT-ab  )qZ**eKH{LBz$5_:֚7X54yѼjn}C?E!FҦ+?v۶m= {T2Q0^*~͓G 흘 %?7߽{+'u+ʹ \EEc)JRĝ)FpdݻVtرTks1  °f~hpm޼9ro md'#}޵o.Ohqisx8%-NL;8;z- d{8ZEO7eejj! Q 8eB"𸁈 iQ9])ɱ2.JyEr&'K əm#ʅID8wÚJEZ( )A Vm$BB\drA@#&b5[ȸ`!'NM(UB*J'DDp:ՋDQ("H'"JI3k6 ׮} c'*EyJQJ'f I}8TƗQfT?8PW'nh"p)ݻxs k5W5X= j B [[@Sb -жuY&t4w\öceǚWkm\}b{ $,ZD͞!J8%&~Ƅ8,#'[@(tiTh776 4 d6,,|Ei5rJͦeW,= Y 晆Z@S2~38`63Ǧp&al>5cyկ P;K/>gϞ=Ѿhx{i1 oQիwjiyU*.B72G[*@(ioW(L.JAba0?LzBQ !Ƀ? O"bKER?Q#.p@Ta7ΠC, RX.1?7]USCHb:Pْ`!~\"!: 1 ۩8 tJԫZP)2$ a*6$@RU.|@1hVrKTy-&a,}۷õ2QOHn'' 82|JR։"؄@nQ< I$Vv+:":M$z/6w\MnK>|&~5;5]Csa?8$$dƍW&PzڦnTS/O;ąͮ;kַǯI'qU3}"]2f;eΚ:gr꜉qӦ$O@ަ:MNs26}M̉ixk:>y]2ONe 6r]¥@q3[z7#oizG=zi8o1mxxŘ==]T7jࠂ /p"Eyl*/#2fF}DƞdmSZt"nB-WBD#F-.!b(_ /^^`E,OǷvcqG 'a)ErrSs H5τ)BfI`>ĭB58$r֤r'@ y8rg7a :Rde\w+\ (F [}q_ Vp<@9dQ5DHk鑶3&p'W PI2G!De:_zݺ^nu2M#DlJ\G@Dp~߀jNtc 9@ ,R ;`6}Ya+>p$0Dƫo/.s*_eWkke*D޹nQ_?6F# 2 ob:6 pb"Fܸ S#?c3@s #&zz&*pwS\@ H%.q\͌ ~m"f3" /B͙}HT tTLu(8#5O*2m@3 {trkr%|n$hh [5 qBP #A *@%5% (,,,//_f 8Ax"AH}XcjYZxie/FGO>}-{̀-RfwL?2]%3#GX;h^Gל'%,әsO fYD\/zMKg3 " օA)qSXs@10 3LڇyTAOD{h_`';?zVT"H*HÀ# jKڟAi'5P =и6p'Ah=x1^Gc6]_!cWQXKֿjJ5L> +Ggƿ^jo$t uQ_OSJA1KAW?R]IP# b*%G K.^ډ#^h<2lBO $(P*(| tQQ;x,@M4 u[/mZx=tmkPd~רOc=9^%d7YS'G9e̥Q C ['㰍:)6hqDM6"4 IoT;1c[̛in=般 QS}L-cå"v2j½)nKg]̚:ӭRgNLw. } P^jkIuw;4zcH}H[@[Z\< >- (n]]3?a`r^Gǘud:2#xiαEo 8XqXnƇ(oZL?_جڻLBeY a#+ V;2”S|Q ߻mȲWde3&FN?k9Zy񯉁щ/^]W@,Td j-= d@" DrȮalg!@OMfam0 X BJ9Ov g_LL1qÄN 6@>%$@f င<bC3FLI/Ps '@>6g4*OvʮlN3Aa2Rg %$}BrZ:BEAk?LS :&#vEK֌)P>̄E1%H0MтWw>{264eNt%~ d!:爋@Bqw !SD?z Hb7'pҋ1<(wzVIg{:FZou[;{Y/G gx1C"(ҏ?{($ekIcv*[ô;XHݻ0Hƺ#krK)釖v/z?"=9~'jT41v\PUxzFch}܆-4(60Yǵ˅5 N&j)x2.IҢ,jI+331&R*OL/PVST lH_}t=ԮSB 3Qa ()F ,r  ? A2E2[G@Hxb&_` l#qB$N&E\YT$ȒX|-HaLYB!sd$h=o Fi EEjA4O,Obb5O]~xx~E}knv=a#3K/HF (vB> QUi|*322HA@NS*z{B.N(Y N@ ,ٜ?Fa{wsO645NKBɁ U}ٽ+]a0D)Ut&T_E˿y\;3o{}k?PH|zVCyR"@gh ]B0p:ldu#  =%/$Y"3$׿Ao,ASTx#5 G:ȅٙ ڦ=29kjIhdxP7l?E_=~SCT9ǎ=cAppl44<ab8fbxz*c&&gf/n/ Bw,/goj(ɥ 3FÑXGlC[]Ss]c}ݹKùzF]&D7754547k:\SjϞ;sƺ3 ukkU57V4UuVT8[W-Zْfs9ڋ;[]e-eͥe- eMkO՟g@C-Wmӹ:|suO(Άs @Zp?AcmSSc{S͋[}cS#76 Zà}J"!˿DwSп5܁>(,8--7LS-l~ [N^HHlm$;ʣSͨ,i#Jn#~7%y[0S,@@QϛnT?,@meb,rooLlnl޼v}~vvzppXa={;wz0G==P_w'錇ZvVy;ʽO{{: #Ӟjs$RF;<^W 5pux\5o]4uu!4{}} ~x7~*x==ATuc==ݽqIo4674??w>g`kW׮nb} #+N,yA駟|2BTo[0l|~ :ɪי%2Pg~g ,UH QV|;J@UoeH%' !) J~63ϫcU Tjeiy||'8c@y Z~,9&o #MPQ# }Ljcm*nn\y+/,.\^_x cb,,\QB+sWf/O/^\87=81w@WE*t\H~Wq~v^}t`-&v1܎<~wY.{'F{f&&,^00?pin W^^2xi~av~~Ne^u\z՛W_[^0  'yB2//q70$/L AlcmR#YTTUD)ODZh |/yy ,/8sqIyV! i"!۔c P"-I%(746=5o{[1wKz[~A5#ykIa3%ƍRj!\sELK[;`3ۦ!Z^`|Lذ=<eA_i[ "A@FoGA auaN`vԌW4 8 |4/+@_T|xw{v&|clkc~c2j[; fs໮Hn%RɝT2K1LD'>CD@L׋|6̱߮'tATM ?4;q8(ko^ߋ {`y)!Joa  deP$܆4яcoPX;Sը\|ݜ6(IB D`.yLD ,/hPrDɋ ܒ dE}tנ7I@{2f5Ûa$0w.njH2+y9^[SwB+*P@":ha<{>΀U`" ЙAAdWۆ.fnLw1{^z[ާ7ǕE\ KHcM9J^(c\Z lOOADJN0"ȗ[eRtzڬ䓯JIIٳg!)~?NRAV04'z}@c?WWH1Q :&;5[pja7Ԍa`ȬO\+%HIj{܍+CX\SUGՐ!oYJb1; R;*A>Qa3+oAR#:_SxR2n#8?Y3v((X$1g5KGhx%kA*%_% Jm?b-`~*E Ї>T\\ sH#)gU?P=0CM6,P&PX bpp__wz*kkjW,0W *d`f'<ATlR<r(4]mp?Oij P4MÝ+SVLpRo,ROk3ׯ;s8@bE$P XeBL~MIP@*!8f=bTŊwUxRh*á˩:k@EKӽ̔eH@JURêu_ TFXօV?]OCy,'_5_%-P$v B[PyC=p' ĢcG=n^U-fyOl0vN,#Xldi3%wUA'pSŬ%"1U(Yc`'Uq6x Ba}5 HQIrBBKs؆P{7 u  # Wzeu ٟRT@)iwQ@!ƒb?@t?3f-=>ڿ?5&B/8? .zaNWܑغ40s~t(mB30J!E8A!(2;mhJ|t ~;  "jD!u[ OISJLEQx3 o<ڽyc wݙ"5jN)Tqh7ˏk*bN 0磚ka?s@ '4= +,/`6ldn˾h4o?nցYǧ5oI>vs Ђ1(IFbukͫSӓmG%{ayr8!)JAwqtyӅ]8obAI^X yaM! CU ’y VЛq z׳|}zkmQ"a'y*!a}5#-ڽY&<NBtyjK//҂Pk$΢TM%t=;%3m߆F,,/xA `Egt4PQΔ;4ldؑMpZώ͝KGgc]z\'PJR.AE-?gRF1o.fuNadҬA¡8,,B(9utR~IWk }Qur` 7J_ >*Пyt g 2SW/+JgtJj .M5;E8qaSSI٧\/ˋ#EVe6ˆUEUfJ#+zaShȠ 3Uj||˿+KO=;;^}U 8A(+aP>99?pM"B-ȥ,/x^.bCp\$?.)O<>SIF NoRkW]0쎶C(W Ar9jSN+RPhyx)gD=3 )/kyA-BR@#f$;HS*\ 0 ,)qE{:諍G6S}W&7C~oOs>c8%[,f d@z:ى3 \ q!g'Hm[c|W};?c87Ui|'6{ùzLL@R\_qc T) 2s院Nna´,/I=(yq{qϴ>{r4oljyBY/x[L7jg~㧪J<ต3SZ1lT`[$Kf%#`cF:>0Ҡ{-g !/)l}lyb{_~?g~{??폿ػߪ*Kܝ-Cg^YXYZbgkSVVH|sM9~L}5y }/k*ZxRL쭭nMM^gO4[?{˱ۿ;߮xF"~" w,=(cƭo"Wx8 )-zi{w35vJ֎L4psbˣ+Wrjʺh=S0kL'/ mcXÃQ,2rȼP9xj5oGO`AN=nGI51OXO,qc΂^rTlY$y(~T%6}~i*玓@5򆼰!mGB|(q;OzGE/۞ʜ/؟l;nqڏZNvm!Z9;D sv]\[Aɸ+*|w9(sZpr@ wEƒ#3rPI;@FgTK[_v4\7]rW 1g*?Rb{fgp8>9lϔ:Sl(zlKm CWkBOe%Ze7F-X\} >^Y3CY,) 63ϟ;WT6<{kQS<Ap<5Ex|ULKl3T֮MȤAeiQлhfP3#4VM8aiDZG="LސeF/wcBj h19ZUho|􇱇OlI3T8_,?{h Æʊ.w>]\5~eRa.UaLy˝'>^Urhh$&Oh'˜Bdȋ3sNveDbaC=٘OTC<u D8gG|5[ 552?陲NNdGavr_#r`y q]ZҒ@o,˞U,(c:km& Oa["q8 5|{heϑKc,)Px3iy 5;SPmrl9u}5e/ORppdzS %5[OUj˹O@X|U-3!*‘T2`3 }=[5Ӂ3ŋ+˓ɝ07)fy`yLt'uj_K`e pvНZ:yc{Ɖ腁N_ Q(THMGOW{16b"؊5% f!8 yП'MB@ ͯup$ U"mY͆g J|FP s=NRq*a_D%7pF yf&c7mex9I SR@j0aCMٰ$k,/x1ЅT35#(Y9WkVbT>ilz]2S>)Ѕ#83 A桥X/..@[*<-8-Y^_ |BKDzx'8Uest- z+LF2%)(g'ªy#;=}k_ |$bHbǃZ(8q.3!dx*CBPJk`ii\_]9q00o T9tPkrONFbOvDF{ֆWj?|}}I0;tY/` Z ƚ|ե/« a!N4@)UsP6'0+͊7w: D ۊeਤ) e)=!5䂑yCdê*Sf8"C^53D#Ns)@W3%gz-#ީ鍵# 0"E/.2 a!U"X^x#Aer]I'hPc<=Q +=E6a -5"`;;:R%mlk?N4׾<#IYr`P D-a8AFP&bq>R*>`Q.ߌh҆ 9wQT 7(\*уX"mΠl,8 ÙN,z{UpȑԇsN -3!!d=u}&h8튇#:Nql%(Ic3]i2ɪEAk(q5Cat4ȠyEXFKV%a!ga,Rv`PP 'Qg~_6QN\$,Ja_Y Nv:t_:C7dfJ 7|vTp)ZJ<Y5Tv3=IdmpZ?^IȄhM~cnNΆנ3&'N sUKE/0GX0G}ɒHeKۄ\hn= UMZH-ȡ)vuX+ črzbb;Lb&3'HOh/G6zE/HþK7摙"¼Q GIPc g?TJKk ǡ*2 T7̒R^}ANN@$,ԑ:ʞ @wdP:2{hlyc?EQڅ#AjAEE&|!+F*夀Bp[RՄ벵PY_JϦ JAřC )JJBOw׻S~Rv ZV3J0Ã>] =@+U3z,RySjZc(՟q_"}sZsfʭmX?uCQ#:GraSZ4}`j #y[0nm\yurfB5}cQrI5%AȃBӛjbVJd: 0G$PQkQ:  ?`C*y[6F6'V)6EWf1=姘JふGN\>uCQSӯ;hHb$+59;TW/߸6YP@u4XT!w)q6$yAbB/I!KjL`$m=q %8(tC""F5ipQBF3) (FZ:6Uʒ ShUU!ZzZ?Ƒ,/,6}fև T ^!$$ۀDr2;f/z#L %qzlRbx1F I$ (AOPhcթa8O‚lds)D-6Wi T))CK|ؾ\#"EF Pbj'hV%wmɑ#./X^܅Vw.Pr}y0n>; K/NuHLj2Rف ׃LtN*Ufq$3 uBYў-ӡ:) 1dn 36df#虲z]έ<"vQ0a}I8h &/WM=mF"lP~qb8^O}'dXSΓHGٶ#{v2`AH:}0 \(; jHQA$iI)A*sJ/<99:wa{uV66 FQEw=SZHrˋGbvݓ#ٖ k0Э-O-ݡzԖAV, m M"(]PHz $,u,,90 ՇB} v: 2uU?d,HUŪD2ǑQ[qg  aXz!o.K^xl2Uj@>Qx_t/w~O)(MqрHh\:̔-'TRY8eC/G81Uzv%섔[X2VQ5Gݭpv|)J!Ao(-HGl"n' paM%*0k[i;(]vC` apqVNAmWpPzJJDA`${ZF s2Y3 2OU /0X^zVz)!Esreg 2;гcϋ8aԱGZyMyDVçDZ)òw {aS(=N:+ @:O |X a8̓ɷ W[yy XjmdEQ<EBT͡[ܣz1CsFxl" F  P $Thr&9;G艈BIdC=Q* &JPqn ՠ\WZ]xDm*Iq=TJ|rʹT& R') ( >9O1I üVj6c+M zj : "_veT U7jjv Gf/ .-]ٹa딴 WR^ͯx(G&M4pBA*c!0 s߰+I7JuX: z-CK}+˓6%*}eHK܋lJ[$լe`Q%DB Zm4(9B(;V}5UNPmT"^bw}??2K0 0'L:"V@Ea'aCAqq E1xȠ)7&7WһToC\jaH%uȌ$M" \^K*)s 1>VPU;MU][tő{$NH 2b"d@=WJ@4 jB( 0 %*詈<*OAg q51v$8Sp}P@{rΔU]nޚK$RSyˋ>&K p"c݅E֡gv,ֵ 3ј aفh,*mBX&84nGuHuJ)a*cE ;z<9&𹋼]Nˉ~m}Ed$pZ \Yt6u6P/СZ80 0Cu |9~ ovt&w Ns F:y"޸yub_wn3?>+wN'ߪ-h 0 3,l[*wCkeu 76B.BIYn<7ya(#Dôt 9AZ. q[kaaY.22 \-׹mA `ɤ8B#k~vtksz"ewkUJoWlf =B0(ry $%)`a7 ,.w)/|P+u,eyfF6 w!Aq+sC"U$'׋[?0 0~]!͌mo .Ezcs_^ I%waa|@B)ɋ$ aaX^0 0 H0 0 aaX^0 0 0 0,/aayqaa,/aay0 0 aaX^`a0 0 ";Z^eM ü읅WnK޲]{ IBFIݽݽ4o9ܡG-ڳwC2!s>>3U,~XJѠҺ炌^{F99_gGޟƈnsTTT<9?  {4v /`I vQ:7D{,$m坂dD7/cPH]x{HmշDZiwr+u [Ea q_0^qx%/ĔQLZ88IWqi £Ogy; ; 548 '#FVkVoKn]]Ow-0Y;r5[==>_$&DJ%Ӭs#+x='z)A9@s܀gs:A+ /ZEkN@< $&<Bd|-]Car9̉k72'n|:qk~{liK[/y=cS޺281fF/fi 99,s{J "6j!ٻe}9&#LúJjǒ,HTTTeU#龬E;ǓПb<0d[I^f$*q <>`RʑNyTALxyf8N晚'=DYC4'3\ 8%kG?ִľ?oinXIӂ b |*av3='w hrlx2A}GhF @ SWQQ6+)\/4 Oʚc$z{d0$ os7WN U^sCy_HL[]sב/KGCIG * 1GepgݩZ7st1c\D&H5b>W^8rIg*q|-/c9. yjS$)E -}-3}kBJ1eUD_7z~f1/n(@L0 \Mlu=מEEU42a (01ꛄ3W^h8)FS5ԨQSO5I^%=W;}uM? F9'ZHA89[a4@Ul?`׵kT'xæ $ 55j `;>s@Ǡյ NO1yf^xX湢%򽹌]HaA"7:\S=xⱾwR/MUti5!C15Dc~" r@&}8I4Y6&W# 'u3.e]KZQ I$ ="(JBы5jԨySTnנ%S#wO0 8/^2 E"S;haSsfIv-~%54h?4'#﫨)et52ۻBš b  qKÕ{˚VaEF$ a\?]CV>% ± R1sIjxZыtYTƱnϐT|ڢAkFԨ-.Gύeczm,vtdR}TIW(4:?!O$jԨ{d( >&` > ȏd98kE(:a GƢ?rsg0=fKٔjIWV=~rCsf(lE ZzQGg]k=x/.ܔM* ҌNd&?8:8u9.ppΆ ;_\Mkn؊[ FDg_dEG.+X<_UhK]4q-$ пsqG% W@1H'.szP0($ dbNp:Z0RmZS|^=c)i9&МN bצoc=)6K{@ TPe_a /k @U`])kjI^,i'ڟ75#8&4 b0^I/VX23d)FWtJbVvRfdmY._ A|qu6`)tO<$Wz xZxlmd;|a_[x؎'o˒&QF-Np_ z= j`$ܤ{ tʬVWġ*HslUn@bSȲZ--/XLo -sIETR.j'i~UiOyEƑaz!b/@6Ba<C[[2OxS0F%Ir[Ɨ-Ԩ9  :jNoi 3 [N(kxQkG&T݈-@%o4bYH%.Mݪ>*>#i$FE_5|O曒on!cJ-n.'Dž??R nΪZ"pKkhxHޙ61-V^ #^p3e f"!/HF503aַt QOq"/ |Ɉ汮mzZ/ ml ry#<#ȸpƔ6R/A&{z35^,I]`a ~OF-'S\Ammݾd{4D/0@,,V^LF1co,/LCRd8b~PF:^|sMc'0t(#vb 8tUŋFXW 4oś"2DDܷ=0e3fD` MWy㤰J @S "//H̩*!J?^`-9dx akS!u"<^%2p;% Uu31?HAZ*fvZ;vt𽎫AJ=h[a i Ex`b!%^h#/𖗆-*x!͏,/ዺ #^AËCg/mjp xtN@HNE/$YPt3Z٧O>x޽{=*'[j!^;voF1^tX1^HBvuN2oelѳهO_g=RRyL̉6LZz0Ҍ$7dxa nU}# ةsx{$ x2f^0,^Ym ㅢjY8!/D/Ą=9ȉJeeA2f@|VO>˗/g 0s٨Qxsũ+ 9by x!ĝ/7:i B2]Xӗ?vəܾc|`27.f¡\}ыE o4%?gSkᅱߴ^-Ȁd5&e5`AXzPL8kܟ7 eTA. ^ ^Ԯ `U@/>DƲŋϞ= WyLMMMNNr! c jj'G`9}ڦWv^,89B)fy"#[0$>||3nG;LOS$=#l_o2 1aׯyJrNr.uHOf0Q f9Ca}̋\iڗHm5o2D~,Xxq mazג#9yvR%רx% eoܸQ,!q͛7'?~ LOO߹s^}[8s%jԨQ?K*J@ ل90^/&Ъ-kl7+i7>>p|&#x\zqvz&;D!XT?bmo"QL /&MU-Ax33,֌kP (/D!Ԅ^Wbelcc;$GB zJ;ECƋXIF+{h͛7eYI$_~?~o?׬Y)={`ȀB0^srDhi9zBrY9bl( S{g t"w8{|޳p4.=3v5r֑D@g`sCƺvh𩋏e _աK0KYAZ󺱘TVpC[ AG.[x|]m~zL&uRT7\a ;`gV5 ƫ+oo}l +e m /*^ ' Hz|Ϟ=P(@XBEӹk׮ 6/~K_׾ 񌆆V'Nd\v-_pD\6Ͼ<[`y{Kަ"qfrm/ʥkGBt"w4JUHHbcL:S-HCo'_3hmc[&cJs# )Z:nK52P%22N:(2EwUyUGq e*tJPbIz% \ucp@(^Jeu>,W`,K!.'/@tY UU1e@#= )df}[cЊRtЎ:"±{N^̜C"m1{ rh8vl(z7u:d)9Mmë7mm`㚝]>0qbڬDKjطTd`Yu2)zdB$KXԵ q,Zps}CZ$} (1}px^J TOl@ѣT#qFXBюf2,RU'KR(^TlC.6wlZZDsgBoN9DFЃܑǹcOrG=?=ޏoK-M:?\ӵwOZ݇O](*Li PJc/'RqUE+]ʢI A|97Z}j-bsSIEh4VB\)-EB-CK,dM7qjz?)UIEt6/zLv @^Q[X,Xz 0u{]UG޹WE(4122Gλwoߺu#j?0[ЋP[ U&Kھ3,?}bU(ބjM?m񉃑잫{N=9p3犇/]PHN=y:8c*s}=5xT>|2x+.E UJiR(HS,i4Kqw{2LdLoOW.og?p9{vfwk34o]3^&\Ca:3K1^ج聯'a1WRNT͕ c6fIpJv{ L.mS %!4L6Qh2p#)v+%|f Ġ#P. W Dݤ%CBX%[1sZ>Tڀ,@XbƌBBW1jZ=Vhmڿz}`s<9Eg"R @i{UtE?C7wyk 7{ŶɳWny(9IR dtŵW@E NӐWl}sC2D#,3֟_05k&S(GMfq\Pa>{̙+Fa&o(21cV^FpZ r4Ί# A6LPGSEfj̸1#7n԰Y~#:Z:{Ϣ >h$j<8p~5jԀ֯_oJ],^hҤ C 3]v;_t -UT ?#ii'ּkKϲUX gN=y}wl$UJ 8HI$L߮N[OG3[wo-^\;b\ /^7Q_𧑙x{$eF*B @l y<Egřn=M^snM[=+UYؗOkj:0D 'C 0 d^ho\$a'AmQ\_Ir0wk:Y7߶FQV<,wo+-[ǮCef8 n6)12{|1|+ 8*`}foNfZ~1MŌat_N4`C0KV {Q.l8sR3M !6 tW1qn΍oH|wPǻwUM^ {.Vh._ʉի!t_|)!,#F@ [j´?^M'+_I S k׼)fM[ 1ȩjxϮk,~jUQsHUUðuWmf3-s@AR[ꭏ~tޓaavEKMN6q<48̊E*5 rO/7&yqЭQ)&he4zfk27v {>, נ#\d0b؏|͕|e6r_2k\%T|~o;vX%;+o>ط Ly8Ë}vxVLo}2Wɷ%dkO35rK(HWr:ʷe*F4nBi<6!E_hngҢ^sGfMYgF|DtLQ,4tuN{Fi hU1q]g8 &IZ ̘ي݇fEw}ҮЄwVc Έ_`<c&ws‹p;P}[ o8krJÛj@eEq|+Ry߁.^/(Ak+hdA>ˆ]SZXxjo٪ $z7m0 􁑸>0q"Y(RP#֬VAE,QʈO|] fܲ##n6rhڊb_(#esVJH( ]~Q`I2,ϣqΆL 5l^ ^?VUZbѢ+V,9|I_zMB9{,$Bɓ'C*j.Df\K7rbtJh8Bm:x>MHF}U6|63 D:i/N:eILj$$'16 q`4]WxB&W'$7 *B &mN=wnSoҲ(.R@3(i#jMvUQXi?Qs _$td܎yFEyCLG~ZWf FPc@46Q%o~vxXqSyxb; LȸpG_y3d$Ƥؠe+L%Y$"[Ah5N&-%Kʳx^qr#g]v/̥|f=|);B {ZW:15e5AWJ& [r: & #(Minc6+d.rzfs{FHxi6 J| RV5VMNO,.RYe*Gjތ.MpS1afN]9lMkE*)&0 TѬZB.Y`Ͷ*%kL#.^ڍQSw oQEL(^d( #iIx %j8^~Bv,'u|j`h:}3AZPmJ0k#Q`3Vס(@PI;B~T {"$y" *`߶-pĈa^@RG/nj3z(%Yl e/]R>B(p`Uu ñ%+q}% {)Dr𨁀T6=v&D2F=Hye6r>ÂEN9~] }q]F̠L[lPӀS\ǀ24{+ -&!b.;*[EֶXi Bo<𷘫j!9M@| G6}KNڂqH`0⺌qB'Q}*3cΚ J se bۭ\"6>fpxA'u<}}:/W` ;!7-4b>:΢:w(._ Ep-rBmR1Zx͚~O_*$'>xr=#/G|ۧsωZ2^|׵}NmV^0wCXZ{ݶZ9xe,h[(Aa# ԭwd'ٰjݦ!f;PMwsѧ+kNJ ʈ"@ {|eڶhѪy]:;L' R>/3osΟIobׅ^Sf̟߅W5./3lW!NfL͚O__Ə7|^ry㘐xF}aԱ#0JWË B3ܺq/ ڵKnݞ?N%X*tAq 0Y$2vR7Ncc7zVUYuk'.UC1P`ಌ^jA fx!ݕPګ_,Tg̘п35AT^!>9zmGn=VJ)䀥H5dsm"' Zf>C+f,`X#C$s3\H\+x½ ˜p;ZicScaQPqڼ2LbT]!j(V%s!M!mn20|SeC/"(JI,"tfD^kv!XZ  "D .$먰ߏ[]M)β:m0+zWvvܽQ9XG$'4$.`\PnݭDB-6r{_DɅ}ě4Ac:8ı ̃t]t]EBl(*; @cH!2qwoW-9po1¦M߫ ȽMag `b8Wn51ٳUO31c n18_ppMk ָݼho^O~J#vʹvlݲWO!t.vbWmtۓg#bbGD5g.]u3JSVp#zJ3`9_|睻EM-[|y 6jVrC,a]p_f*Al,zQG O:i285%/)%A'qn'FCg!6^r.h1юLT IƝ535&2I!(D}r }`5.ލƘX5YNܵGW|+gn%暁x)&gq S| ROs0%E2aU`*$ 9 |||=qG_]8׬3gՂBbf3aGf!RNh-s$Wr@SwBz(-kE) @ڙ!S0QFjTvmh .NM3îj-7`9x᧘6e&L r^,빸+9E`lI۶f@NX\虎nC|4qp|9{'<-3h߀zq#J0w"rkgF%Ҿ ^ N \߁Wˆ]5~KDn? ̠)`ߐ/4l%豚 =wm 4ȈNQԓK totjzQ~I2+KGwD':kOzGgO?P}Y~+/w'F2}qP4eϐ & {~ul^șs![5`Hg]|=lpCl!$Vjx?V{ شw`/.]j^FPVgϞ:vɓ'Bѭ';y^h1cì!i{,J0t%iCe_qY@H`G?jӡ!Z8ԟῖ(GwmZvTxwm{M݀͸Yd;dDnG "߹Cs' ;7oĩȵ|񥎒̟{F[VS NMձ|&PPY?~x,M-041+Bn=|/Hĝ7S6GǺ`hMmqͽܠ3-S0rvKxI Q5=}k#3L}[]gE5g*+(?}/ u B]d EJ11iJ̲&rq֘,kl6(*l ' #YE@ *e(2(KEYUvjƚ]|tu,cҌgUxa9b+ڹ;{,%>|i0X߷K+Gnv'&?u O'if:yjbol9BEuprBB7z;;3εN_SZޫ#9V5N#mԻ#2"%[E/W<0lShBը\%."h![3ȊiQSH3" Y>u`;nÎn=<~PVFtBK!7I'څY<Ӄx~,oxOa!qhYfȺ9po[ojЯĭg:A^tǿ z7?>ry/ҙ: vЍ}IRG)~2BƝzD[;'^NCui Ѕax'Bߚ\ WkH#_q\ct3-ڭ%hT׶26XPchQ xޏ^嶯w87VCWQ7T*-++[3v`/vI?a36lX}|&KOxGf2cT[7N$ ?`:Y^n(9yA~qPQt".0M:j㒉NHD7B$iԢ Z7پ;mr迧 [Ğm=M٢ԷSm"P 8'.H[2Y܌VhI#>j b?fؤp/Sˣ7外N]mʲ_۷3/O07Łe &%礄]u+YJK$Q3/IxA%{OaH6B zrTRQ"Cqdh0 ;a0JAJ XQ^@cϫO7> &䘲B.@\+0s Մg,PJ+9U +)$-5X)̨$lG <Ʊ Oh﹤k.Ls} ?}񖠣"D].Byv +{OBS®y" d>{,0%MG'&<d56$ia|^d///J E8b0|!&B D]e{zكLyYLoM;*&/j0iV-Wr$iSǽ Ni1op2-38|R6( ul*,!x~E2 J%) #>6y$U1iCẗ>%z|Mʽpȑc3Z=H;}oxS);plvn~zd+Uc/NlAߘ)ڒ¢\:52Y'i, =EpKsNon>:nKM,L[(WK?"wJBr]:5xiq :(8 @@Ia:#4ws:#N$IMm6^@b4}=Q:#Y|)ȏI2G@ Hȭ22T)>%{kWï'MV smƝer&6iT=-hDޏ .R%DjSdoDΫȷo{A6%*ػC^n=qZJ^~B5*͗૧edKj+AyY-1N)p*LGn]+f+s@M#W^K+x!(KJ֔J4*s;fg=fucF5@W9{žVo9zyjJ/I7mɹg$ehθL^$9g& d@˰W*"q'ܵg7ޝ h [nq@`Xlab"N#HO@Pܾ3s@|i0=co$1UYa:pcIj<yj5k`LKb1/`ׯ#ժ1e1Z!69bǶ gYVyv4tI0טGV,=y$^*rh-SC(Q*#Ф3NvPÍ ל9SHj}ƪ;'v\UjghӞ3{_UfSlʋbN^boxҖ|5%ۚMȄJ$nEdP< ξNrwCgK43l>]rvC@q]u3Xm~H\HX8[lH3Xecw$DXk¬MZ oL 8zn;E!)wlV"X$u%ZV7qؓKv!ץG^sBqBK%^r""׆N advxqqI}[b,⿇X<79Fj@@8-2:rL}}D-F<.1*7c0mלpgGVuq]a$HDʽobj5㏍^{Կ/N/0Ԃ hBON5 R!wIÐ$y\]Jxcgh< nuPURJh) ISlo Rp|J5,ۃӸį_PňݥtbU.FrhR 7";x Hbv!MMŪNwpuNx/p r1U5s_)#"K_@UNhW9^RgEd茉'jK׺ܽrKY+M[7 ؐ~ *~Bm6MT1\b~i %oX=ߥ懲t߉Aǯ?}gc_L+*g6-JN)Jl 6) j?-(ΰ$#PBpBiIcCؗܣHcI3W}=m%7bcR%uBr$k0o8Q Gd`HΠ@؞Zt꯵ҍʀZѡF%MYnf <{꼗*SJao |ZUWbn$斛\DF.#gi) 24E7=,&MيeB.2f S(ped-╜HʵoRWCؔ \Tb|)ުSx"b"٬_}٫;U"K}IZ#2nO|=Ib+@Ģj+bVѢ8i5DvTdfX8Ug5=]y; jF6n~?羙ܪ}g/jb:o*qf^q1Iǃ#M=K*Z"^- y xK'$ dTє=nAgPGQ<"p XA$&LOﻓ&;| ^2`>>P&thGίr߾xpN=A%Su -'`/Sx!4,phz ҏ 4j{~5iN"r]ƺƩYU ,0) >P,""vSrڀ9y6fbң.iȃDWE>x;5#Xs}T!Ą jB35W:}΃qK G5(j]N͐o?t~+][I-H,l* @ 0D ɨ@f$ l;M^tB8’$0np%EeW *[;zj$&ԃ:q@Ѷȋ/gfa]IHT?P=q}7x/^8 |yZ%|dmĈN$d JM{u-8Gϸ i;FN$Mk^~nN %"T;5C5?n򋮚EڌS\JTsmaЧM \"eOLt >!)995OQ fEǑFa{9za#_4>bkDw doTY~ce2 T)Bӱ6ѰHu,7q48bM:2Vah|A.0W>},vzNH9Z+1ILEK/[fUrRx!K#BƩM)1E @K/D^hQM{[aAp.b  TËuAGr2<xJKVm;yqҘ_R`c+"" :HV2I>kϩ޴84&J!*!)~sh.Ld-<70 as|܀b TJ[j QͬEۯYR9%敠q Ux\{ݸx<^yj$Gn8q*C|#~3h/WQƳYjLZ9i춨nRش^`a:ݺ}! ڋ){a-nSFރ_E&x@_O/>ˈrSM_U,LsÅ8H]B kȓg~իsO5Ցd(^J)rKcZ@Qc"MJQ/8ޜA[S<@0zu[)Q {e9̍#qMqV^Ɓ>g޿`[^D_48%")ua5(:|ޒR+wlXӝ!>FIvcy轄Gb6=sv [<ЬP٠)K2rJ՜ q6d:rhxa2heۈ#>#,Qc# Kx=2Xj-^ڻ`˂SB>(GVb_/M[\ -!%)4U~: s !Tl@ c]G@~ȭrZ1!B O3'TeyMYO8KQ @?<5%L&"O0k4L#+.h@5qLkyzM8q`#Ui GrJ7]"qP9Ӽ^̋A$RWP8Չ-Fgx6ȑFn{il/dabj>D:}Q`1:H$Id*_,"%ZHgLAF0|>'sֲcIppSf-xq|JB X,]-Ij(4 M(ԑwauY:Ql#2'+%qAעp6L{8}h[kG5x?xn](0>ǏN{9pt؂6ri nظ\~aܴem%_uM1M>-%gM΃ZcР^RԞ2c(6.˿+D ܕ'NDy/};xaቋw2W65 X^$P]_L4nIOz M{BТz5bt`N޾tƗ=~#:Pfp ^4kYӒAʈIIi]wSbԦXͤhqc'o8yL`:  ?R'?qv=G [ /SΪEDloN78FCIpiT^@p6e1ʁpIno&f#2 #pFEh=^} }g+.\gUQjNk)ZpG$!䕑Im󯥕LJ{0KeDMe)6/w+B%E٬V%Fq [*`%WKM~F S,4BB'T!OyH3zQ<1d@.hyU:{T>T97`7IN@r]i ` :V!s$)_HQHT?S35)pF>عvqe;.OY4?N ֌ا l2ʿrF>O+4"QR3\Qb߱" [s~hv5)d8sÁk aE'^ѤPzZWM?jJn~_3Y™{בXE|nR;)r(*/7{ւ}CiًvY T ,ބXOr9/Ta+Tg98Wp?n bBR-ͳ:+/V _ ۍF^ & ; $8iӖ0ʯ>SV4zE~&H(@񣱸S=`ANs -W4Kh+!ҩ D+y(㚑"|d ;S5oYq'6=s^\z0%"_voI('DZ;-qM jA oB rBbҔFz /**3r>+v+=ECX"R]?'/_UT\WC4iu#JL1f,"}<I4Zy%/%etsd29b1OhqX62ې=k&3j Ѵ04ㄎ N.J~~AnF^yFU[EyI1DTzriM `#;NXWH`FL(Ȅ,ye9F#ܴ=iMxk_mdH{ ڋjxϛ7 ^u‹at[8c|0#D cacBSAHMCF G@ HT6& ,w?o Bΐ u BƔi? &E@^Wm9 g@5Jt/Ҳ`׏l^^^J\k j3Ȓ9 'qv Sܸ?>8%xSLb;d3V}3339""6MZ1eʔE{`mV| /pfA3YY BLI)Wk5c,㸵pnǰm34&)'iɮaffha433f4ҵvgv@3;;>WW&"pSCca~Azz:76n9TIKh,*,kYazkss lj؂\eL3$@!ܶYKEeqaaam]SQ+CX* RHG;Ȳ%mɘK1; ,TlLizGgw7{Y(¬Q/21 ՜ttLtv:CQ;fѧ#)x4ŽU Jkz+ C^Kf-*C SHH _#7)c}[ D@$ rPFJ(S/zB?999!6zyA:ek?+*kM9 d N`F&bɾxXsF1^xN䦷dLe`Ƥ1JNP|Tbcaq'j;s"Jm e(|r\FΣ}`~ 5kZQ\cw`EW 2s?We XGbUd6(Sd[)uܜj~|ec^a+pŧOvON?}( A8wxlS냁BUuM:QqoxQpEEu=kU$a$%!([YWvϴ^lDlwxx[_E aq1SOK%1[4M@6AnP0$^Qf0_< ˸#|:n3W+; nRS}%`Q1»;h%%tM<}WF[h ֲ?cl44Kmq%#/;Ǻm)kLYᶩVjR['/ &|0-5 y0G_KknDOweڝIG53ٲ3g#8z=v:u JáC|Mm۶9z(0LLu*`T쌌/>}bJwBF!.\طoƍ8N' Gf|q 2FrW2n2 dzT+V,itK Zob/80)N膹u|GepX+{kyv(˼lop-j%&F9"^ {?ӳMmB`)nfGI0;{Ý-" ό3{A#f 49U> "²` >COfR8gFĵX ~*/r8-ّܸLY19iGO8~겯%uV/b$yԙ( *cx $^"E$2Ssqz`T,J 455de斕Wҫ2O>y۬ &Q݃۰q x cezB//`oF~'ڌI:{熮Yf]8ٳM9s,Y2 p vs?ɓX neH˗/OII3f' h` Ԁ|ei#?zYЇLm~@+R5t=MדX$Ou!5Ѹ~klro;Ʀᅩ*36wpUtY0v7;6p4G9)vՅ0 pۖ pDb\&/4a $)y6g%^IgW>?v nx_G܆ /=407zӜKPf_ ]ͽxa{( Ĺ6+>[UQ H;&jHX,p/ W7)&'a6V„Ymp6d/>Q ^PE4U=צ4۶a:/.;WTT0VÊ\ Ĺs`  ΰ,\$ )iiiPG02e挩d6x~m wYPN:fMԬEQ"/*8\iAY}4'dM s Iڹm;m2&lv)^ &Lx\khlɺ!=bd ܰy  t':NiFm f|m&&F;hKc+CQaBV8|fliE0' %޹D:*hD$*^xtndI^z嫷a؈^xaXƾ_/:G)w/bly曌vVBO{UF %NUB5+Apɰ6kfN_;u>Ygikfz]h?3fڦza.Jm/~쫜^8z8?u[5gLX@`P4 R&^rA%z` [ Q4 Z*`@ R/}$G6d%![rp7qҌ g\@J jr.cV h4 Tura&VAE2´&1c^so۞_c7uXQd9ȴQ0Դ dCWBh0>GؼX]bm,F5 2HI ㈔.rqrJKrV l[&&mI\}#0#_q]I)ֹ`Yt$tAb9gisTքax5 JxQPApF# ޕ>zi);RV`I [wmۋxa*jI^yB41OưI۬,mff:z8=vǍk-R0nZb%<Mk`2 qxiwӶ)x;@dl/gQ 33x&#G /eeeA'`Xx1'NLlp@OS rTAn\7P|/ 4"a`h onǵ&}QwN#tY4|xu@pRE7I?\^aJ0G^hM؇1*DHW#LqiNb~Z`3Z,017^[;Y;s:7ey RDTH?GDƻ- 5W o؄_x 5)]T\Cx!3 %A~EU,\30 ց5NN:ݑ{LMpͥ?h^ ך _,]];T"*8"FļjA;x=2{G|Ci0=sp7\wZˣ"% yq}ƌ]A5!&ә ӣA N;82 < ETAn_N1u7. ̢<:4 k9ںfB\xsA̢X.@]%)щe!F %L!J-pr(cLP>. Bb?:Ȯx\\}P; j B[\8m܍cLG$o)g0$ќum^( Sl4gAՄVbj1.n,Z,zlS%R~PT MGhv\S vE^u,vl3w *eL:#f?2x杸Tr  5 B%լBENRY9tf⽓mВ$<{+*IX$ߖDЍ.^8"vMONHu]V 8ta.MJ5F{\"oHs67A FF3Bk]E:"L#*%H8YL7&a;d>ςlCmZ!풇^ _RB\ +R̤,,_)q0kZkWýC rǢCvgB7C[n-"a] {^8w |9)9R (< R;[*aƤ" k'SX$o 'ԽP 'w~Viis$o>6Q`|&Z#솬yr1;5ֲ5Ή.3Dl_ m玢oҭX"x+һTGJLOb9(O C-]ţSw7 _P-V@;VΧ8zꀗ'FW?ze粊Ya?ᔣ!sȆzAxQVˋXA5ϩՑ";9Ѵ`qɫ\ܕ^`PAQk}32yqc8L30[939݂t@KO_(o&I:$LVH+DžlD([XpU"9uNT9/1y3 ԸtL r!T#5%2N! 7YIX8,t]O͍֠wXETȈ b4RPsG6.ۖٱilNd0b1=ۡFFF \MO$aNEkx!Kg'p@c^4Z8_3 la:u^ cJo 9h`j^ۨs}`Α E2~]O/d5E[ucf#ޔ4ӼTɅ>z6xs.dӌOG }xj.kڴᴶ>)ԋt1OM;L'pZg؄m >olGK,Fƿ^h aK^e׀ zeְ1 0Iߟ~m̬g2snޗ;A;0avD6w_{=a ga˺cU|c{=J[.O?K^ɯz K,O=}='?~ĉO^I\p̑=}~?~篎SP-;Sw>O=߷S?2d3o$$]X(PUV\g ~w/~~SM#F0_~U0t։[-soƒ-N54^ "Q^`Xl'x[0`O :%M"j\927Ve@_t؝T/ͩj+Jj=4*iʾo{?muSW&,T}4Wm B@¸TgzyB_slr tz.%f"-?̀kSHbg>"咒 G?]wX'v ebGXeg؇?dm(b@lox_OZ[.8bR&)q_C R`[.2{GP2Nu}e}qg>zQ|mؘRV5Bq2SqQ^I_~z?U"-OxN5L% QNFۇ߲:q{spY Y}|(b7=zsJs_wۻ՚f:Ʈ05Q](9{ Vhgn|jГt}oE6ztjt nC:O'BbxѴ7$7ޓ]tbi/,*<.7W$I?25 fævVP7>-0VV\:mtDMF?| q/>lLj"MS_fFٺ5kۘ2wU7ٺ9y~L4Y+?}=G>ç2?v"q^h]jc7|v?jٙnY2zMԔGj z}h!=9C4VB>Wᆈ/"5H>'q9s-RxQK យ<' %+22OL=t`[tǷ=Om;vGigf̺tEgVrT?vGl8g]]^dʴǏY8%:%{ %F O>3"j~VU 0/:|5gV-qýiga{B-v^3~ظ:6a-̰h {R/b M[-?L]>UD8,B.A4=shz P8VYϯߺTVݲes~?>;`R@XNba. GE M~Ÿƿ ͕~{뿦W%xlhb@uN]%ݶxGߴ@ټ$o\ױ\[/Uh`z7ifI=LE"iċCT-(^xGz9K6 ~mZSL@|b'¨I `iu*Wڒ^x&pUe_&X茭18؝`vaK v-**&-ywu6[s˹'gﵿkZ_cw]yy>dǞ3C?lAKF$>1-:q`|P3@I=:kU;6oо[_Z: |9ߝGxav{142#m HWiYdĎ}6k>Ёr˛P^|z CxǏ [1B5TgOǸ˵:@B0@D=bjѩ@|AoŔ n,FqlGLM{ְ2{ Sz~/WVV}SF}ͺPSԭL}cacXNCmLְxC z:OQLj E b%'j%C;7W?ń4QBlU5FC>i,1 Cez+Ho%c6i$YnB;kpZȃ‹l÷;w5:pᵒV>yPgӃw^܏r}7୻@X8@LEB:aq]w=޿pLDt捛W[׎<$bàMvo![Ƭ]3yڀuBB Z,d钐 .v՜M#6 =1h!~tym}e㶍sֲ :~@3FHוhZ 'RF*u鉋ɥ1ԒGi1W 󟩙o=1{&dl ڴ/hS||ۂ;Vlۺ̮WN>tOͰWŝ߱ž]y~KO]yv؃# 3jލ;6tΝ~700IpРH^N ӄWgb9 %2貖g~c0lՉkbp!SdkQ%g e8n^}c wKtxU"8׳cM|ys=Xe[ qaċgv!9tZkA2\ a2V(9H27qq<D1i{Y:oOK!k=HDz\v b213dJv^SD&:w`Sۡbmxhs8!fs񷏚2ײ0ӕKLbru.m-тr@`ϢePP 'waӂKΑ-3EdBy:{t󖃽u3Oבƌ?h 5uӪmwo?dDӑ.~k{[h;cY˦ L) ƒϘ L=;f`a#D';8pקþ܉7SS-ŨE] 尨Z4oK7EH+z1liS_}ԲgF?|\[6mQ1OKTJɨ IzFNܩҮcf-zt}KTB4剘- a2.EA,^8c-;K6FNCBm}ܛtl;o~O)_.jE JOM>..έtZV95bWK/+u MSh㄀ f. ^"Wb`(_&hjhX|r޿Ǝk߾[ݻm)ªzle71e,/ >w; 0+` ͚=7`QD쏮]=xO{v>mǎiBu-I6.@ɘ +j(\E2`C ER*fҲ(Ii pmGlV^9ۗKkNm6\~^ ~^` /DMDFjwQΜԿ3u\WayW-286L V9Wf5#`\"-E(@Ț8HZt!Ë́jiܡ 9c4X/.5?S祾7n&+-I*X$gr鞃'Gާcvmڶll@bFu1 Z/0_ m6cVM``&Գb(%-]zݿWv6z| G,#ЁQL^ߗ'W ` BN@j+۲LCgT=0qdTch5nkUڛ2T>pm~dY~Ъ}wZ09[OV0]$JQ8}o&aR렝[U"̛jBwplAӜJs4wј{h/Kh.ZcQ 1EUi7 {{vT2PA#!>laNS'i5ߦuܽ{)4ĪYcc9-pdGs!57s||)+A|"95=dY8_1W^ w*$> EV1D[8C?{|E;)cM?{6 :epr̘̹0'o</U/̴/Bݻ&/;]l<7؎!2E"qsfFglehU$[Wkq\7UW2֫ (x7\Qp-%Qrz&K]c>^|JK-k&O-Bb鐮@]5CBn3l"G4v.0^,ŜXu4-5'kC1CCdi'O=٫Wύl5PFP3f$ Ξ af "P,wkk.wF޺sKHRr WnY Q߆MmՌގT9!˘,%G6C >dPݪ|| S7^u\'M~{ ^"2\Kf0(`C1pL XKe,da#Q!~gy?3RÄEHBur-e肬W|[@'F1BKf"6B*o\;W4]pP/`6lAv|{rCӬ=(+jTٍAs8$Aj0%a^SW>ߏM-oG G޵҆,<Ο4mDTԻ+dq psJXQ16ʼnuZp"*H;nأ{qFYOiߊR)`A-JOYzy;N=+&j#vjծ];@[hxI /p]$s H( Ҟm{u=D,hEk}|QG=|W;Z,<86Z 56xuV3&$^dv>=x][5φp+n#S/be9]QCG>UjWi9WR)/ܼlZn1( V@><*khX*2dV y Eeܤu훀یgQ8~@rgŵ8܋TڠxɅl\׾Og, 3 S妛e3SE@0YKΣ2X;6V,fEJ:+$I$ܸ:~cxACX\#leűax3( /m$4-7z#sH3aD"UpF!1`?| S; 2T늑~I~c[8MY1(jƁܽ:⒃ awv*Qނav ZojvDc (e6BkXƒAs^UauJN]Z*)e~Rڇ>*E/y~,66ff鈱AlGl 1r7φ$%U+ݿmC^Ic Ē^XOڤu_^&XÖg h.K&/m}2e^8d)VC;.mZ#¨*47'9psC_T-sB矲2˕IiQyFbGkah٭mKFk%b-2mm) $Z=Й6x!++=P;2wa$%%'2VO‹^r{[B}}'#AӪt,-3SR껏_޼ۄO_R3s#IrJ WY٬PWY 0 ,"ԩqы:ķS^^^m۶޽; a@:0$?/OiPk⟜74MOMZۚN_ػJ0@=cC66L%%܄gl}c֢6]rysΝ;x%tחa@偺v0 dkﱷRa@xkk u*zk%g#G" \Ǽ9 2h_q /*, 9lۥp ~#klf8n|(/FRteL( u*й']t9S{{d B8G}OE$S&V{ =?tF/wsFIU*3E@\ܡ=h=iIx%VԒmݐ3L: QJB ^kCfekRZD< ݀|t !M#r?aEv%4r+xÑyȫ×f0ȟ|$N8-xXJK Ud-쉄X#XX2uK%&d7tP Hd֋RQ UT--Ma >3yeWG\ T.yt+b)^l>i"U8IE,tT(Fy\1'k NK548&EPA9)K |XIRnpQhဳқ;qF [B(| ߇ ^a@>D-4Z+a ơ>#]K>DlPN,o! =`R 0GNY-wc%5BO˅feQ8XS^˒#oɫ%`o$\j@@G)zu=DDWMolڷP zQ޻]SX}:RE ~k%6.;zt'ESG P;Y !N/*z| y<&! W*˪$Ơ'o:|w$".XqbU)_;3 6 4h;\\m,CƷ>ML6\`\EN$oGAy/xKc0GEM ɌZ/~^Y|Vr/plkiV'*W|ڙΛl`uۖDWM*z{VQ߆!y#Cŝ1{G j P%X^Z4)v|e#?֭qC+0TnHFx͓zQ c2sJZ{INzcpm0&#WZV:YώX3^|d:Iuq,m ?$R"9;ë?1(R.lɭA~RP<| j *$1`)[ ѠelpwO;@^Wb} \%CYIӈwqVpGSq>QRr:jaa1 -ZYzfGBS1cPGpKwJh<%!z]NPB.mb42"ӂsbK([<hØR Lk)@!-&L~- b-`WSZڂc{{̭} -Y d~SK덣elFZ |k#X/TBbÆ:o+ [I XYAa0X *XG3UגaL}T{5fxĉ k_xAˏQGW6@XrTTIȭw/ #FtX2ĒANOhŦb0j_ +5W^Hb5He8a.PBZ;D [G`wRG*ȩk_DVԊk"ab)~TN s=J,AO]yRXڶWz(eJچ I9RYUfY0]۬P)/>#˵e_d>ጦ2<@p{i&FK!}&T҇/'T_4:8Ē3uŁrDpL5U) Ƭx챃ڨ0ȕ-0ȍ_ dm9rQyv C@jQN PkA)(t ^Q ܳr̡^Me*Y&2y-1F_&p  /~^ܧFCxQA A̤{{\m}y赣4FS囎wZ ٚg1 |0o׀i,Lʞ:UHl% m z{_߻3=؎kNΎNWc38/!u^ˀoԌGTt?! JڃX(Lӱ[(7Px/<ěe`߹H#v{=Y?TuhXNf*"lih41%жƞ"@STW&=݀ٗlO222|"C6ӦNHiAp"vRU*,ͣdw(s:R^*ps$Ur0\?tB8Y"9۪ \aVN#5 :I.xZ?PrDng 幛sb# (9iEj̝l[8ɓwd-Ƃ`$eX@4IjBGZ !6\Pf ,ɕxmދmQ|~``ˏTS.4do;毈-wGAvp<;J#"79D- 5)8\vp8t;$.7~y%U,<߇23k}/a`Ë K@U0 >u?>$[0|||al $"0#zyqH4ߡТ7xD3$0]C HKd .0YӤ\ Ck)M򎗅%(h 5:UXy{Ęp+&dYxc,to-Ho.Y1ChӬgR2lijM2Т@/UnUM-\>T/4ilHXr-ĕ3ǩ@^.Ȇշ]:g@_ XϹp.d|=;Fmk%W+Ny)lG?z` QbcبsOQO 3GPI3@l š}63K l?3Y5 U S")M_ЌKsxͺ?Y92%Xȉi<|ؠE7lvg6-vxwK_x*~%zٱEo<{+a'bpQ6̕]-rt,|QڽMd+%⯩ib-5B\ Kt$@k5ff`>OT|xSLuy& s/`~ <=d/9%qvH[Qh>eɫ/*^ik*"O+[Ȃ6;86̲4$$SW|%`fHd%US[X6^/I~{ٜT6ho^62D[ ,XZ*V$Uq{#E[XDUhR(RpDW,HEVyF|z[-:ȑhjܹ|Ubc )Y0q QBRfJj4ESѺ*,ie@5Ze۰iB!*X2bn p(-nACxGB꠶]#[]dfx؊B%(*0V%3J "Vm;oAXxbKDrV)eϱwKI|n 0x)cէo>.-10SgB31& Rp,fۺo\> 3sK3>@ L2ތ,wcc–7-e3Y450نU ۷֡s֭(Bӗ> u|@ ZivSSJXA/PDaRWt7 (9&D3v\酬+(`N-9XC͓&ͱj ܢٴT_8q@m,7s)|u0j%۷2@34r6#BhiNfMe)ß fe +v#;xjiZHAƌ=x⌧p?ӈ1)9DsࣛED*\1]Civ ={h?VS\QR<)dz ď{8<1ibmʒĆB"Sira0wEL%JC1JV@`e=zAH8. N> o iy2=!q(dcBțUl&H jLBLzavY" PT%E_[XrD. S݈ۗY"pUeiM-&e@Ċ>g'[o~HA/|X7eL{Áhp~ok8A@Fxq/~- آn ]Bg&*-€L0 HѣgϞ*&BNZMĨ*KrrrJҟ16ZePNM%5RҹkJaM*)-ρX=0rѭ ) `8ZNE0 6RObbZ"tQ=5¦2`Dpr'+.ih2t HiM% MJ\#f "C DIg#(U(C1f)NJH4J CIfC;v]d[bBgjG5.ύ+ר_=}X[)kb1,"Sq<džc,L//?Xz1 #oq@e+l1A_.)IMBKPCk]=xwSwTV6aBSDɱNڟRS 1$mm/~j0s G tVE%c yifuŁM]!$^0d$ZN\S')XkLJ7%]Hh MHu~Ȗn-)%D{&>yU&\ ~Vm:"bU=7BCܡ]$Ĵan%z-Ypow ){/?lV@`y(X[ѧɟZܖlDcԏ/rHR>p B?uRJ ̙q̙ݻwg[n%>}:0=J?cwYqA{:.;EUɏ~!ӵh8֖ftЫV摖1r}jJNGHim[4\IGolAv;ܥdw&LVFpP 5pり5p8SFb:k&ψ<$-GÚGA4GrZ~#iY0oH'u^Ϣy[5DB7j1SB!3\X>-ӎQl?ӦSr/Jo6ZOŚ슌CNfjEi,33}gO_W/&~/_?|Jx6%%O>~xwĿzeG/:&!㧤ʲRQQaYrJ;A'wlZ^i`ad6T@r[Nnni5-BtD$~Wɠ|zfa~^\s9"2)\G,-U\XR]$̀XS N_ʪMSX 'tRI42 ˰t҂ܬܲR9 Dq[UJӚ$U*o"qKu䇶J.J D˗䕬HT))(̀*R!Z#J*z?x3"TZc, y ;jirPDU UЋhjaS)u ^W@Lr0jt`ZJTnTw9BJV Zfm4BTz:{A|+.*(+)*jZQ)AX盜V+pt%PE ehqTு 7Z 6)E *TTۃQ0n2%1^\~?^gY?&`@cLiϛ6m9s&0g u$Z/ d iOx/~5i*Op.ar}uk,NJuz &fxv$4jc=}X.2iT $GЄ)sUHc1w6l ἣk.<={7.:V2JQScۨ 'ecDGÉpDVt8yvd:IHzpjrC8+5<@hL6#.QXQDdjAI x_-ka$~q%։F7~MΑ'_x\G.0]fww1g  1Jbl5Ųb[dٞarбcǶ{CjiWvg۾OƓk}*[j{UjUOdʛ=أO?h,w] ͭvыx,YXXX\\|G{c?aq;GdY?0>>\#M覘>k;1B-K8 ֘).0X:#W`4be* ~\dz'%o*KJ1F*ld7ش&@u0̰lHnKD 7ħSG5's)SPu>T'Nm*W1Rp E ޓB y@,EsHSa$$)4[?f80ob "vxO'Nf3 b[; כW]SAc}yh,? 5ƚ 4ZGtFd# O {;YV?r(ވ?}?p{i~U7*B/&&;cAzO ~zA\e Uĸ>`0v;\.O#`g#G\WeX2Bj`s5)FhAh"'"1zpR%J4 X%"R !K|GfCU~I N& 'ȂkEѳh3gJ }0 ENānԓJb,|gB(>]}t40 GV*ZiƜCDTw,*4F#PXq ָSsy54մe΢TodX152{/L U^«ִGp-z'7o78 G9p' ޞ.|^78]^oGWWW!D0?|СC fp@oo7؃niܞNDM" \x~~.jju,ӼB+а {R&gZ1QP $: 3o!#XSiWUzݔ+S /#1< U5:m'D\J%«zz@I/B-rJO{՘a`ϐk%6Μ9^VX+wߍkx~^(^I ,zj:D?bF#Hv)1=,ۺrBghhl06B/'{zђFB60 i`3t#X9'㪗&ѣG|'NN:Xs=3%  tIHÒ?kb5 ébMhB/Ҝ^~S>XEe?f)|z1"zAN^$ ;z:2kz[QV.^xky!o?qS'CA ZS3(x- @DZw{饗3^}\gςUփABfB]h%"VU#v@1ahT~zNeq4n8%kRnza z%N"PB/&z+`y/;XRNEElBGqd,//SdK&o !w PRY c"f~? @'ajQQơI6E,B8 ٰ(H ҋNN:N/1SC.X/\=d"}yrY;]sOj^ia! 1 诂^DV3ӟ^\0sX/Hzq9zecz [v*yk)t㓨{CI"&OcK}`fр0h6yRHzqrmhe_2k? Ą.~Sk".Id$E tzGc򮻗[EIkyaW)Ѱ^ͱx''PD/1jzvVT?u6b&Ef*Hާҋsarc{N8RyAlF{W)McDz++6 VO4:^IJ6Y}/ED/HO> tF^<astOk> zЄ>^"EZ/^ζZsA/M"]];|;=k)+cїō)R2zEg;{J/F A/x_{\x<׸N/*"#G|>3斗 ׭zB @l,EB/ZŞO_lmkko\T{|5ŵ}h"Eҋ!_A]a׆H`P' i`^RWUڷN/ "E&~35A/D zak)/ z ^L9ޖM?y^[)BIֹJm탍> =^Ds6p47RZ)J!d)R$0 z_/)͹5mCM 7-Nȵ/ScvgGڡ# BzH.J&EɷW[s+[4 b!(4p!ᠫY>phk8HJ{7Hz%Ud"UOIh+|U$[ZCc@^s 33>,fƛ;{[sZvV"zL*T ŕ"E.qfTL2[IZ2wXs0 0;!ap#_hmXC:'+ ,("E B[(kڊ,˛2:G&Svѹzp&Z[-%?tȯvF ETTza+j\_S}i3i#EЋ@7<ӝ^8>@+Jzv>r.tEЉy41A1 NnFHH \~ (:רWbDP_>g]79] ~HB;862lom,+wbل@wL,Hӯ%$[GxBOhlP}7#!DSc@tK,Ժn[ tZ/s- E-hj\]PWsNd%41/藒CVJrSn&h&(qu P!+#T/3/bpv|f6Y?N, N_vl5%k ?sr"UM i !t|"EӜAÈmV6I9V: kZ]=}33͘ @/ D/L"_NaglQ\T{_Q‹lDYИaGF Zɼq,eyD Eg4UMhf ^zbt1s,F܋a0Edl<ѐ?:WBNMuǖcE:KM K$}_YRt鼰m`wbߎ⦲j[Gg4:к7غx?CjGыVO癙; wT˩~3CbHtLA3ыbӢU`QOtU=w17 zf#'w5le[*K]ρ^u߄n][ -R%$6 /n1)_A"5kl렰1A/b("\ETم?2:RWrs K~yF]I-RܴWp LkzCŎ,tla61`-βYQaS%N w~kdZTRPq]}[dK%C)R\h \Hts'V/RTTV_Y=60[v9A/8`XlvTgoʵ=X|k \1T0Oo^HHHNf GѨ؅4N{Pm` K``^A/E,yA/ƇF{*2K7u^ғqȫ!l) Cf_$FJ乢1K5]%ûb6nf1E&1b/1ЋP|cM=%7Lxb2LFnd$!qU1dZ^(\ݠt_BO=j*7Gn*h4W죈T:h=ƂȔ}zUb,Q\Wm?zJ](K EUƞ:_UTBB9Io|S zr0l?4k%@Q& 2Ca}"WsaiiCJ /YgR/$MRWS HpE{jA}ǖҦ-eeeyP=6xtBIa3Mn l8g}mg*;(K~5X ˹<RHeWִx'sX6poֶo+o/*)lo+(h4 1WP 2zaDžuj\XPu[m2kk]Ç^:w %vkqJobHHP8HwVc+oW;wlڙ=[a{sq|j!0@ϋ+rrRx>dXpTVe=[@~%R;v!IV/X#!!aq]~+u٥7/{ұ.^?8sR7cy6 %zkptfPuʛr+Q]Ǚ?W96Tza ^x8hOmG/怨 .<xE: ׺lu٥UYy[3AKm+VXzgIͶꭅE%,o’؆t+4Vh@vo-ܜ9g֬YۡnU9¡~06f.a`2@UQ,`$’^QR*Nl3T@}^T^[`Vy[dn۴#'~{nm ȬzE5We-46l/ޚWvfٲzECcuGm16dK [c6 -wLe|"|Cg*MiosZdžlʺ]9E;vfnF >3gKv'[GM \Ҏ92f@vdej媴eTtK:>8:;M;gqC4=30E:梞Ql`#vߞ=5X7oolZVv~梒-;wm.ټ]g>|S  ;wn(ؐvv tDCAʠ,Z>PsZ]H_zz 1% J<f [!Aid50ـvt9% UY噶,[E6 (Ͼ !!!T 5Y j:K{lMMV.$ +V -D`Gܦ:Cܘ0&Z&)8 -yl5嶝eU;mY@YefY.@*5A;M2[f*쪂F&CH%oZ:N ƀ@H b_k8Ԝv&G?7E=\ zjekH5EgcjFϞ.wI:;W@M *C.i/B TBn!'ԓ腈 cҢo "][о$ w 7<3418W踮΢<7۞ .VNW_SBBU.n/pwO垁ک=MD0kƤ3yN!% 웈s  ,nN -& ;Z |,9#378=ubOpHĞ+&DU09R@fƛ0H1u9:H{3 ;xý+67$0 $׊DaX+Fє}Yq] DXx=ʉQÍC( Ph#Gk}3vX=m ;m!"|z4 CI)X3O>.Y@ _XmQ/ A!oLQLk*ڨOR7cIa~:EC`xH*pR3D 4p&#bG {8q?7D:ȘA3iE18 8lZR$Xc.!ע37c ClN @$0CH;zele6q uG0ypl1A(lBaIm|C!$H%si0@P;>F sp42ƒ @$ϱ{#քcP_0ĆX6؃Bw>.b|!-~碯7 2R% G0-@VΞno\0 fgǢs@LTofr*73R7[~rLwzgbP <FfǣӁ;6ہm2%2!-*J9>v1WYH , QcwcCӑP`(z}sX?|AHHH`{Bё`p" c@/\.``s rd6zp{k|S1 Hvы`7 2x!/ v[;m Wn*Qހe[Jq V޲EJ'[KjfV6\X@A9\@tt`& ?k_@%aG:wHBބZuVY|=W' EƧ흎U?Ppa#aSQx8T\+KYr#ª lTn-6[{430ua/6'j nAHczAoev-1=X ]@c{Q'hvOl)R7ey%xI绍h -nQT_|߳0R UZ_&G^O!O ughƱ-= /=S H,$YҒg/)ϼT㡒ڎ3@t0}}0VFβH 2N z7=a_?T_VŔhw?445YоtyJfL'5%& %8HHHNȪq3ZӉnҦf;U4'(9ad zf t̗3m`v8o ffv\E+zrII%c"D贏SKJL 3:z"A,YINlH5z1ךv}VOD$׌֚(`5="E#M5JoQmT 87embr52FyuӁ(Q{!Qig#n~1˙"#*ze܇{\ bvE,lMe)KY JamĂ\>=IVɺqb@F]jzmЋv@lgٖf~ǸL aBCK%V-$$nZ\mBUV3!1VG:޶|A/z",S{2mS.P*)nZsh,7Pb(A0bs:AO*-͞=3 /˟f bQ褘sgzmiƜaq-zT-(%: ER E,ơ3[SY_$vᒑ|#y50 k'mkL A} MKΞftd+)JR7R'dZ%D5 X)RU6H_Q*Jd) DB}@@ޚ0@9\"P}?WV;3#LNeH|͖5vRlT$sz!>qR eu4yg,>Įi4"}ÁCO$:7qd*  g9 {{IORO +!XϽڥVt#g\e+R%Z4!pȾV5`ix.M{-BaDH ҍ^D3m3ՕSc_xu@QFVS$c)E Ӎ "%:cu# gR<>R/BU#LZw"x\htg3 ubW8:Z AYP&?V0 q꟞:0!ZǀG `Ta /OGۮaf:My+&@<@)|3fJ x\@gUc"\Ӓqg{z- F_x Ny㣩),\Y')tEŴ$UzSTy%Ar,>WmDáj6w|0)8)Bή6K?MGP3/+Rh)tZw(w7.T) bQa"ŭ@RB,ւXdG0oMoeTITU3*gxw\E]>k.UWcf›c,I{zNjFpd\~NiOSH"5awe"ld=n b_łw{onY%zT9*f* EaT+ɰ,L1XyL'z):A/ tA/L ̚үj*DJ3HZD 2=H1=qex6HʰVt&V@4s>@̼Da Sb1DJZ|q%8RVPZٺjXd4x];@VϞ`uSԤ7pc"Q cxLVf{zUce~DI/ )L}pIM.X)bful.4 _mS !^\1!qV f7@}ܧTI@ELshJ tS .)Nb59U@4q@aAteh9o$83^8)Zܴ'Y,jb,d^ SWLKHSGo`n:|Q2 Y,RĚ)f1Ak 8=lQEȨ 9L/M^|>oߞ|[_tHI/AT]ڣ/+Uv;'BOH15pxrj$4]%!hBbj'v #(oXf,Io4C Z@[mH!1-,, fʅj_A +q°EΰG' [,,l/qy#iģM!^$F/3rVza^*:-S℄$OT^aS/T ug\sғ^NO_ovEǎKR6z"EY3[$c's39A#و^L;M &ͺ":۳4*m:b0hb 0WoTev 5U Tf7VU}J䇞N]YfJ7`4+z߰骚( {+5N&Dk8ڻEBb3Q +. +D Mxb5F%q|¸>C &.g_.m虚` '"L3zMM{w,}~Ƌ)^PGc+=ΘecƶڂFXo bڙo&VW TO:GK:n#EnP'YD +J_\P#NuzvT%FIuo8ǚvU ɢ"&z";1n&ȼ= {-ʸ?:ntմBGau6rhqpq=Y Lӄa&|}9|ޖq˝ƨƭdrǺ/s87*{e?a1 (КdYOf@> -itI!ւCjíGkO#qNqI#k:=j^xY5D/.bM0ы$sH`==I/ah|x&||GNN};ձE(?{١G|;5 x.=ܽx&Kɋ >kWSnLҒfs,Ka㋃OwGOe|^8TOEƆҺʇ[e8HV8hf/\@sǠĹ\^:tjIUT23!|U1t VzCF hike! G5'wŇSjPz%K.] KrЅgprqqecy_hZ_(X 4~H/?9-(2nW<̱#/[ͧ ۻ[pP嵵(4xS@~VXiDMEgyrsoV- 4і.T/߾?֤]?U ؝[?y,z}Eo~~_y _,+;?rk~׿}Voi>[2! /,P}o.ƥ2>뇫JKj$9l\Y>*k;ƧA/,^ Ӌhd]ӏ^OzUtsZMc[mJ1otyzqx}jыuЋ.%s'WVݳ"3x/mj.}O^:N{ ~uU۫_,|s?{oEO2qB!;@N!;qwqT4}gw߽:O,~ayޗ[}~%'&SZ,e9ꯃ,rb.%ʼolX8޸vrԵء 9ûֵ$Fi‚m4 *Fu0!;/ޒZ٬Y *ҫ|@@S;n kzGrT%cF`lD#a3V#>b2/`a7m" XįvHյ{@RL*ʳvjwmkHd"k (5_]r'g’rNoO[{IT)Q1%a-ia ©t cML`DnnA`d @F}ݔ-+am)@?\z!m_FA`ٰ|B Ԕ̄9;|w #uwZKR~- 3Rg[7Sd"</xFT)8 = /@#/޼3GG?]oXZM7xpp`ŨxBEt/^l9pTïfPG ;7pB|9|_V<{D1P}¬}]7™"CeܗvӏVuZ1v糭7~}MΕJ*z1 )' iU/&FԶjb6w'L*}" 14DYXf4,_B@8K+[PSlhlMFOAőIgE5CCPdg6bL?;jB=W!. ӭwf`,lU)tG6ZU-`~#02NXZWi^JȺEsGuN'8THpGe{C{~Y.06 -H/ !PUm̅m[32> RJ>N G2Fn?tqq[y\!Ęb~ҥ>vXRNpjԶS;8 "'pRNWmKZ|ha˜7 e7:Ţ[_>~G^@C 0L$9_ /{/Ћ'D@B@L$*...(((,,7"«]J2 P~漟&tޟGH}sI;_AQOu;_~'}_ N;_^h7=u,Td Q.</4ʷc%/%Z"_P1ҍ6l "![D6\ oz(&Y\~ޔv~\YXXP u/^NrK i¡`#>8O ,v_xfCF>T۳L8B Hҧ6uXxJs^`)+KDYٹii陙Y9yyZPPTTTR\ C,ʕ PxT;*^?Wso4g9Y_>aN2pR5ƯŹfjf7u8%*u0VuAd*rZK`a.N <`~.^]VNrڅ?y.xux%X#װ[4P_z'g ){q ^<}tÂ_vp]k(.BSak=*:t^tܾkEh6.lmև+m |$[Xxyx]qm|hi7\x53%Ty ~P3q԰;!%o8k51Xhw PNM'.>JB7O= /̧얐R]5Ah^[2/4a8Z (wi%j9F(4Q0Q`4pȚV$?\+M~[Ng!V>N 5 BZ&#Ís^XzfU.1؆FfR1cywӚz\MlKWO߼-oi*$||y v_8ݳI% r!Z dW9|㏡)zw̯!J.QDzVD@`=9_́3-՘R;&-##sDˤ&|cNvDY,!V.υ*HJ:g@p<*ǫ)UL[|G¸sgO9%!HXe '$4CJxq,^pBފ8!JJJRh)iiiYY999y2()A!KE"Ll"+eʄ~6p5Ԙm$xYXC'/?-RU|/+P蕘!=|xQa^j4J&a5M4,K SƁaA%+?C48_}2z,R(U|>;V?ï"+ 4lr|Bw:^`aq>B@Xۏ8/+r]yR{‹ggx0*4[SD_{]qu;}7~~*cmњoxP>`uJ6ƓM,3QZ^~'gV[Svd$Hko*/ixLsםeB~qgλ6<$UimkMW$fEotl .5+ q)콇u""DV&fq)|| $ \ 5"3 ~F  ` AvwÞF&ƥ GU^0Ocjȝ|P1\9v57?ֱP`]49N/ʅ(2BbQoUC 0TN)|*c/و ]*MԞ ߀}.r#'}?1ݦ`G !7?&1QM] /&$V5!r hB'CC~~Ux'?~LO>}/[$&&xM7Q*X,l 2a7A 6γ(LUEH`@4Aū6&;9d& '^LiὈBGx߄|; .)aڡvMz1# I+kG ^eNX[b=74؅US#d*Fjx!-p x—\~esxAV!/Z1.laZ̿?PUJV\v3m彂UE)^P&L:LMfuO[h?%mYm'ŏJ%Ko)ƞ/=Q@x%w?8ݷ ͍~$37 ̋~u ]B[-୙1̦ya\F B%sLj^F\K#ZɏaܨsT.sn@! k~OJ&5vVX7:lN6-Emj{`D@*ܳ!iԼZ"FWʀfX_ *Ӈt tfh^~fx ejjh*wLA)Ocf|6D^$ E%,ɝAǯ=,ׁYpHUڢf LUp=[5U咒l"`4ǕR6= Mlnљ"vNlleL;ŔލȈU]zNy0TԻ)A&ꢪ 0MN ԤE W JWBJ?->hԧZF>岜%EA? /~ߔo޼yǏ 1ё1Qq1I Yrsrr KK$"1n, B.€^y?BlA3dռAf2 cQ :pO5UhmPfq^^E sQyWyq/dÉuwYxޜ :@S<:'Ӂ!Wߒ+*͞^5a8ylO%ɡ+>o/J{R2Pi|Tw"] Zf/~+CDI<~,[&'≆-T*V=cSղО>TV R͸)y" h]gO(%Qv+=Ɓ]>`J߳Z'v{O8pƒE!,%dAÈMIȢh#;L?\B__;|>ڣe!ńwbD9S,,0uK-iu>-mͰp #K%1l(ҽ`-9o#s \B cpEcҥ= pUpbqiv++g YYtJؓ oF+ F[px%0滈 0b3, Bb4ޕɽg%F- i,{cc}E8I't_ֵSrOрr/QCܫ#^LG4zvCQ^ q> )vUAD@M$ mtBEג-|x52ȯeJVt&.KkFxjݪ /AlpW<{߿o߾"!>61!EzZJfFZnNVA~na~`"1䎔@ YU v"€ #&a֝:sίy5#E WM(X\CsT)*1kGݒ` *t< LhH<1asԊ*4H-fZ <3 m|~  q,QH$`3zc"bPltLR0?#vlBZiq oƽ@7iݐ9=^{7o BXR\Zv= ;YKX@2TNPcpP+  mShRh#EOK" ~aay5)Ƭa'ro$‹߹);@uxQ9ѭck^`eR.I%W`Mjƅ!^ 7)K{eEhdImXþ{Zhe9:2aOXΥ֢sM,}U{)twt=5`ε(t>E c%RJ^X bw=$\+ٙMB[]nؼ b4:xa$K9##u0rtּAI.uu ]~;۲ms߾0;vL0=; ,*447"&)jaoQxʗgE7:#y123&CG%..ܽG֦2sT21=6Jۧ9ةyl`K#@vvb iF;g?|} '#NH*xvGzG\]\j.= ]AFJyu11ur!Iư簩Ҷ?/~ }Y*[Ͽ1`O䭞ʼn&n[Ϟt q46| 񋯥\Xz3{y`β9-)]*[Pjp2ҴKi#gjh aDg憩>T~~K2+P1;H{p¹rZ/oD[$M.1f?Ӝ饩:R إ01>juĩS|ϝvgFDDD (0`[5'M5+ !^Xj^9/g{By+ca=]L'Vy'-)Qq9x钤o/lel@NLl~bTtN:S8Ek,G|M*<=`R0xJωs%✨#Jn{(~yĻg? pE4 ]4)U͠oh:o}v1]V?m؎BYW[>ޟ#f =<}4$$$44ƳODzͫ޼\෈~{׎ƴkۺV.+:9577sttYvݺ;[ܠā x $"x,f)'Z0LDT5mHd̍ʅGI1y06U/D~Jum>9azÙ(b58ײ4B.;$?fۖCǎB1 S IA!ЃbfA6FO\vm6J*ِ yo=ѥ9˷rK.ժp_0xnU?Z?m3U'n*,p)3l(h^~/UE@SQ%I3:@a(-2*EAbBtTd\qLżUJ]< >B(#/).%!-WPĘ_2SC<hX?<*9*#GrDol ́ $璢 =1+5l=$TIQqvFBBRvLbO>ظ4qa)sN>)ܗ(%)9:&!+'WJ*b#R ^hinfVltLffz:Ы*Y/.)1M&U34\c(hS(R:=- Kb2!J%{An2.ܲ;%1IYE2 rtS%r/j] e Ń=z?}嫷߼ǷZK玕+U4bA Ašcut<$Op]@ `J_q09_6݀-S*WGp:U5~̔cRQПo"Sf?4!zr]̅z$~  z)B*N OWQ+ɲPɓZAtȈ ݳsƂDooC24!sw8}޺|xE:՜.[`= 6Rr EYJQ!W8*=~$̲e$ 9DHi~S)2(g1:x#I1"nQmI tå,RДd[&jVyX ]Vp./7s!Ɵ7j-`ҵ݌zU'NZdoޭ>wc##ŌլUHg*tx‰Ǟ;ͲOf\}t>斊Jb/U=RUWO~VQQ ǤbCeF&|5J<`F7pZkİWR 8r5Ŭ|54FÂoI3:Cneճ!@ t>\p.49Uc+N|jJ z@ h/ O|t>oW8sYXRv?+N?^J u5ˁ nfS !\g(쟁~onyæp\PRn'>AȐP8B27JPi)z;[v8?1ːK'S G rP^'NɆ@2VRfqyvR2*cjR[@nj>2vF93R~{Q+ ΟHRuY/NlvB@Dnٳ52GYsjٹDZ Vqu)jxZ{ދf>sor9.J^,R2JԃO{\ǐկg̝内+6{~ۦl%zy(AB=`&ߑܖM N.ދc9jdžú1qIiY9g[N8ߊ?"uIs8r4m^/FJ,? c\SŠ~UHƑ+oDG)؃2e83gq]le;.ׇÀ,+, Od~|%' ~r?劳I3  jpnA.Ą_R Wu,^ܧۣЇA{x/ <ŭj5 **leecii ҮBJCy5WQf5oݴY+u9U414jҴ32`v8-U@΀W_ #rW뼑ߒ=γ)Q3&:^F8EoU:~Ͽeo^w)̍ )HЮg_}VU4} g5sO\x&|߻uӦ|Kb(AT'/|:eϴs1H3[1ߧ__~$?ۣV owL)dO6dh&MkTsJ riϺ}]M9<^p\">jڌ9Kg =oL9f9~V,(uiXHЁ5kѨqӡc' cb Ef&֮^ݭjaFZ}n$L7 @/<ի+߬'G VFݶ8)aX¿Z&]f-E}1SJU|n^#B>~6-(4kԸz5}>xAS&(RwЫIӖ&L7o) ,#P4?8ĚGV뺶\{)@wN scz_ʨFbm7kj\M}L;{/ }wo_rBh[/>v_lz7D;.}[5!H]:r賹#Og;m{\#7-g/9\B={͛?~Gb|HրL GC->/f8q6^Ɛw8Y[1ϙGrbs$!NEh 4"/>_q+qag! ͊p4edҌkXGCUSЁ!+[/N8%|ՠ4ɧu#HXj6Dx^29{Zw~h=zjg/xJP`q;w 80^ʥuw}@`fnmnaѽF/A/ [qmwڵ[nݽtټE W2eԸt`-ne> t }s)ZQt [8qkҥm?By燶13 : `w9^? %S @]]ڙ^0v2%)O&beXLAݿ%[4R\$#Τ||j&/L3"&\Rn:5th%e-+֤҈-`&f4 %ЦJR2E 6ISuBdGn6%P'An̫;昋gl 1r`"'lQbU+c /̑{[} 9BއQ:h+E_K3ak4Kd~{yO(074-[jGJ? BZa)JSք}EENEc4G\wӽPS<2wS-WwӠ+#nV6ojR{Qt֦4ޔbKRoMUÈfƍmo%&))'/[TZI*  sSoK&Qye&QT!wtags[ 8wrk5nVZj6عc/^{ޓ'Omd_rr2<0 ο4q"V|[_l "]4x .Q|*aS1wI* |5c9a.ѓGǂ5˄4NkL p'pKIbS|BGdl0WxiCV j89`'zj!&AR;¾@1 ^/nܸx]x8xΝڹ, D=feZwHJ΀3gN4գfM`Qتc0{V[89 :$a ز ?!bs/r i!(-hp;qitM(Z*@ 4Ӗch72AE mxl^g`o.r~Գ3^V: } /Q]h7aReKx]~RFRIzy[(22sB|i1dC$E'̷H`]]{gj:Nt'fF>H[ Q>h_ig.&He,$aX7Vk}ˍv:4'ܮ:Ǭ׵%bYbiM6$uJF6p֌g]fVH0 MMUj`ès @애 '/9qٸ1sݒSxsM 1 )|2gɌ>xf.sĥϟ:qC۶m[~6ߴgϾc'ߺu ( "eXSy-<1rVb(:hа,pλΑ?>&6(/[hi"FiHXA%% 8[,қN1| Nǹ pUHuW#H=,ϝ.N!;iP}:kn(UFQ{z̷p.@EV/' ҞjA5q,>{|R*tS4%0f3o~.ڹn>-jlJ%47v_sىܲ*vgSZq jt9KFG@] e&rh qOn_V3_ MJfZΖRB!kљslhAcg2MK`] KC6s,gk5![+! qR;*2!uJxjXk֧ԓn]43ol@~ AG ;`"=K7$o*ӰQF ;Fy<~%4d8։ ( [{.[ʕ+ O|v`ch=#c;m۶}f+YX8XZ`ae[Ү<&ۤg~5rku4jԦivԱ{n'N9|-<=nޜ %Ѐ^ ^ ^00‡IlUx|7o^s'> P . \<b?6W?M$ƃ(lLeU 39|O\_P0_+Q@X*4lb8(@xj{4qutꃠaxb;^a^Y-]ǿچ?g 790KLc8zJi ^Dd]He … ຸv < - A@@4ioQ_>ڪUy3rfm,[딙_TK5btry4SuƝ[4жuc'@kN^nBJohab^u] l\0=$O FSar 30>js(KYf9&C_ lTY=-jݻ6넒Nd.T7k6;JiIFh^2eA5:JUIA!Bv1k8qgݻuӦ&TJ5`Kލ (ID?ZLB'Y]1.RaBj~~m[X_(WT)Ъ%/Z8)2!N FJ*`p0~{SwJxC@!PASaF$++|src6BR0O]`@~ ôs C8FX-:GAXqLM;N즂!4u D6 |JDj_Q+Ȝ%/?Yͨ TVox:i^;Xxs5?>K0tYVmvW)]^:o̧ xET՟{h;π;w+ ^XfP J4|"wz3|cv AA઼qޝ'ת! L7T pd,엯X[90r2p4)oaTA_!ѳGo\poVV Z2"ALFEw.bY^{7EtʍARL16.Vv42I*a0N~VS&Ēϱo)SM,SBR b x\FxU#eG δ%T5SGr6%Bhn\q2UHBI p4)o, .̳4< vzre0Bz\ }Bw5erRƝ2P@Bwd/"l!-Z[/}[]OY@ &6^ݣg}z߫g]8@B٭~pG>Y:/F/Fd{l)٢Wz ;Ժy˂n׸Ȥ贸)qQ?3T2'. b*erQb6ߣy` l 'E*'?0k\!==PPTc 9h84|C_6z%{ 0e6 CPlI*p0"艡pЂ +@ϑA=l@ |Fir8B, â$Q#͖h`4X$|^\X,S[\g1OA6kknGx dWo] >|N롳\4sùduߓm[B ?:rŧ&oyz8Y۔ {mlřu5"ʽ*D4YUB̩4gJ%MUVXH^>SsEiH@4Jb2aUЎ. >k":~Pzk;G ?9};sZr}WEw> A6$r)-PGGcwN9V *Z~=Vq :2.E䊈{Y Ey$UrE$ְA d`(iԊ94/` O>}IH~K~q̩FPTU ٽ@*PՋX|lEo#IbniҴY5kVmܸyV;Lm,AZ},1lڬŇ2PTT ~@80~/"=K|c Ew N" 2=zș7)V_̱cnjt:ͪWGWJi~!_$mx:ic)K&"[IU씘Dbҵ n6nxoϮZ#N2ۨ)a,uY>wj0*irx2#<VJɓ vz"k)Y!Q|xc.Kp Z)u !aBC\ZF㾋A\(~\116_ Pd=bct5< 0qb5]98>OK<72VD.V 9͟vSZPcK\(#;dӠd)C W L*0LCau` ^6uc^ci-?&ZkSGܨ2>3_t䆖}'.۰W )5K:sS/_qjiny٣^pCg5[:SZ˞{3Ha}dU-}`K܇yI@cWeF.'TbE+&oظ^j;394'o h?Ml9 0l!U E^< ҾSgN;gDY&>ucN9Ko+@N:+zfEm8wk̪)@QxkV/ڽ[U.Unf洹a^NDP~-&`wE>kJORjy{ҥ{Zj73zҥ!jJAQ2`$)SfO>mTY>pee*6qI>&O;cɾ7ly#HՍB_܋^Kׯ7] xn޼FXFI`~'ٱ\ņ T{t6VVQ oI hsqT h2RaDH@[NN`g€a dh<غ::ZUء^hͅF @7;(9u e͡нA!XjY·7cc3t] CJjԵb]vy>C rnME$ ݪuZsGl-I̚pDdl/%Wk(rm< P>„rYD[LySWXU_#L֩}3U{JMpYFA,ܸ_f&woe}tů3_a;ȼr=p@Y˷Kغy'цrs56$mJٱ~)+6:ű!#,q9[_?ʢR|5^ M9$f<>o-m`ߒlDԤrTZk\'3ǽ.X%رi)-YXjӉ800bw&=95MFo?xm;}$H֥Q0 >y3'O;nbV}O?w}R W qծT>+ߚqgZm6ĶYjգcUxԣa_r===ln ) 0C@#[P ՘p<U{$ADCF v8<g<y)}sIC%fZU*74% u84p,Dx:xS\\l$q4PG/$\ QZ?%;w,>ҍLc'I3!K\0Ű<]Cj:1q7[s9m:w/ bGhM-pUveRvW3X)b -,mfFB#^١R9yne rk/x ́d{w^d˗/Ch3grn|F3ahJɹ6@h2AA`:#X Sҥh/L̛͖ { s>QçJ,G#r]_ GFD%$d1aW PK M2xdi)IAS"BW*kպ` -6!s\Rr.=5)E2 <\:# V[HiavjfV)tBe"(%YI I)&4BRVRCZ*cyaJ_L2vHUCRBZAN`Rr*P--ƮE An HFVZptE5闗W(AqoaJ+LLMRӎH 6B({,U jayրĺ8`vRH>z\ S%,˜7Kv;ut˺۴[ey>* V2T}6`Ė#n }V ([3g7mԪMÇ9q=~Iē^(|ӶM:z6kIj}|-)75_Œ@leᮣTУÄ㧝t$Ƅou1\ΜRE[C!Khs/_pD5"PXS\V1A,ϒ49.9PpUK]=Aθ;ڹy֭{z4 "_Pʌx/v{\(=H;i/lyD< wf(^ ,{6 x͚ŴW-33@3IԩUtzU\);o9Q䋨f֛69] 6! _ V#f⇒M::qxӽ!Möw"?$M[.w1#l=t& DtIW?0H,)΂6@ $n^^^ AĭKn}&V6ʺAOMj>02"€ċo/\|l@$/r38ez 9*S_faTqA"(`;the[wg|zVë5ާ >mG1m7_hٗ&/x^ovDu.îzpo;zWnB 3FWe{89Fb$`44f7w>mE,43znSQk*eN~9|T s<3oNAÿ;,}#e_`12)U|QNQZ kS94g%l8`mF!ӰXp$4\FX4!'B>B+ҿ8H͇/AA8 *xv/1!VܽR7**=}!5?I;[^C"2~OJSQuc6 *~TتNBzJ8&AV0UZ$8彻5;5 FEKEqf B cuqL ɿ2q(ߤ{y[V\wۊmgi뻥%=}3R`]]`\2p8-{ Z|58-> 7HJJg dQw3g[SNlmSsng~r0dtJ>ZG Cd" L!S8;Tq}K5DKj$VB1ppS*PH8c8 b0~9,Bʸ~rЇ0AnV$z)HgJ!oڳ\<-t%f}ģ)Ǻ%z ̪.~rm6KXyQ 'YԞf߮57jcw<.Ġϵqmo^?| eT\w8|'.^̘1 AfjHC/^xh{ytYuo5Ŋ~ZdPh0y9OjU{e*=l3bo9F"ߎ揠5M,4_;RC~i'Kt۹5sG>uiJe?eӦ?#)gCW{g.^ɳz1Cjԩ^5nyXN@/vYacDlfAgAOc3J,M8&K(T&ղ7!`o[NLxa1ezVܪy@uߩ3K.E#z uLPƎ3kw1D*dL^\Qf uʲiD:7nҗg✕IIwI 7ka!(nQlyP:tCŹ!;Z@IJMВYK}Ǚt?m'Jz+:U˽ԪVX>JW-`~%"?x &u^jР" нB7gg *!Ըk5$(-L41w}vvycH:,&js Mf?q_!k*tfSYthΫYφ:l؂dԫWӧ9ŔT- ]xRW8'9:%>DJOϾcM?rU4YG+T[q)xlo ZRѸCW0!NP*|C.P8JfzZ<PbX\?oc#C  cv;kxQd'xu98^'? *6q æ|&Kl۬kӠ#G^:fW`y8<  :y~瞃 ߮M{as.^<^}Gп kݥ_|jA: ^ߢ\_q{rDžu w<Ʌ2e= ~mb|1on[r3> MYxJ RhPI(LfiAة'~OFIY޳zYN'q UA>31.ܱ 456t As$z{xQW1`oj"Y_R3 8@r8=WZ4}-lg]+H*l -N"nh{!uT /.CG g`'l 7=ai!9[^mmxYP0-fgf$0;i})z>5n ^Eી? 5DCL̟??""RI䃫ѣGK.N!}n/)Lm[{seZka5^t޵&34M7reٰϢ}=u{߾#ٸ_\oؓƍ:%͏@3sw6~M $o=]F?_'v66&)+~Img7OqҸe]CC6mYFWb5vi.4ѻGs#S@E-9 (<`=hw u>gDqמ$D&ۿV>}r>@$f|)Ak 7:ڏʄ\b ז -I / 3&B;O>M@8f'ʵZH_Mǰ⍰$VH֤سhgHJӛHC=ˏd W6wׁb[%7J b'vo ˘ z}ٓ<úJ}<`am3, 5`%4>Яq̨՘N`*պ{c3c?6^uc g|3bz椑|<#JND 60A'w]pd{qy\('̞P0XW7DSF26j_1a BO?hVu(C̫XgUeV9vT@ŁXgxEׯݿgۮݻ{ߙ;t_ڵ7n }:zxH5u?kEjߛzsמ&sqJ.]W׶k7ze*^?o`Wjtz-Mj_E0b pi$qaB[Ȕ71w 7¾H NִELBHGv!"lUvÌS8ex7|6#KTd])B0.9R('q2LV$9kѲy6:kճr">Ӗ$ֱ3nt?AaA4JbX{G Mp9Ыhϼ zjb>WRrMܦ+p|$lbz`-ѫW,J/W^S|`8N o'^]El,/j«G75Юuñ8^04¸Z Iղ"K;s궳bCU>D{=Rwnmݺy}ش}#q-n>V裇O_?w}ρ5跲+૨sՙuf<Rn RJmTi2{;w-9x,.c RkHjdH:` R9O֖Kj)52=a(D{Z"y$!Ż{!jldh`odY+WLޜ:z@FEǶk-"߼ i1o`y.%*FA Lg+У*&C^pRsp48/*raڃEEť&{/ y}Xdq㦓&M:u*O>Wl. B#rU[w ]<3ݶvi}x5je0KZ|Ê - pkc߲SLB2 9tx-(=RhW Hu]GV@)c8Z>RX ?(/*k7| \9 tlr^yq^;Yqp$@x θX_.AxQ! Xʍ 㞿荻X,<ʰ0C* 3shRgi6,/as*=PyБÏ5s`⊳~ǷmzCGO-]Ƽ'ۿo߼uį0evnӭBmi4#+-fET13'෨1v.9qS՛ 9vgSRӕ*X^IΗ>VCl91W)]re奐g ȝ&51u^^>p 06䈨"I^ks|b[cf=iYV25##|}4!!Ӑ'!O-΄VS׎g!6%ɴ\ vH3F=&{TjSsV. aPy8?:m!cRV돭ԮѽP)Jmel {`H񞭀Ap\>B "LuvJݾҸ,{%($j%{U>UL. z>3%W!oe3*=`Jf ް{>`]t-|~վ>qVtVC0_ /JEHL{DF^@ KO~(d!ַ IE?@KCXvdž6\L%'S-k:`i{N^F+.uۦA_mhۭozv [+P;m n y_ [J#S2HԯgxAx ?UoxNH{Ϧ`/N$^y_Z4 L36@1_fXM# \ hS!> >Pa_ & 80_BC7:XñRcyB]WAB^ ;㏬a-xa4Ըp afWѩ6>]p|}s7]wڎ{o߹kמ- 'O~:޽Nx9K+igh=9}Vcjo17\inG۬xl6g SPQIR;GQQLdCL^ud}, t&$ u-MlF O7O$Y/)%P|1} wg!; 3ٿ,v-jBp ^=+0-QtJ:͇Ba=5& ul" (HI0ݵ1yM \')L)~xE1 ٱU0-Mߣ%%n\;8xmR ^TȧM+|pJ>>vQfMDJj3va >P>DNJ f iN`!(UqQid K*h%2 ݡfvO_Gژ8B+^X-#=ؔd #?0Px^JIsIQ bYYdL 98])հyLV4WkHMὂu( ^T;A^(z.Pd ۭPQ`PN#^_QIRM)iUhN1]FQp!' pzÆuPRᅞȒJ>lD>*ԨɣkY4&k +x?-e^mP2C=~jx]ATr@Jj5_}+*iKi8:L5)_; [E26#W7b0qߎF#ϯ㎨y&FqdU|^]x=OΤPaQ!ƕTaZcy_s-oұn+xhA{ܽd㡍[vص EsR3gBacU|nT~Peܽc d 7܆]2\^wtc-{} +p_۲Z T\)J9[R=4ߒ)8ooz/-%SAFPB{&gK r8 eF٨ENӽ9 FKm&co dȢ(IqoM$ N.6iQg3kGw66:V,++eHXǪzr)UJ\E"Z0Zڶr ԪۘBV vINVBjoNzH>-8RM܀!,do=ȜOH`ݙٞ}30Ao=aO`m"jfUc{|=4Q"Ziܭ%ŽKA9PA:  U/0  ѕ$"\B#30 iֲ3‘...puHyAV*M_QC4!p'ڥ<%o㬲uC$"5kU+iR fKSI$B\KB%ɵpHi4 n4H& Z1Ddžn"8[ Fy_:3h#prJ&+hXqȁˣCW|5@%mzjʍ&U.p(pO^+6rmZۼm-kҶr6ﵣjMmxiܵBggN_tի}ɰq3lwi:h_q7*Ui-9JW܆_nC~dR5qǂ4-2irߩ[lVH$I١1YP9Ƞ_x/P7?JLlETL_`h_iϲ&VXM<=, +=R"In1ȽL"L+Vwz0ypyX$u]*C @2&W(#xaR!1CB@1 ϑPj7;Qe'/[ganKEWYV^. 3$QUm#W g '4̓[7"LV })ct% -f:TԵom{eВ[cYJ J.ǎ {Z%P?/*{j@d#308_n?:Y̭]CG1sbĸeGz)"C{y 1Ua70J"hѢٗO rr{N@~ ~*#4aS\K^~3յuN1dYWU(A:9ټcϗLYx'4 %>h"cflwݣl޸M[[Ŗ<^#&}KLx$#ƫc{,gYa-UQiiFpy,_7+PY]ZblKM `cbV%%W\ n6^ w-6v7Æb͗i86t)(?80%000&[ԙ+s*oڽe k5gv/g<~s/\v CH9uF~f5صm#TduBrF,A L וdE)i IY%6 aqP`g(4jp 6t1bj|%9a3 p^.SGcW1MMI_aX Tj*C ,RS<`JJ󀎓+S0LQ橎ώ F%B-7W!D*>Qu tNAv'` 8tTasFʢ!Se[ uaβd;dUE z͜R̘F?4Ch[oش8!Ȁ}{}۔<*@Vrɮ=[*Qs槉;nHЊY mH5=X*B8@:'rE?/d uo}͑$oGd闰^ry ODtӀћ70N4RAaT`TmYf:6e+۰g;wX`)S':ts/\p*yjzOz-վr]:w_&X:TS~CGl۽rӊ !ǁK pA?U#h\0*))Pq@V'eaK렕{0{sx.0*,Jj*&w0`TTR+u?6!h^1'7Q!UM0arx^C+"dȁfPfmw{țf+R,O[hf KبB"0Ae(HeʎQԋPȷ>䂀* o~0u)!* +@>ZpC Tr{?q܅g/^z80@޴[_3l[mٱvǖ.9mm!jume+BCdede( G] ?! gDD+[jO&{؃TѤy[ X[˦zAdU0p!Q074zI>a"g I4%UJ* =J if4~2K¢<>S8*|9^h(n}EUQs*X)>{YTqӣpQ_U#<XCgX5BCeA HB 2HǎyAH99 4Yh,#L LCUPm̷xve6#lQ&BT$+35##-nX rF~^^ NgZ[N(! =bn]ZԪ^^1cF< "-Ϟ9OC{^ݵaÆK+]+.ւsgtޣZ͖ ޶McpSx]+Acm>HW}&ig1#t޺~}LTl?^UhyHz1|$k؊y3:tѰQ!'?|]s;-0Дкg>ky3{vkވ[6{yΜ3gAl6-kڬAvflVO;j%6fl9oïUNuz {nD)88޸~o{UínfVlF ˓U~{vhYݭQL {CDFKfN;h@rZ쳗{jQafSf$e)Y jII#{<@Eg&Eҹ3GaXg.U5if]9%4q-G kjҤy&^0 ,زkWHH:c Pĵ']nӾ vlݲgӦ]@ۺegx敫n܀ipQaV}YMW5YkQcUQ֕{նC߹sܺ?:%wL%nFcɗ/!ъ0*E9,Iynbtc<\*poKx`l)}\*Mn3ݩ/9]{(JƒC`QqH΋}#'{CIg8?%KĦ;ϲpT3*<ϓo,NJ =_ /~ H}" Ϊia^ l,G rR4{(3T Y44QfbLM.(1;RH^>fX';Aw<…WBz& EB49c2Vn>"tȰ@N5㟚m'[`ᢴU$kr#^d%u^ ׯ!'^5ɺiQֵB@ļR;Ujum ;s6{P.U-8xfO "gиk_p,J.#? 7^O[aռ+ϒYȇ<Ɗ!W,ʨ"ϝWil}2#WwbWjw4pP]vD<0>tP_^t[rr2P.^ @_ D<-[*T)$Ʀ0.A6!CqVQz3IFcif#yfʌ򴦢:mXQJyb 0?U2vdܜ|h #:HQ\9$n ԸwPnivC%հڑXްeÇwZ7ՐϔUv}- Tz.9YH`;,/:o)XLf୳gF&dǶ/&v[[>hRJxzcgG]8ߌzpZ:Em=9v?{k4x~ed<|WN\?2r@O%;.2d*"4 XؽvFIQJ$"~`BP(oi/n߹M`U[&xDy=9 [> iݰܩ_E4oNJ90cF$ g {n8y0YQe|ƈG!Ftu[*3m0ΐN#I'6MlmڼQݶMqBK" g@3^8Tj~w2DƯ/XW1)1l~cAӰ87pF k/]{m6nݶ}5z,_'OxϫW߼yx!\hcUӰBo =vrӾ] >|wŅ"LY&Z 6;ќsx悇)8 iO24OWXEE@zYlDwC.1U2{M4y_ ؿyэs9@9 o(0<<ǫxU xsG9ip@FL&s0{GЇ6l\2A8녰0y=&O,&X8.&X m`DOA+ŊI"w8`E\\B&`‚ԬLge"Xg*Sɥc0jiFBv'-dۆqi"dF[ϖU }'%߃4+N"Uhįor"퇻 ,<*WhNwj$#T?{=ꍺɵ@Ԥr|tZ}B S,+g.هU)J:kP(ǀVpfza G}sva% (Y ӭN WQ |Y1u"5DZ 2(4#/4 qVb:<ȬZiJuIR*gcF#(RvlZ,jgJɄza`\9.CCEq̬oޏU+'9)=Ͱ+Wm:G_ٓD_C (~3hytfLC7z3g"q[Ol:+ŦW3`GGvgؑCAće<" _/~ >2ׯ1>ųgn;vhܸ1Q GMN*__Zl'p8 l,f[JuBBx~FXceTXHNJpA^KR1qRNRB邒7A| BUa\=Kfy wi };YU`7}Jڦ*ۓ$j`GWpf{&JriDGE&G^WR]$aU[zJ)3SSCĤ-2J-S4ѽ'zEJMK#{y3~_1}: ymB3BgڪĨ-GKKs1o(8kP^5H'ҏbN7uk>ՆY8iHC(TIKWmسn nܶas'΂8{ $^zͻAvr)t۴C>.8o޾[xJ 3}M2_I?*: (0( U7kў"^0J׽ia&3ݡR1E%1)7<[H~5m:̼PˊM2to^٫toڹv [7ۼvek׮lرc@8ArH]v[7n޺w7-;N6s….{wo=0}x~Wq/zxX@p$n/޼ysNкڵm`爅B T;W<s _ڵkM"--EjrJ:MMNNJNfldT?G~_fI=`2E7@N6-5w4tQ˕EVF@'%iʸ-dF+Q%ֶ US$3##kk)p 7` "ۜ}I=S[g;08M)jTcK:4mҲ5EW\Itk*4= {͡!+R\9U+Q m Z Q m3bHD`h@"T5\9?8XXAQ)]]Ի)Ip8Z@}\FF-4Q!)d>svb$ I>H^X@ȴ0гR(৔%Zim~,KDUp:[.u-HTW_Dya7T;5KJ w)SӲ_|e+qm2}Ma.Ì+wo8f̄V_aM[o ˖wرG3pWo3{wr?yӗo?}+(SЈZH!T;"/{]]DzoICOj i$!!@H-{yg1rdvvvnf3ϳm@.G&1I%! BVj2X;UfU$$!o Qq呬Aj/5 .oi.j1 íwPLDodȨMWfɧPźEfʪb&]N'6E-x2gir k]IA€*v `l.6\t!ßx.p֑S S[4jsw۫偼h|U^L( }‹onN4s&-n26MߵϤ/f_SO>!SO9n(1b3gB#qvrR)Lhk_-[ղcYnk7K_7m6eΕk/ZxӖSs].l saI"Zm)md>A8ݦ"/\S8ogJxI>V>9!`~J1#4cfmTLKf$NkO*R6rg2WX #a۬}tFxQ+X_^x5x@x‹C&N;[HyKQdx uĽ_OX@IBB;w ZF+/N%M7 gWCC駟߿\B<t <'''OpmKJT=SйrFÕԍWb|_Pku]>bK+Ϲ1OL-0 ?}Υ L.irۓ` 8IܕS71IF>42H:bot>2!! YiR oi_YUr$Fo lSk"*>xF^ x׬!NDQ6Wu|RN~ꃦ Q1#w^Zu]@<ʑ*n&xCCUe30pS.O݊@GR4Sp2!x]U1OO<>xnfޒJܰ ^a 7g`d)I? @- YMl=U G~Hw?/q \I 7&~.qȸc&U1yyIu;OE%`c^-:>ew}3owT:vbȸ'M:mѳf́gl\R VK2w^ h%+VNEY;H׭Ojچ͘om5,J߼ `*\b$'!kx&#rIBUN)*{&qگ)U{*uMj^A SX~>yA*Z/"E3OQSa@1DIt奔ƚ&#5s|Dn =yQ +6?pJFCMiR=1+ ы[J[*?2S jg#x|r04uuY$/+/Gv$~6PGkhq):"ȕ=w' GjE(:6Ebw_;QV@{ fȽ>s^v?}A&9k)cllG={i?!sа!SQhFd3gϛxx1V]t9UcD^xvi@X|K7o1[xj5lhcb WL#3I6UT BZ3o!lTM!b~nB!f,M$:9q''d, %$\&4@ ZU30f6ϊ #VUR/,X27۬o*fn=ʤD:UUqIMS oCȑ<&L\)9DI.I3EְɑISW޲! 1ߤG{Ŝmis.dx!vx^ȡ :@ ` ` dI{E]>'?!0e&F|u&C8n*U08qi!PUlʸ6ІP?` c^6htժ>cEYy./BU5ʓp&~O'X"H4g~RsRnkiW#rB[ŇOwg0Ҍ^#~dFxZj?%sjo-.qm_VդpWǧ5-iC{2ߦ}#+zwV{y\ ոUv6JͯÑq*25c*d@O_r (5sa tF);ƟރXrEZ;Ox| [g˭7_G kf^ڠ-sNT8tQpxDM;oƗtlElF@'[!Zű./=!E-+[ϙ5e-mAH吥peng { \nP8ށll BF2W=zOmzX)G64*)(".g6E黑V~njDF(xV_yU  jSE 5mϿӳ'+*Gsksf| y'b [[w}{=|dpf׼50a:qhC?5XEL$GMe vN^\\q(rb$ nAshXhW]kQDj܎,bb%r HH_hAr`T;S.3- ˘=qD)!M Y)S#cbgMNZؔ!d¥/G*d x[*SJOTrs^G6>sp j,PGIc WZ#$UTRESxfJbT m80w5Uzk7F5i^nj8% jdKR.ra彄u4Êrtl*hM$'C4oق -RCf¦52C K&JEX*n~.Es4~^TzE!IE/{A"q{-mօ/6Z,Ꝑ(c {]/$ț߿w@ d ?$74e켡N!gU,jHӥ2PdbY ; YSy SM$3n(T z$tT(cۡ;8$t4r|d7j 8a g+Buд]QOAI&l2G{z$G hXi3򿤙Q6puH*aSFaV>-nd0C[R/^uE/<%Ő>=;x.aS;z8K%@Ȉ4;"2_A.VBsg`0\%x+cbfΜ;FIIɹH,L>3xY>Gu}*er"'=]NYm) 7zKoڵ <{/<-sВ/|ڱCo]>)|Tvx]g9n͉w5q{{q| {H[j|ӻM{#m\umw4dd5&V[Lz4c_gh{y.syp>[no߮mw׽w݇\K7@Mı8LC"êM5."a" c&Hا@!:.°LuHR* <^#* U2Zu&%Dd EtʑkO,馄49R8m!Moc #4T-mu.g ̪Qqc‹ c=!AQ%,jWݘvX\7I&RͷK:7ɩtj=ϋp?EyZ\8Y+SWa^dO00-PEH4ʰ?eϾg+0/VKɲa6|?ux9sڐ"&*5]m7/ 9xY[a)o<Қ`2b5fO)[xmcW1A^?H,4 YL3 .;SzH94ėĚ>g.ڬ][yAr|78Ħ@Q٧ؑ[txt%U:<Γ{-}cz.oK켢>b\}U꣇IsW:(Zњ9?bs0Մi2#J[0x!4 $P@N!'s#v6ؙ `#çrcB5)DNJotrQ;/x1[ I†bWJ"x?A<~VEEXб@})Ls[x|*<dzX&\}BĬj%-hb҅RMQcM(+%t1U{ĨlJ,_+ZI:zR27  YPMG`t3+NJ)Zw.4t Q$@Mw d'77JH-Y=GBcLJ&Οr ^DN\̉8mş;:# 'O6|$"&NF@ȞSE0 OP>gbL4FGNҷo_znݺ9rܹw _,& ^4}GʳfJ%x :(0l6$FU8>UJjޝ}17)gAդ . ؚ9WEepRjkoޅ5d#;Jl]P x6ѵ_oN{[Z1Ƞ6Y 9Q4xb{M"gV8W+ `bHd1_?(xϕLB@cQVGFE[` ԥKG}DGfp`oxrpMMx&vUc]Pf 00uOz2s ():^hbX㗵Ƅ?}. ~5?|zsqp +0}bܗP}տ8zygA<Ŭv:C@G*;FKw E\,YCEEow/Cj~F#ٰ}<'*ۡ/|Tk/§JqTYGjLxJi^RiDUx„ϑ^BOy+থ1xlf;+ϴn 2H+Ṉ{Qn>V?7T=*ՕmmPvӃ]< H_bwFЩIѰpa $G/f&:~ %%sx9Gpc 3d[ $~k,@d8D8:z׳OWH_H>Ĝ@bdSbu͢L\-1[BJZ͵|@&14' MDCw[=I PaU%Z 1 /V+֜Z)f[8l)3SW$ /d,BRDCG@D E4dpt}ǻ!3Pfϟ6/ګW'p9H"(&9TUAjyN4; 3ңc8kx͏3'0>>SV]wn#lf]aJ ^+Δ!ƧN.\43{[fm{u|AS*Z@Oʺ]tЅ`QK;.`H #ZW8Yx`zH_qiܞ*6#pQڏƬؿ_NDov|MRe͋Zv wY1 @#j~|N8s~- p/F'{$]E$a ;ٻi>׹ /sƃ/*E=8{?ңWTwNOV#+p;! M6JaCXծ1#+,X``ыz2TذHK0joCƦ.o^s% fI.ԫ/ܙQ#m6sk$Uv}FB+I: ? VQJbJ 7 *5⸓pO¦hw}< umtLM tmC\xf A)Cis}RCXJpd|ND'sҗO ;/6kشd5@x:_VgBgZ"7+io~ȋ^|/sDؔ/Y ؼ>vujBҼؗtgxaXŅ>I`B` B\W҇f? bpLu0x%tV=CdeDc /'?iw"EפRASE_L JlUM$@2INћ/#y@,*`yRJapy"ؤ 3RRW$^lBIbzBxq^:I$ (~@9}x…h̘1C„hŊ`WUXX<)+0;~ { OיSa%9'b8/{E!9xզa!!3&^bY FO2-qAB2tnNy 1 qe\RxceE`WeOPT^Htz%q>T4I RfHq,&?p Vx9|Bt[#V@͒jTrTC,-{}XY4׳H"&a *qIcgj ,gj Hj¬ /C*!!5/DI۲. z7'xaxWAߞųcQaBEMMؚ6nCA8ش6I1&Gfjx@F I۹D/ٷФ0,V[7D-Cσ#MSvMSx5v)fk^y o@Ðez^1831zW/yQPuѲ+լLcEjq';`o PƕDѷ:lN)QQL>rjgҝ N)R*F_7c xQϯmf݅j*@;]״1N}30 8dL 1Gwx;70=rB[㺞) 4` /zau1IFA8}@X5e9e4re:ocrࡶ-~gDz"rue?з* 8)eQ JF$E.Z4mÚxv oߚGls9)CBFLaxpNjGnz-Oլ.EFz]Z6@rdE.taĝd9'~}/zG޾uGl+O$*ãZʬlxTauĩmK:rY{x܋qtЉUƪԊoIYMQ!hYkP zt {ܶ} #8U`ʟQ5GuqvA)v~-[Z4E|}aq:Av*e 5Ș~Qũ{CwdA}l<Wа1evzUnE?Z^y^Cm8vB4݆քNKܫ0T/n羓C&DN_,f:&^'̶rv'^R%= m/%|lz|RDi! aN]8t~-E9?׫ptW]1$\ OM [tƵ['$x3-a+bSƆ9)3Hc>SR%A`i{˽yh}\J{"I9HWU<5nEېH?2wOxFaRETsUuxK՚B2 tGDqT7T֋NbKU' w$Z_Ǫ$k+HsE5ytdN`VѦ )^N7=.l-Թ4?!U]{pDD4TFNE\D e(HVÿhR8QJ͒F?D ;}M=wATʨ/vޚ6 qDK8.,xͺ[7ĦF̝?%bТNȐ՜9]}~e\:qh)vbwz5HO sY1Kۚ@PA2M9<-0*y{Et'M7sBA<FqEc:,STOꌀAtE@'\a^(fD[ʾкVd\޹Uo&-W:/Z6_{gȲo{ " DHp,.cmwFѐUI<#{ثdw5Ezf{=cՕ!!  hŀr/F0X as͍lc 1vwWY}Gr,I֬_W}1bZ gBIV(%"wcw^Bt<3,[}c .t$Qd1I8V b,8:Onu{&k٢TO÷?@,Rq*H% T9eTnM5.YE_~ }*ȕܣؾFMC/twb{0 IK 3~zu/IwBj29ԇΝVF{ߟK#̂nkrmn^X>:jD-8f4\[KvݤC3p,䯱Lbdc3ۜ|=՟*i4MIQ%XA?=Uvh4&М ԫf(zƙ5~iҁ_ڣEGE h[kj5[De;"f[8TvpH=5zV˓*|LSԜHDeKn!GD]EҪ#P| hJI7619[÷aXA'A 7"-%&sz`m{nj5wuXG=]@!>1?d;if> kjSJM`.κg(`s Fkf/_c5NJ/yghwa4֚HI[d6kpCABԳ&anzĨ#^hrkpswja1ZݺviܕC{extX>оpG>GLmrU5]Lvo_(IYBTcCTUOh/^jw R8.jU (5)SRWͿ.qVAx5{1FS/?ROv܋{< -+IXL{s Է:]whgQe2-iF10:|ASgt:]e~[{]RcunZtZEŇ7mrhYH| B&m^r5UA!NvXʪpP_37NePo3g|$j\V)eS k]<$)S+I-J~rpC W,֟{]ȩㅤPV^(GFiQ9)lVxke g6X-zGڸS}{ghq>yR)su @ 5VԦ$x 4a%ۥ13jWZO$T$pL>kҠ4D]s_)Pr}yRQ=Y>[ ggd44w #A|g}'IH!{{M.E(x|2Q.9JcVXOyۺ{tm-:F/kt`f?.Q@kY^6e2S2taVx|4XЁӫ-8EXv) mtozg{&oDG =Yag,Z_\As8dE1`^C]_1%->)^I`5/N}F/&g=aq2 >nGdko߫)=> ɀȧ" 6h\ Rğ,BEY2F/ltn|}&ȓEQE(go/ρAչ^[(׋{AP`T,I{/ -%g)qD9265^l,lL+Cclv( s[C/18^̈́V3%xa者{q/kB-_fs0X_[Y9ӹ01?yhTV#Sf]ey3u`J \[nLzcX濝C~ˉMvXJDDh_?hPyk X.Vx*FRij).~' 6 ܊W{ov-̺gP$ v!uP_tRPL-w8 1'rCsqPJYI01xf)bԍS03g=HIg>MҹTrTN\.$ (ƭb'ݛFJd{wPM&xΆt* @փ" k^{/^9RXWi)D,N5k֬ 2L)$oIh֡)E-Bk݄|jMD2pj; @\p+C5 "=E" D6)\u:Q{!gyRgN {/aᩝ 0 ð{0 0^0 0 0 0 0 ð{0 0 r/w_x$܋줸0 0WٛX!;~'x5T.Ç˷sy\&3.7ba*Q +8|{{q|B}{Q:>z/bB69y;3u+3?#e͚5k֬Y_M$' I%Go.-Z䃇7P.w/Jj)18Go߻gVR|j2֬Yf͚ĭY3 Lrw~^{Q>yUs/^|c!ZZ\V'֖&W'Yf͚5kWFeትJ46ˬ/>{owi  \9W_w~G/Kaa _}6 gϞKuJ݋"GŃ}a͚5k֬Y_ Ak988*KGýPPh f͚5k֬&9y(K|ar ?{=+',,,,,,,mGw/),,,,,,a?{Q%^\uaaaaaa@BYÏ^^Yaaaaaaa)܋+ K%JZau/.s/XXXXXXXX뫸/gHIENDB`openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/automake.mk000066400000000000000000000214541514270232600237360ustar00rootroot00000000000000DOC_SOURCE = \ Documentation/group-selection-method-property.txt \ Documentation/_static/logo.png \ Documentation/_static/overview.png \ Documentation/conf.py \ Documentation/index.rst \ Documentation/contents.rst \ Documentation/intro/index.rst \ Documentation/intro/what-is-ovs.rst \ Documentation/intro/why-ovs.rst \ Documentation/intro/install/index.rst \ Documentation/intro/install/bash-completion.rst \ Documentation/intro/install/afxdp.rst \ Documentation/intro/install/debian.rst \ Documentation/intro/install/documentation.rst \ Documentation/intro/install/distributions.rst \ Documentation/intro/install/dpdk.rst \ Documentation/intro/install/fedora.rst \ Documentation/intro/install/general.rst \ Documentation/intro/install/netbsd.rst \ Documentation/intro/install/rhel.rst \ Documentation/intro/install/userspace.rst \ Documentation/intro/install/windows.rst \ Documentation/tutorials/index.rst \ Documentation/tutorials/faucet.rst \ Documentation/tutorials/ovs-advanced.rst \ Documentation/tutorials/ovs-conntrack.rst \ Documentation/tutorials/ipsec.rst \ Documentation/topics/index.rst \ Documentation/topics/bonding.rst \ Documentation/topics/idl-compound-indexes.rst \ Documentation/topics/datapath.rst \ Documentation/topics/design.rst \ Documentation/topics/dpdk/index.rst \ Documentation/topics/dpdk/bridge.rst \ Documentation/topics/dpdk/jumbo-frames.rst \ Documentation/topics/dpdk/memory.rst \ Documentation/topics/dpdk/phy.rst \ Documentation/topics/dpdk/pmd.rst \ Documentation/topics/dpdk/qos.rst \ Documentation/topics/dpdk/vdev.rst \ Documentation/topics/dpdk/vhost-user.rst \ Documentation/topics/fuzzing/index.rst \ Documentation/topics/fuzzing/what-is-fuzzing.rst \ Documentation/topics/fuzzing/ovs-fuzzing-infrastructure.rst \ Documentation/topics/fuzzing/ovs-fuzzers.rst \ Documentation/topics/fuzzing/security-analysis-of-ovs-fuzzers.rst \ Documentation/topics/flow-visualization.rst \ Documentation/topics/integration.rst \ Documentation/topics/language-bindings.rst \ Documentation/topics/networking-namespaces.rst \ Documentation/topics/openflow.rst \ Documentation/topics/ovs-extensions.rst \ Documentation/topics/ovsdb-relay.rst \ Documentation/topics/ovsdb-replication.rst \ Documentation/topics/porting.rst \ Documentation/topics/record-replay.rst \ Documentation/topics/testing.rst \ Documentation/topics/tracing.rst \ Documentation/topics/usdt-probes.rst \ Documentation/topics/userspace-checksum-offloading.rst \ Documentation/topics/userspace-tso.rst \ Documentation/topics/userspace-tx-steering.rst \ Documentation/topics/windows.rst \ Documentation/howto/index.rst \ Documentation/howto/dpdk.rst \ Documentation/howto/ipsec.rst \ Documentation/howto/kvm.rst \ Documentation/howto/libvirt.rst \ Documentation/howto/selinux.rst \ Documentation/howto/ssl.rst \ Documentation/howto/qos.png \ Documentation/howto/qos.rst \ Documentation/howto/sflow.png \ Documentation/howto/sflow.rst \ Documentation/howto/tunneling.png \ Documentation/howto/tunneling.rst \ Documentation/howto/userspace-tunneling.rst \ Documentation/howto/vlan.png \ Documentation/howto/vlan.rst \ Documentation/howto/vtep.rst \ Documentation/howto/tc-offload.rst \ Documentation/ref/index.rst \ Documentation/faq/index.rst \ Documentation/faq/configuration.rst \ Documentation/faq/contributing.rst \ Documentation/faq/design.rst \ Documentation/faq/general.rst \ Documentation/faq/issues.rst \ Documentation/faq/openflow.rst \ Documentation/faq/qos.rst \ Documentation/faq/releases.rst \ Documentation/faq/terminology.rst \ Documentation/faq/vlan.rst \ Documentation/faq/vxlan.rst \ Documentation/faq/bareudp.rst \ Documentation/internals/index.rst \ Documentation/internals/authors.rst \ Documentation/internals/bugs.rst \ Documentation/internals/charter.rst \ Documentation/internals/committer-emeritus-status.rst \ Documentation/internals/committer-grant-revocation.rst \ Documentation/internals/committer-responsibilities.rst \ Documentation/internals/documentation.rst \ Documentation/internals/mailing-lists.rst \ Documentation/internals/maintainers.rst \ Documentation/internals/patchwork.rst \ Documentation/internals/release-process.rst \ Documentation/internals/security.rst \ Documentation/internals/contributing/index.rst \ Documentation/internals/contributing/backporting-patches.rst \ Documentation/internals/contributing/inclusive-language.rst \ Documentation/internals/contributing/coding-style.rst \ Documentation/internals/contributing/coding-style-windows.rst \ Documentation/internals/contributing/documentation-style.rst \ Documentation/internals/contributing/libopenvswitch-abi.rst \ Documentation/internals/contributing/submitting-patches.rst \ Documentation/requirements.txt \ $(addprefix Documentation/ref/,$(RST_MANPAGES) $(RST_MANPAGES_NOINST)) FLAKE8_PYFILES += Documentation/conf.py EXTRA_DIST += $(DOC_SOURCE) # You can set these variables from the command line. SPHINXOPTS = SPHINXSRCDIR = $(srcdir)/Documentation SPHINXBUILDDIR = $(builddir)/Documentation/_build # Internal variables. ALLSPHINXOPTS = -W -n -d $(SPHINXBUILDDIR)/doctrees $(SPHINXOPTS) $(SPHINXSRCDIR) sphinx_verbose = $(sphinx_verbose_@AM_V@) sphinx_verbose_ = $(sphinx_verbose_@AM_DEFAULT_V@) sphinx_verbose_0 = -q if HAVE_SPHINX docs-check: $(DOC_SOURCE) $(AM_V_GEN)$(SPHINXBUILD) $(sphinx_verbose) -b html $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/html && touch $@ $(AM_V_GEN)$(SPHINXBUILD) $(sphinx_verbose) -b man $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/man && touch $@ ALL_LOCAL += docs-check CLEANFILES += docs-check check-docs: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/linkcheck clean-docs: rm -rf $(SPHINXBUILDDIR) rm -f docs-check CLEAN_LOCAL += clean-docs endif .PHONY: check-docs .PHONY: clean-docs # Installing manpages based on rST. # # The docs-check target converts the rST files listed in RST_MANPAGES # into nroff manpages in Documentation/_build/man. The easiest way to # get these installed by "make install" is to write our own helper # rules. # rST formatted manpages under Documentation/ref. RST_MANPAGES = \ ovs-actions.7.rst \ ovs-appctl.8.rst \ ovs-ctl.8.rst \ ovs-flowviz.8.rst \ ovs-l3ping.8.rst \ ovs-parse-backtrace.8.rst \ ovs-pki.8.rst \ ovs-tcpdump.8.rst \ ovs-tcpundump.1.rst \ ovs-test.8.rst \ ovs-vlan-test.8.rst \ ovsdb-server.7.rst \ ovsdb.5.rst \ ovsdb.7.rst # rST formatted manpages that we don't want to install because they # document stuff that only works with a build tree, not with an # installed OVS. RST_MANPAGES_NOINST = ovs-sim.1.rst # The GNU standards say that these variables should control # installation directories for manpages in each section. Automake # will define them for us only if it sees that a manpage in the # appropriate section is to be installed through its built-in feature. # Since we're working independently, for best safety, we need to # define them ourselves. man1dir = $(mandir)/man1 man2dir = $(mandir)/man2 man3dir = $(mandir)/man3 man4dir = $(mandir)/man4 man5dir = $(mandir)/man5 man6dir = $(mandir)/man6 man7dir = $(mandir)/man7 man8dir = $(mandir)/man8 man9dir = $(mandir)/man9 # Set a shell variable for each manpage directory. set_mandirs = \ man1dir='$(man1dir)' \ man2dir='$(man2dir)' \ man3dir='$(man3dir)' \ man4dir='$(man4dir)' \ man5dir='$(man5dir)' \ man6dir='$(man6dir)' \ man7dir='$(man7dir)' \ man8dir='$(man8dir)' \ man9dir='$(man9dir)' # Given an $rst of "ovs-vlan-test.8.rst", sets $stem to # "ovs-vlan-test", $section to "8", and $mandir to $man8dir. extract_stem_and_section = \ stem=`echo "$$rst" | sed -n 's/^\(.*\)\.\([0-9]\).rst$$/\1/p'`; \ section=`echo "$$rst" | sed -n 's/^\(.*\)\.\([0-9]\).rst$$/\2/p'`; \ test -n "$$section" || { echo "$$rst: cannot infer manpage section from filename" 2>&1; continue; }; \ eval "mandir=\$$man$${section}dir"; \ test -n "$$mandir" || { echo "unknown directory for manpage section $$section"; continue; } INSTALL_DATA_LOCAL += install-man-rst if HAVE_SPHINX install-man-rst: docs-check @$(set_mandirs); \ for rst in $(RST_MANPAGES) $(EXTRA_RST_MANPAGES); do \ $(extract_stem_and_section); \ echo " $(MKDIR_P) '$(DESTDIR)'\"$$mandir\""; \ $(MKDIR_P) '$(DESTDIR)'"$$mandir"; \ if test -f $(SPHINXBUILDDIR)/man/$$stem.$$section; then \ filepath=$(SPHINXBUILDDIR)/man/$$stem.$$section; \ else \ filepath=$(SPHINXBUILDDIR)/man/$$section/$$stem.$$section; \ fi; \ echo " $(INSTALL_DATA) $$filepath '$(DESTDIR)'\"$$mandir/$$stem.$$section\""; \ $(INSTALL_DATA) $$filepath '$(DESTDIR)'"$$mandir/$$stem.$$section"; \ done else install-man-rst: @: endif UNINSTALL_LOCAL += uninstall-man-rst uninstall-man-rst: @$(set_mandirs); \ for rst in $(RST_MANPAGES); do \ $(extract_stem_and_section); \ echo "rm -f '$(DESTDIR)'\"$$mandir/$$stem.$$section\""; \ rm -f '$(DESTDIR)'"$$mandir/$$stem.$$section"; \ done openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/conf.py000066400000000000000000000126431514270232600230760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Open vSwitch documentation build configuration file, created by # sphinx-quickstart on Fri Sep 30 09:57:36 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import string import sys try: import ovs_sphinx_theme use_ovs_theme = True except ImportError: print("Cannot find 'ovs-sphinx-theme' package. " "Falling back to default theme.") use_ovs_theme = False # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.1' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'contents' # General information about the project. project = u'Open vSwitch' copyright = u'2016-2024, The Open vSwitch Development Community' author = u'The Open vSwitch Development Community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The full version, including alpha/beta/rc tags. release = None filename = "../configure.ac" with open(filename, 'r') as f: for line in f: if 'AC_INIT' in line: # Parse "AC_INIT(openvswitch, 2.7.90, bugs@openvswitch.org)": release = line.split(',')[1].strip(string.whitespace + '[]') break if release is None: sys.stderr.write('%s: failed to determine Open vSwitch version\n' % filename) sys.exit(1) # The short X.Y version. # # However, it's important to know the difference between, e.g., 2.7 # and 2.7.90, which can be very different versions (2.7.90 may be much # closer to 2.8 than to 2.7), so check for that. version = release if '.90' in release else '.'.join(release.split('.')[0:2]) # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # If true, check the validity of #anchors in links. linkcheck_anchors = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # if use_ovs_theme: html_theme = 'ovs' # Add any paths that contain custom themes here, relative to this directory. if use_ovs_theme: html_theme_path = [ovs_sphinx_theme.get_theme_dir()] else: html_theme_path = [] # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = '_static/logo.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Define the canonical URL for our domain configured on Read the Docs. html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") option_emphasise_placeholders = True # Tell Jinja2 templates the build is running on Read the Docs. html_context = {} if os.environ.get("READTHEDOCS", "") == "True": html_context["READTHEDOCS"] = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). _man_pages = [ ('ovs-actions.7', u'OpenFlow actions and instructions with Open vSwitch extensions'), ('ovs-appctl.8', u'utility for configuring running Open vSwitch daemons'), ('ovs-ctl.8', u'OVS startup helper script'), ('ovs-flowviz.8', u'utility for visualizing OpenFlow and datapath flows'), ('ovs-l3ping.8', u'check network deployment for L3 tunneling problems'), ('ovs-parse-backtrace.8', u'parses ovs-appctl backtrace output'), ('ovs-pki.8', u'OpenFlow public key infrastructure management utility'), ('ovs-sim.1', u'Open vSwitch simulator environment'), ('ovs-tcpdump.8', u'Dump traffic from an Open vSwitch port using tcpdump'), ('ovs-tcpundump.1', u'convert "tcpdump -xx" output to hex strings'), ('ovs-test.8', u'Check Linux drivers for performance, vlan and L3 tunneling problems'), ('ovs-vlan-test.8', u'Check Linux drivers for problems with vlan traffic'), ('ovsdb-server.7', u'Open vSwitch Database Server Protocol'), ('ovsdb.5', u'Open vSwitch Database (File Formats)'), ('ovsdb.7', u'Open vSwitch Database (Overview)'), ] # Generate list of (path, name, description, [author, ...], section) man_pages = [ ('ref/%s' % file_name, file_name.split('.', 1)[0], description, [author], file_name.split('.', 1)[1]) for file_name, description in _man_pages] openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/contents.rst000066400000000000000000000022531514270232600241620ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================================== Open vSwitch Documentation Contents =================================== .. toctree:: :maxdepth: 3 index .. toctree:: :maxdepth: 3 intro/index tutorials/index howto/index topics/index ref/index internals/index intro/install/documentation faq/index openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/000077500000000000000000000000001514270232600223405ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/bareudp.rst000066400000000000000000000064601514270232600245220ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======= Bareudp ======= Q: What is Bareudp? A: There are various L3 encapsulation standards using UDP being discussed to leverage the UDP based load balancing capability of different networks. MPLSoUDP (__ https://tools.ietf.org/html/rfc7510) is one among them. The Bareudp tunnel provides a generic L3 encapsulation support for tunnelling different L3 protocols like MPLS, IP, NSH etc. inside a UDP tunnel. An example to create bareudp device to tunnel MPLS unicast traffic is given below.:: $ ovs-vsctl add-port br0 mpls_udp_port -- set interface udp_port \ type=bareudp options:remote_ip=2.1.1.3 options:local_ip=2.1.1.2 \ options:payload_type=0x8847 options:dst_port=6635 The option payload_type specifies the ethertype of the l3 protocol which the bareudp device will be tunnelling. The bareudp device supports special handling for MPLS & IP as they can have multiple ethertypes. MPLS procotcol can have ethertypes ETH_P_MPLS_UC (unicast) & ETH_P_MPLS_MC (multicast). IP protocol can have ethertypes ETH_P_IP (v4) & ETH_P_IPV6 (v6). The bareudp device to tunnel L3 traffic with multiple ethertypes (MPLS & IP) can be created by passing the L3 protocol name as string in the field payload_type. An example to create bareudp device to tunnel MPLS unicast & multicast traffic is given below.:: $ ovs-vsctl add-port br0 mpls_udp_port -- set interface udp_port \ type=bareudp options:remote_ip=2.1.1.3 options:local_ip=2.1.1.2 \ options:payload_type=mpls options:dst_port=6635 The below example ovs rule shows how a bareudp tunnel port is used to tunnel an MPLS packet inside a UDP tunnel.:: $ ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=10,dl_type=0x0800,\ actions=push_mpls:0x8847,set_field:3->mpls_label,\ output:mpls_udp_port" This rule does MPLS encapsulation on IP packets and sends the l3 MPLS packets on a bareudp tunnel port which has its payload_type configured to 0x8847. An example to create bareudp device to tunnel IPv4 & IPv6 traffic is given below.:: $ ovs-vsctl add-port br0 ip_udp_port -- set interface udp_port \ type=bareudp options:remote_ip=2.1.1.3 options:local_ip=2.1.1.2 \ options:payload_type=ip options:dst_port=6636 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/configuration.rst000066400000000000000000000316051514270232600257460ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================== Basic Configuration =================== Q: How do I configure a port as an access port? A. Add ``tag=VLAN`` to your ``ovs-vsctl add-port`` command. For example, the following commands configure br0 with eth0 as a trunk port (the default) and tap0 as an access port for VLAN 9: :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 tag=9 If you want to configure an already added port as an access port, use ``ovs-vsctl set``, e.g.: :: $ ovs-vsctl set port tap0 tag=9 Q: How do I configure a port as a SPAN port, that is, enable mirroring of all traffic to that port? A. The following commands configure br0 with eth0 and tap0 as trunk ports. All traffic coming in or going out on eth0 or tap0 is also mirrored to tap1; any traffic arriving on tap1 is dropped: :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 $ ovs-vsctl add-port br0 tap1 \ -- --id=@p get port tap1 \ -- --id=@m create mirror name=m0 select-all=true output-port=@p \ -- set bridge br0 mirrors=@m To later disable mirroring, run: :: $ ovs-vsctl clear bridge br0 mirrors Q: Does Open vSwitch support configuring a port in promiscuous mode? A: Yes. How you configure it depends on what you mean by "promiscuous mode": - Conventionally, "promiscuous mode" is a feature of a network interface card. Ordinarily, a NIC passes to the CPU only the packets actually destined to its host machine. It discards the rest to avoid wasting memory and CPU cycles. When promiscuous mode is enabled, however, it passes every packet to the CPU. On an old-style shared-media or hub-based network, this allows the host to spy on all packets on the network. But in the switched networks that are almost everywhere these days, promiscuous mode doesn't have much effect, because few packets not destined to a host are delivered to the host's NIC. This form of promiscuous mode is configured in the guest OS of the VMs on your bridge, e.g. with "ip link set promisc". - The VMware vSwitch uses a different definition of "promiscuous mode". When you configure promiscuous mode on a VMware vNIC, the vSwitch sends a copy of every packet received by the vSwitch to that vNIC. That has a much bigger effect than just enabling promiscuous mode in a guest OS. Rather than getting a few stray packets for which the switch does not yet know the correct destination, the vNIC gets every packet. The effect is similar to replacing the vSwitch by a virtual hub. This "promiscuous mode" is what switches normally call "port mirroring" or "SPAN". For information on how to configure SPAN, see "How do I configure a port as a SPAN port, that is, enable mirroring of all traffic to that port?" Q: How do I configure a DPDK port as an access port? A: Firstly, you must have a DPDK-enabled version of Open vSwitch. If your version is DPDK-enabled it may support the dpdk_version and dpdk_initialized keys in the configuration database. Earlier versions of Open vSwitch only supported the other-config:dpdk-init key in the configuration in the database. All versions will display lines with "EAL:..." during startup when other_config:dpdk-init is set to 'true'. Secondly, when adding a DPDK port, unlike a system port, the type for the interface and valid dpdk-devargs must be specified. For example:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 myportname -- set Interface myportname \ type=dpdk options:dpdk-devargs=0000:06:00.0 Refer to :doc:`/intro/install/dpdk` for more information on enabling and using DPDK with Open vSwitch. Q: How do I configure a VLAN as an RSPAN VLAN, that is, enable mirroring of all traffic to that VLAN? A: The following commands configure br0 with eth0 as a trunk port and tap0 as an access port for VLAN 10. All traffic coming in or going out on tap0, as well as traffic coming in or going out on eth0 in VLAN 10, is also mirrored to VLAN 15 on eth0. The original tag for VLAN 10, in cases where one is present, is dropped as part of mirroring: :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 tag=10 $ ovs-vsctl \ -- --id=@m create mirror name=m0 select-all=true select-vlan=10 \ output-vlan=15 \ -- set bridge br0 mirrors=@m To later disable mirroring, run: :: $ ovs-vsctl clear bridge br0 mirrors Mirroring to a VLAN can disrupt a network that contains unmanaged switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred. Q: Can I mirror more than one input VLAN to an RSPAN VLAN? A: Yes, but mirroring to a VLAN strips the original VLAN tag in favor of the specified output-vlan. This loss of information may make the mirrored traffic too hard to interpret. To mirror multiple VLANs, use the commands above, but specify a comma-separated list of VLANs as the value for select-vlan. To mirror every VLAN, use the commands above, but omit select-vlan and its value entirely. When a packet arrives on a VLAN that is used as a mirror output VLAN, the mirror is disregarded. Instead, in standalone mode, OVS floods the packet across all the ports for which the mirror output VLAN is configured. (If an OpenFlow controller is in use, then it can override this behavior through the flow table.) If OVS is used as an intermediate switch, rather than an edge switch, this ensures that the RSPAN traffic is distributed through the network. Mirroring to a VLAN can disrupt a network that contains unmanaged switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred. Q: How do I configure mirroring of all traffic to a GRE tunnel? A: The following commands configure br0 with eth0 and tap0 as trunk ports. All traffic coming in or going out on eth0 or tap0 is also mirrored to gre0, a GRE tunnel to the remote host 192.168.1.10; any traffic arriving on gre0 is dropped:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 $ ovs-vsctl add-port br0 gre0 \ -- set interface gre0 type=gre options:remote_ip=192.168.1.10 \ -- --id=@p get port gre0 \ -- --id=@m create mirror name=m0 select-all=true output-port=@p \ -- set bridge br0 mirrors=@m To later disable mirroring and destroy the GRE tunnel:: $ ovs-vsctl clear bridge br0 mirrors $ ovs-vsctl del-port br0 gre0 Q: Does Open vSwitch support ERSPAN? A: Yes. ERSPAN version I and version II over IPv4 GRE and IPv6 GRE tunnel are supported. See ovs-fields(7) for matching and setting ERSPAN fields. :: $ ovs-vsctl add-br br0 $ #For ERSPAN type 2 (version I) $ ovs-vsctl add-port br0 at_erspan0 -- \ set int at_erspan0 type=erspan options:key=1 \ options:remote_ip=172.31.1.1 \ options:erspan_ver=1 options:erspan_idx=1 $ #For ERSPAN type 3 (version II) $ ovs-vsctl add-port br0 at_erspan0 -- \ set int at_erspan0 type=erspan options:key=1 \ options:remote_ip=172.31.1.1 \ options:erspan_ver=2 options:erspan_dir=1 \ options:erspan_hwid=4 Q: Does Open vSwitch support IPv6 GRE? A: Yes. L2 tunnel interface GRE over IPv6 is supported. L3 GRE tunnel over IPv6 is not supported. :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 at_gre0 -- \ set int at_gre0 type=ip6gre \ options:remote_ip=fc00:100::1 \ options:packet_type=legacy_l2 Q: Does Open vSwitch support GTP-U? A: Yes. Starting with version 2.13, the Open vSwitch userspace datapath supports GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U)). TEID is set by using tunnel key field. :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 gtpu0 -- \ set int gtpu0 type=gtpu options:key= \ options:remote_ip=172.31.1.1 Q: Does Open vSwitch support SRv6? A: Yes. Starting with version 3.2, the Open vSwitch userspace datapath supports SRv6 (Segment Routing over IPv6). The following example shows tunneling to fc00:300::1 via fc00:100::1 and fc00:200::1. In the current implementation, if "IPv6 in IPv6" or "IPv4 in IPv6" packets are routed to this interface, and these packets are not SRv6 packets, they may be dropped, so be careful in workloads with a mix of these tunnels. Also note the following restrictions: * Segment list length is limited to 6. * SRv6 packets with other than segments_left = 0 are simply dropped. :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 srv6_0 -- \ set int srv6_0 type=srv6 \ options:remote_ip=fc00:100::1 \ options:srv6_segs="fc00:100::1,fc00:200::1,fc00:300::1" Q: How do I connect two bridges? A: First, why do you want to do this? Two connected bridges are not much different from a single bridge, so you might as well just have a single bridge with all your ports on it. If you still want to connect two bridges, you can use a pair of patch ports. The following example creates bridges br0 and br1, adds eth0 and tap0 to br0, adds tap1 to br1, and then connects br0 and br1 with a pair of patch ports. :: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 $ ovs-vsctl add-br br1 $ ovs-vsctl add-port br1 tap1 $ ovs-vsctl \ -- add-port br0 patch0 \ -- set interface patch0 type=patch options:peer=patch1 \ -- add-port br1 patch1 \ -- set interface patch1 type=patch options:peer=patch0 Bridges connected with patch ports are much like a single bridge. For instance, if the example above also added eth1 to br1, and both eth0 and eth1 happened to be connected to the same next-hop switch, then you could loop your network just as you would if you added eth0 and eth1 to the same bridge (see the "Configuration Problems" section below for more information). Q: How do I configure a bridge without an OpenFlow local port? (Local port in the sense of OFPP_LOCAL) A: Open vSwitch does not support such a configuration. Bridges always have their local ports. Q: Why does OVS pick its default datapath ID the way it does? A: The default OpenFlow datapath ID for a bridge is the minimum non-local MAC address among all of the ports in a bridge. This means that a bridge with a given set of physical ports will always have the same datapath ID. This is useful for virtualization systems, which typically put a single physical port (or a single bond of multiple ports) on a given bridge alongside the virtual ports for running VMs. In such a setup, the IP address for the NIC associated with a physical port gets migrated from the physical NIC to the bridge port. The bridge port should have the same MAC address as the physical NIC, so that the host doesn't suddenly start using a different MAC, and taking the minimum MAC address does this automatically and, if there is bond, consistently. Virtual ports for running VMs do not affect the situation because these normally have the "local" bit set, which OVS ignores. If you want a stable MAC and datapath ID, you could set your own MAC by ``hwaddr`` in ``other_config`` of bridge. :: ovs-vsctl set bridge br-int other_config:hwaddr=3a:4d:a7:05:2a:45 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/contributing.rst000066400000000000000000000224531514270232600256070ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========== Development =========== Q: How do I implement a new OpenFlow message? A: Add your new message to ``enum ofpraw`` and ``enum ofptype`` in ``include/openvswitch/ofp-msgs.h``, following the existing pattern. Then recompile and fix all of the new warnings, implementing new functionality for the new message as needed. (If you configure with ``--enable-Werror``, as described in :doc:`/intro/install/general`, then it is impossible to miss any warnings.) To add an OpenFlow vendor extension message (aka experimenter message) for a vendor that doesn't yet have any extension messages, you will also need to edit ``build-aux/extract-ofp-msgs`` and at least ``ofphdrs_decode()`` and ``ofpraw_put__()`` in ``lib/ofp-msgs.c``. OpenFlow doesn't standardize vendor extensions very well, so it's hard to make the process simpler than that. (If you have a choice of how to design your vendor extension messages, it will be easier if you make them resemble the ONF and OVS extension messages.) Q: How do I add support for a new field or header? A: Add new members for your field to ``struct flow`` in ``include/openvswitch/flow.h``, and add new enumerations for your new field to ``enum mf_field_id`` in ``include/openvswitch/meta-flow.h``, following the existing pattern. If the field uses a new OXM class, add it to OXM_CLASSES in ``build-aux/extract-ofp-fields``. Also, add support to ``miniflow_extract()`` in ``lib/flow.c`` for extracting your new field from a packet into struct miniflow, and to ``nx_put_raw()`` in ``lib/nx-match.c`` to output your new field in OXM matches. Then recompile and fix all of the new warnings, implementing new functionality for the new field or header as needed. (If you configure with ``--enable-Werror``, as described in :doc:`/intro/install/general`, then it is impossible to miss any warnings.) If you want kernel datapath support for your new field, you also need to modify the kernel module for the operating systems you are interested in. This isn't mandatory, since fields understood only by userspace work too (with a performance penalty), so it's reasonable to start development without it. If you implement kernel module support for Linux, then the Linux kernel "netdev" mailing list is the place to submit that support first; please read up on the Linux kernel development process separately. The Windows datapath kernel module support, on the other hand, is maintained within the OVS tree, so patches for that can go directly to ovs-dev. Q: How do I add support for a new OpenFlow action? A: Add your new action to ``enum ofp_raw_action_type`` in ``lib/ofp-actions.c``, following the existing pattern. Then recompile and fix all of the new warnings, implementing new functionality for the new action as needed. (If you configure with ``--enable-Werror``, as described in the :doc:`/intro/install/general`, then it is impossible to miss any warnings.) If you need to add an OpenFlow vendor extension action for a vendor that doesn't yet have any extension actions, then you will also need to add the vendor to ``vendor_map`` in ``build-aux/extract-ofp-actions``. Also, you will need to add support for the vendor to ``ofpact_decode_raw()`` and ``ofpact_put_raw()`` in ``lib/ofp-actions.c``. (If you have a choice of how to design your vendor extension actions, it will be easier if you make them resemble the ONF and OVS extension actions.) Q: How do I add support for a new OpenFlow error message? A: Add your new error to ``enum ofperr`` in ``include/openvswitch/ofp-errors.h``. Read the large comment at the top of the file for details. If you need to add an OpenFlow vendor extension error for a vendor that doesn't yet have any, first add the vendor ID to the ``_VENDOR_ID`` list in ``include/openflow/openflow-common.h``. Q: What's a Signed-off-by and how do I provide one? A: Free and open source software projects usually require a contributor to provide some assurance that they're entitled to contribute the code that they provide. Some projects, for example, do this with a Contributor License Agreement (CLA) or a copyright assignment that is signed on paper or electronically. For this purpose, Open vSwitch has adopted something called the Developer's Certificate of Origin (DCO), which is also used by the Linux kernel and originated there. Informally stated, agreeing to the DCO is the developer's way of attesting that a particular commit that they are contributing is one that they are allowed to contribute. You should visit https://developercertificate.org/ to read the full statement of the DCO, which is less than 200 words long. To certify compliance with the Developer's Certificate of Origin for a particular commit, just add the following line to the end of your commit message, properly substituting your name and email address: Signed-off-by: Firstname Lastname Git has special support for adding a Signed-off-by line to a commit message: when you run "git commit", just add the -s option, as in "git commit -s". If you use the "git citool" GUI for commits, you can add a Signed-off-by line to the commit message by pressing Control+S. Other Git user interfaces may provide similar support. Q: How do I apply patches from email? A: You can use ``git am`` on raw email contents, either from a file saved by or piped from an email client. In ``mutt``, for example, when you are viewing a patch, you can apply it to the tree in ~/ovs by issuing the command ``|cd ~/ovs && git am``. If you are an OVS committer, you might want to add ``-s`` to sign off on the patch as part of applying it. If you do this often, then you can make the keystrokes ``,a`` shorthand for it by adding the following line to your ``.muttrc``: macro index,pager ,a "cd ~/ovs && git am -s" "apply patch" ``git am`` has a problem with some email messages from the ovs-dev list for which the mailing list manager edits the From: address, replacing it by the list's own address. The mailing list manager must do this for messages whose sender's email domain has DMARC configured, because receivers will otherwise discard these messages when they do not come directly from the sender's email domain. This editing makes the patches look like they come from the mailing list instead of the author. To work around this problem, one can use the following wrapper script for ``git am``:: #! /bin/sh tmp=$(mktemp) cat >$tmp if grep '^From:.*via dev.*' "$tmp" >/dev/null 2>&1; then sed '/^From:.*via dev.*/d s/^[Rr]eply-[tT]o:/From:/' $tmp else cat "$tmp" fi | git am "$@" rm "$tmp" Another way to apply emailed patches is to use the ``pwclient`` program, which can obtain patches from patchwork and apply them directly. Download ``pwclient`` at https://patchwork.ozlabs.org/project/openvswitch/. You probably want to set up a ``.pwclientrc`` that looks something like this:: [options] default=openvswitch signoff=true [openvswitch] url=https://patchwork.ozlabs.org/xmlrpc/ After you install ``pwclient``, you can apply a patch from patchwork with ``pwclient git-am #``, where # is the patch's number. (This fails with certain patches that contain form-feeds, due to a limitation of the protocol underlying ``pwclient``.) Another way to apply patches directly from patchwork which supports applying patch series is to use the ``git-pw`` program. It can be obtained with ``pip install git-pw``. Alternative installation instructions and general documentation can be found at https://patchwork.readthedocs.io/projects/git-pw/en/latest/. You need to use your openvswitch patchwork login or create one at https://patchwork.ozlabs.org/register/. The following can then be set on the command line with ``git config`` or through a ``.gitconfig`` like this:: [pw] server=https://patchwork.ozlabs.org/api/1.0 project=openvswitch username= password= Patch series can be listed with ``git-pw series list`` and applied with ``git-pw series apply #``, where # is the series number. Individual patches can be applied with ``git-pw patch apply #``, where # is the patch number. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/design.rst000066400000000000000000000157541514270232600243570ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================== Implementation Details ====================== Q: I hear OVS has a couple of kinds of flows. Can you tell me about them? A: Open vSwitch uses different kinds of flows for different purposes: - OpenFlow flows are the most important kind of flow. OpenFlow controllers use these flows to define a switch's policy. OpenFlow flows support wildcards, priorities, and multiple tables. When in-band control is in use, Open vSwitch sets up a few "hidden" flows, with priority higher than a controller or the user can configure, that are not visible via OpenFlow. (See the "Controller" section of the FAQ for more information about hidden flows.) - The Open vSwitch software switch implementation uses a second kind of flow internally. These flows, called "datapath" or "kernel" flows, do not support priorities and comprise only a single table, which makes them suitable for caching. (Like OpenFlow flows, datapath flows do support wildcarding, in Open vSwitch 1.11 and later.) OpenFlow flows and datapath flows also support different actions and number ports differently. Datapath flows are an implementation detail that is subject to change in future versions of Open vSwitch. Even with the current version of Open vSwitch, hardware switch implementations do not necessarily use this architecture. Users and controllers directly control only the OpenFlow flow table. Open vSwitch manages the datapath flow table itself, so users should not normally be concerned with it. Q: Why are there so many different ways to dump flows? A: Open vSwitch has two kinds of flows (see the previous question), so it has commands with different purposes for dumping each kind of flow: - ``ovs-ofctl dump-flows
`` dumps OpenFlow flows, excluding hidden flows. This is the most commonly useful form of flow dump. (Unlike the other commands, this should work with any OpenFlow switch, not just Open vSwitch.) - ``ovs-appctl bridge/dump-flows
`` dumps OpenFlow flows, including hidden flows. This is occasionally useful for troubleshooting suspected issues with in-band control. - ``ovs-dpctl dump-flows [dp]`` dumps the datapath flow table entries for a Linux kernel-based datapath. In Open vSwitch 1.10 and later, ovs-vswitchd merges multiple switches into a single datapath, so it will show all the flows on all your kernel-based switches. This command can occasionally be useful for debugging. It doesn't dump flows that was offloaded to hardware. - ``ovs-appctl dpif/dump-flows
``, new in Open vSwitch 1.10, dumps datapath flows for only the specified bridge, regardless of the type. Supports dumping of HW offloaded flows. See ovs-vswitchd(8) for details. Q: How does multicast snooping works with VLANs? A: Open vSwitch maintains snooping tables for each VLAN. Q: Can OVS populate the kernel flow table in advance instead of in reaction to packets? A: No. There are several reasons: - Kernel flows are not as sophisticated as OpenFlow flows, which means that some OpenFlow policies could require a large number of kernel flows. The "conjunctive match" feature is an extreme example: the number of kernel flows it requires is the product of the number of flows in each dimension. - With multiple OpenFlow flow tables and simple sets of actions, the number of kernel flows required can be as large as the product of the number of flows in each dimension. With more sophisticated actions, the number of kernel flows could be even larger. - Open vSwitch is designed so that any version of OVS userspace interoperates with any version of the OVS kernel module. This forward and backward compatibility requires that userspace observe how the kernel module parses received packets. This is only possible in a straightforward way when userspace adds kernel flows in reaction to received packets. For more relevant information on the architecture of Open vSwitch, please read "The Design and Implementation of Open vSwitch", published in USENIX NSDI 2015. Q: How many packets does OVS buffer? A: Open vSwitch fast path packet processing uses a "run to completion" model in which every packet is completely handled in a single pass. Therefore, in the common case where a packet just passes through the fast path, Open vSwitch does not buffer packets itself. The operating system and the network drivers involved in receiving and later in transmitting the packet do often include buffering. Open vSwitch is only a middleman between these and does not have direct access or influence over their buffers. Outside the common case, Open vSwitch does sometimes buffer packets. When the OVS fast path processes a packet that does not match any of the flows in its megaflow cache, it passes that packet to the Open vSwitch slow path. This procedure queues a copy of the packet to the Open vSwitch userspace which processes it and, if necessary, passes it back to the kernel module. Queuing the packet to userspace as part of this process involves buffering. (Going the opposite direction does not, because the kernel actually processes the request synchronously.) A few other exceptional cases also queue packets to userspace for processing; most of these are due to OpenFlow actions that the fast path cannot handle and that must therefore be handled by the slow path instead. OpenFlow also has a concept of packet buffering. When an OpenFlow switch sends a packet to a controller, it may opt to retain a copy of the packet in an OpenFlow "packet buffer". Later, if the controller wants to tell the switch to forward a copy of that packet, it can refer to the packet through its assigned buffer, instead of sending the whole packet back to the switch, thereby saving bandwidth in the OpenFlow control channel. Before Open vSwitch 2.7, OVS implemented such buffering; Open vSwitch 2.7 and later do not. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/general.rst000066400000000000000000000144001514270232600245060ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======= General ======= Q: What is Open vSwitch? A: Open vSwitch is a production quality open source software switch designed to be used as a vswitch in virtualized server environments. A vswitch forwards traffic between different VMs on the same physical host and also forwards traffic between VMs and the physical network. Open vSwitch supports standard management interfaces (e.g. sFlow, NetFlow, IPFIX, RSPAN, CLI), and is open to programmatic extension and control using OpenFlow and the OVSDB management protocol. Open vSwitch as designed to be compatible with modern switching chipsets. This means that it can be ported to existing high-fanout switches allowing the same flexible control of the physical infrastructure as the virtual infrastructure. It also means that Open vSwitch will be able to take advantage of on-NIC switching chipsets as their functionality matures. Q: What virtualization platforms can use Open vSwitch? A: Open vSwitch can currently run on any Linux-based virtualization platform (kernel 3.10 and newer), including: KVM and VirtualBox. As of Linux 3.3 it is part of the mainline kernel. The bulk of the code is written in platform- independent C and is easily ported to other environments. We welcome inquires about integrating Open vSwitch with other virtualization platforms. Q: How can I try Open vSwitch? A: The Open vSwitch source code can be built on a Linux system. You can build and experiment with Open vSwitch on any Linux machine. Packages for various Linux distributions are available on many platforms, including: Debian, Ubuntu, Fedora. You may also download and run a virtualization platform that already has Open vSwitch integrated. Be aware that the version integrated with a particular platform may not be the most recent Open vSwitch release. Q: Does Open vSwitch only work on Linux? A: No, Open vSwitch has been ported to a number of different operating systems and hardware platforms. Most of the development work occurs on Linux, but the code should be portable to any POSIX system. We've seen Open vSwitch ported to a number of different platforms, including FreeBSD, Windows, and even non-POSIX embedded systems. By definition, the Open vSwitch Linux kernel module only works on Linux and will provide the highest performance. However, a userspace datapath is available that should be very portable. Q: What's involved with porting Open vSwitch to a new platform or switching ASIC? A: :doc:`/topics/porting` describes how one would go about porting Open vSwitch to a new operating system or hardware platform. Q: Why would I use Open vSwitch instead of the Linux bridge? A: Open vSwitch is specially designed to make it easier to manage VM network configuration and monitor state spread across many physical hosts in dynamic virtualized environments. Refer to :doc:`/intro/why-ovs` for a more detailed description of how Open vSwitch relates to the Linux Bridge. Q: How is Open vSwitch related to distributed virtual switches like the VMware vNetwork distributed switch or the Cisco Nexus 1000V? A: Distributed vswitch applications (e.g., VMware vNetwork distributed switch, Cisco Nexus 1000V) provide a centralized way to configure and monitor the network state of VMs that are spread across many physical hosts. Open vSwitch is not a distributed vswitch itself, rather it runs on each physical host and supports remote management in a way that makes it easier for developers of virtualization/cloud management platforms to offer distributed vswitch capabilities. To aid in distribution, Open vSwitch provides two open protocols that are specially designed for remote management in virtualized network environments: OpenFlow, which exposes flow-based forwarding state, and the OVSDB management protocol, which exposes switch port state. In addition to the switch implementation itself, Open vSwitch includes tools (ovs-ofctl, ovs-vsctl) that developers can script and extend to provide distributed vswitch capabilities that are closely integrated with their virtualization management platform. Q: Why doesn't Open vSwitch support distribution? A: Open vSwitch is intended to be a useful component for building flexible network infrastructure. There are many different approaches to distribution which balance trade-offs between simplicity, scalability, hardware compatibility, convergence times, logical forwarding model, etc. The goal of Open vSwitch is to be able to support all as a primitive building block rather than choose a particular point in the distributed design space. Q: How can I contribute to the Open vSwitch Community? A: You can start by joining the mailing lists and helping to answer questions. You can also suggest improvements to documentation. If you have a feature or bug you would like to work on, send a mail to one of the :doc:`mailing lists `. Q: Why can I no longer connect to my OpenFlow controller or OVSDB manager? A: Starting in OVS 2.4, we switched the default ports to the IANA-specified port numbers for OpenFlow (6633->6653) and OVSDB (6632->6640). We recommend using these port numbers, but if you cannot, all the programs allow overriding the default port. See the appropriate man page. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/index.rst000066400000000000000000000021401514270232600241760ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. === FAQ === .. toctree:: :maxdepth: 2 bareudp configuration contributing design general issues openflow qos releases terminology vlan vxlan openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/issues.rst000066400000000000000000000515631514270232600244170ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========================== Common Configuration Issues =========================== Q: I created a bridge and added my Ethernet port to it, using commands like these:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 and as soon as I ran the "add-port" command I lost all connectivity through eth0. Help! A: A physical Ethernet device that is part of an Open vSwitch bridge should not have an IP address. If one does, then that IP address will not be fully functional. You can restore functionality by moving the IP address to an Open vSwitch "internal" device, such as the network device named after the bridge itself. For example, assuming that eth0's IP address is 192.168.128.5, you could run the commands below to fix up the situation:: $ ip addr flush dev eth0 $ ip addr add 192.168.128.5/24 dev br0 $ ip link set br0 up (If your only connection to the machine running OVS is through the IP address in question, then you would want to run all of these commands on a single command line, or put them into a script.) If there were any additional routes assigned to eth0, then you would also want to use commands to adjust these routes to go through br0. If you use DHCP to obtain an IP address, then you should kill the DHCP client that was listening on the physical Ethernet interface (e.g. eth0) and start one listening on the internal interface (e.g. br0). You might still need to manually clear the IP address from the physical interface (e.g. with "ip addr flush dev eth0"). There is no compelling reason why Open vSwitch must work this way. However, this is the way that the Linux kernel bridge module has always worked, so it's a model that those accustomed to Linux bridging are already used to. Also, the model that most people expect is not implementable without kernel changes on all the versions of Linux that Open vSwitch supports. By the way, this issue is not specific to physical Ethernet devices. It applies to all network devices except Open vSwitch "internal" devices. Q: I created a bridge and added a couple of Ethernet ports to it, using commands like these:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 eth1 and now my network seems to have melted: connectivity is unreliable (even connectivity that doesn't go through Open vSwitch), all the LEDs on my physical switches are blinking, wireshark shows duplicated packets, and CPU usage is very high. A: More than likely, you've looped your network. Probably, eth0 and eth1 are connected to the same physical Ethernet switch. This yields a scenario where OVS receives a broadcast packet on eth0 and sends it out on eth1, then the physical switch connected to eth1 sends the packet back on eth0, and so on forever. More complicated scenarios, involving a loop through multiple switches, are possible too. The solution depends on what you are trying to do: - If you added eth0 and eth1 to get higher bandwidth or higher reliability between OVS and your physical Ethernet switch, use a bond. The following commands create br0 and then add eth0 and eth1 as a bond:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-bond br0 bond0 eth0 eth1 Bonds have tons of configuration options. Please read the documentation on the Port table in ovs-vswitchd.conf.db(5) for all the details. Configuration for DPDK-enabled interfaces is slightly less straightforward. Refer to :doc:`/intro/install/dpdk` for more information. - Perhaps you don't actually need eth0 and eth1 to be on the same bridge. For example, if you simply want to be able to connect each of them to virtual machines, then you can put each of them on a bridge of its own:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-br br1 $ ovs-vsctl add-port br1 eth1 and then connect VMs to br0 and br1. (A potential disadvantage is that traffic cannot directly pass between br0 and br1. Instead, it will go out eth0 and come back in eth1, or vice versa.) - If you have a redundant or complex network topology and you want to prevent loops, turn on spanning tree protocol (STP). The following commands create br0, enable STP, and add eth0 and eth1 to the bridge. The order is important because you don't want have to have a loop in your network even transiently:: $ ovs-vsctl add-br br0 $ ovs-vsctl set bridge br0 stp_enable=true $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 eth1 The Open vSwitch implementation of STP is not well tested. Report any bugs you observe, but if you'd rather avoid acting as a beta tester then another option might be your best shot. Q: I can't seem to use Open vSwitch in a wireless network. A: Wireless base stations generally only allow packets with the source MAC address of NIC that completed the initial handshake. Therefore, without MAC rewriting, only a single device can communicate over a single wireless link. This isn't specific to Open vSwitch, it's enforced by the access point, so the same problems will show up with the Linux bridge or any other way to do bridging. Q: I can't seem to add my PPP interface to an Open vSwitch bridge. A: PPP most commonly carries IP packets, but Open vSwitch works only with Ethernet frames. The correct way to interface PPP to an Ethernet network is usually to use routing instead of switching. Q: Is there any documentation on the database tables and fields? A: Yes. ovs-vswitchd.conf.db(5) is a comprehensive reference. Q: When I run ovs-dpctl I no longer see the bridges I created. Instead, I only see a datapath called "ovs-system". How can I see datapath information about a particular bridge? A: In version 1.9.0, OVS switched to using a single datapath that is shared by all bridges of that type. The ``ovs-appctl dpif/*`` commands provide similar functionality that is scoped by the bridge. Q: I created a GRE port using ovs-vsctl so why can't I send traffic or see the port in the datapath? A: On Linux kernels before 3.11, the OVS GRE module and Linux GRE module cannot be loaded at the same time. It is likely that on your system the Linux GRE module is already loaded and blocking OVS (to confirm, check dmesg for errors regarding GRE registration). To fix this, unload all GRE modules that appear in lsmod as well as the OVS kernel module. You can then reload the OVS module following the directions in :doc:`/intro/install/general` , which will ensure that dependencies are satisfied. Q: Open vSwitch does not seem to obey my packet filter rules. A: It depends on mechanisms and configurations you want to use. You cannot usefully use typical packet filters, like iptables, on physical Ethernet ports that you add to an Open vSwitch bridge. This is because Open vSwitch captures packets from the interface at a layer lower below where typical packet-filter implementations install their hooks. (This actually applies to any interface of type "system" that you might add to an Open vSwitch bridge.) You can usefully use typical packet filters on Open vSwitch internal ports as they are mostly ordinary interfaces from the point of view of packet filters. For example, suppose you create a bridge br0 and add Ethernet port eth0 to it. Then you can usefully add iptables rules to affect the internal interface br0, but not the physical interface eth0. (br0 is also where you would add an IP address, as discussed elsewhere in the FAQ.) For simple filtering rules, it might be possible to achieve similar results by installing appropriate OpenFlow flows instead. The OVS conntrack feature (see the "ct" action in ovs-actions(7)) can implement a stateful firewall. If the use of a particular packet filter setup is essential, Open vSwitch might not be the best choice for you. On Linux, you might want to consider using the Linux Bridge. (This is the only choice if you want to use ebtables rules.) On NetBSD, you might want to consider using the bridge(4) with BRIDGE_IPF option. Q: It seems that Open vSwitch does nothing when I removed a port and then immediately put it back. For example, consider that p1 is a port of ``type=internal``:: $ ovs-vsctl del-port br0 p1 -- \ add-port br0 p1 -- \ set interface p1 type=internal Any other type of port gets the same effect. A: It's an expected behaviour. If del-port and add-port happen in a single OVSDB transaction as your example, Open vSwitch always "skips" the intermediate steps. Even if they are done in multiple transactions, it's still allowed for Open vSwitch to skip the intermediate steps and just implement the overall effect. In both cases, your example would be turned into a no-op. If you want to make Open vSwitch actually destroy and then re-create the port for some side effects like resetting kernel setting for the corresponding interface, you need to separate operations into multiple OVSDB transactions and ensure that at least the first one does not have ``--no-wait``. In the following example, the first ovs-vsctl will block until Open vSwitch reloads the new configuration and removes the port:: $ ovs-vsctl del-port br0 p1 $ ovs-vsctl add-port br0 p1 -- \ set interface p1 type=internal Q: I want to add thousands of ports to an Open vSwitch bridge, but it takes too long (minutes or hours) to do it with ovs-vsctl. How can I do it faster? A: If you add them one at a time with ovs-vsctl, it can take a long time to add thousands of ports to an Open vSwitch bridge. This is because every invocation of ovs-vsctl first reads the current configuration from OVSDB. As the number of ports grows, this starts to take an appreciable amount of time, and when it is repeated thousands of times the total time becomes significant. The solution is to add the ports in one invocation of ovs-vsctl (or a small number of them). For example, using bash:: $ ovs-vsctl add-br br0 $ cmds=; for i in {1..5000}; do cmds+=" -- add-port br0 p$i"; done $ ovs-vsctl $cmds takes seconds, not minutes or hours, in the OVS sandbox environment. Q: I created a bridge named br0. My bridge shows up in "ovs-vsctl show", but "ovs-ofctl show br0" just prints "br0 is not a bridge or a socket". A: Open vSwitch wasn't able to create the bridge. Check the ovs-vswitchd log for details (Debian and Red Hat packaging for Open vSwitch put it in /var/log/openvswitch/ovs-vswitchd.log). In general, the Open vSwitch database reflects the desired configuration state. ovs-vswitchd monitors the database and, when it changes, reconfigures the system to reflect the new desired state. This normally happens very quickly. Thus, a discrepancy between the database and the actual state indicates that ovs-vswitchd could not implement the configuration, and so one should check the log to find out why. (Another possible cause is that ovs-vswitchd is not running. This will make ovs-vsctl commands hang, if they change the configuration, unless one specifies ``--no-wait``.) Q: I have a bridge br0. I added a new port vif1.0, and it shows up in "ovs-vsctl show", but "ovs-vsctl list port" says that it has OpenFlow port ("ofport") -1, and "ovs-ofctl show br0" doesn't show vif1.0 at all. A: Open vSwitch wasn't able to create the port. Check the ovs-vswitchd log for details (Debian and Red Hat packaging for Open vSwitch put it in /var/log/openvswitch/ovs-vswitchd.log). Please see the previous question for more information. You may want to upgrade to Open vSwitch 2.3 (or later), in which ovs-vsctl will immediately report when there is an issue creating a port. Q: I created a tap device tap0, configured an IP address on it, and added it to a bridge, like this:: $ tunctl -t tap0 $ ip addr add 192.168.0.123/24 dev tap0 $ ip link set tap0 up $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 tap0 I expected that I could then use this IP address to contact other hosts on the network, but it doesn't work. Why not? A: The short answer is that this is a misuse of a "tap" device. Use an "internal" device implemented by Open vSwitch, which works differently and is designed for this use. To solve this problem with an internal device, instead run:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 int0 -- set Interface int0 type=internal $ ip addr add 192.168.0.123/24 dev int0 $ ip link set int0 up Even more simply, you can take advantage of the internal port that every bridge has under the name of the bridge:: $ ovs-vsctl add-br br0 $ ip addr add 192.168.0.123/24 dev br0 $ ip link set br0 up In more detail, a "tap" device is an interface between the Linux (or BSD) network stack and a user program that opens it as a socket. When the "tap" device transmits a packet, it appears in the socket opened by the userspace program. Conversely, when the userspace program writes to the "tap" socket, the kernel TCP/IP stack processes the packet as if it had been received by the "tap" device. Consider the configuration above. Given this configuration, if you "ping" an IP address in the 192.168.0.x subnet, the Linux kernel routing stack will transmit an ARP on the tap0 device. Open vSwitch userspace treats "tap" devices just like any other network device; that is, it doesn't open them as "tap" sockets. That means that the ARP packet will simply get dropped. You might wonder why the Open vSwitch kernel module doesn't intercept the ARP packet and bridge it. After all, Open vSwitch intercepts packets on other devices. The answer is that Open vSwitch only intercepts *received* packets, but this is a packet being transmitted. The same thing happens for all other types of network devices, except for Open vSwitch "internal" ports. If you, for example, add a physical Ethernet port to an OVS bridge, configure an IP address on a physical Ethernet port, and then issue a "ping" to an address in that subnet, the same thing happens: an ARP gets transmitted on the physical Ethernet port and Open vSwitch never sees it. (You should not do that, as documented at the beginning of this section.) It can make sense to add a "tap" device to an Open vSwitch bridge, if some userspace program (other than Open vSwitch) has opened the tap socket. This is the case, for example, if the "tap" device was created by KVM (or QEMU) to simulate a virtual NIC. In such a case, when OVS bridges a packet to the "tap" device, the kernel forwards that packet to KVM in userspace, which passes it along to the VM, and in the other direction, when the VM sends a packet, KVM writes it to the "tap" socket, which causes OVS to receive it and bridge it to the other OVS ports. Please note that in such a case no IP address is configured on the "tap" device (there is normally an IP address configured in the virtual NIC inside the VM, but this is not visible to the host Linux kernel or to Open vSwitch). There is one special case in which Open vSwitch does directly read and write "tap" sockets. This is an implementation detail of the Open vSwitch userspace switch, which implements its "internal" ports as Linux (or BSD) "tap" sockets. In such a userspace switch, OVS receives packets sent on the "tap" device used to implement an "internal" port by reading the associated "tap" socket, and bridges them to the rest of the switch. In the other direction, OVS transmits packets bridged to the "internal" port by writing them to the "tap" socket, causing them to be processed by the kernel TCP/IP stack as if they had been received on the "tap" device. Users should not need to be concerned with this implementation detail. Open vSwitch has a network device type called "tap". This is intended only for implementing "internal" ports in the OVS userspace switch and should not be used otherwise. In particular, users should not configure KVM "tap" devices as type "tap" (use type "system", the default, instead). Q: I observe packet loss at the beginning of RFC2544 tests on a server running few hundred container apps bridged to OVS with traffic generated by HW traffic generator. How can I fix this? A: This is expected behavior on virtual switches. RFC2544 tests were designed for hardware switches, which don't have caches on the fastpath that need to be heated. Traffic generators in order to prime the switch use learning phase to heat the caches before sending the actual traffic in test phase. In case of OVS the cache is flushed quickly and to accommodate the traffic generator's delay between learning and test phase, the max-idle timeout settings should be changed to 50000 ms.:: $ ovs-vsctl --no-wait set Open_vSwitch . other_config:max-idle=50000 Q: How can I configure the bridge internal interface MTU? Why does Open vSwitch keep changing internal ports MTU? A: By default Open vSwitch overrides the internal interfaces (e.g. br0) MTU. If you have just an internal interface (e.g. br0) and a physical interface (e.g. eth0), then every change in MTU to eth0 will be reflected to br0. Any manual MTU configuration using `ip` on internal interfaces is going to be overridden by Open vSwitch to match the current bridge minimum. Sometimes this behavior is not desirable, for example with tunnels. The MTU of an internal interface can be explicitly set using the following command:: $ ovs-vsctl set int br0 mtu_request=1450 After this, Open vSwitch will configure br0 MTU to 1450. Since this setting is in the database it will be persistent (compared to what happens with `ip`). The MTU configuration can be removed to restore the default behavior with:: $ ovs-vsctl set int br0 mtu_request=[] The ``mtu_request`` column can be used to configure MTU even for physical interfaces (e.g. eth0). Q: I just upgraded and I see a performance drop. Why? A: The OVS kernel datapath may have been updated to a new version without updating OVS userspace components. Sometimes new versions of OVS kernel module add functionality that is backwards compatible with older userspace components but may cause a drop in performance with them. Especially, if a kernel module from OVS 2.1 or newer is paired with OVS userspace 1.10 or older, there will be a performance drop for TCP traffic. Updating the OVS userspace components to the latest released version should fix the performance degradation. The OVS kernel modules has been distributed with the upstream Linux kernel since Linux 3.3. As of OVS 2.15 the use of the kernel module distributed with OVS is deprecated in favour of the kernel module distributed with the upstream Linux kernel. And as of OVS 3.0 the kernel module is no longer part of the OVS distribution. Accordingly, for OVS 2.15 and newer, to get the best possible performance and functionality it is recommended to pair the latest released version of OVS with the latest released version of the Linux kernel. While, for releases of OVS prior to 2.15, is recommended to pair the same versions of the kernel module and OVS userspace. Q: I can't unload the openvswitch kernel module. Why? A: The userspace might still hold the reference count. So ``rmmod openvswitch`` does not work, for example:: $ lsmod | grep openvswitch openvswitch 155648 4 nf_conncount 24576 1 openvswitch Use the command below to drop the refcnt:: $ ovs-dpctl del-dp system@ovs-system $ rmmod openvswitch openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/openflow.rst000066400000000000000000000577351514270232600247440ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============== Using OpenFlow ============== Q: What versions of OpenFlow does Open vSwitch support? A: The following table lists the versions of OpenFlow supported by each version of Open vSwitch: ===================== ===== ===== ===== ===== ===== ===== Open vSwitch OF1.0 OF1.1 OF1.2 OF1.3 OF1.4 OF1.5 ===================== ===== ===== ===== ===== ===== ===== 1.9 and earlier yes --- --- --- --- --- 1.10, 1.11 yes --- (*) (*) --- --- 2.0, 2.1 yes (*) (*) (*) --- --- 2.2 yes (*) (*) (*) (%) (*) 2.3, 2.4 yes yes yes yes (*) (*) 2.5, 2.6, 2.7 yes yes yes yes (*) (*) 2.8, 2.9, 2.10, 2.11 yes yes yes yes yes (*) 2.12 yes yes yes yes yes yes ===================== ===== ===== ===== ===== ===== ===== --- Not supported. yes Supported and enabled by default (*) Supported, but missing features, and must be enabled by user. (%) Experimental, unsafe implementation. In any case, the user may override the default: - To enable OpenFlow 1.0, 1.1, 1.2, and 1.3 on bridge br0:: $ ovs-vsctl set bridge br0 \ protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13 - To enable OpenFlow 1.0, 1.1, 1.2, 1.3, 1.4, and 1.5 on bridge br0:: $ ovs-vsctl set bridge br0 \ protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15 - To enable only OpenFlow 1.0 on bridge br0:: $ ovs-vsctl set bridge br0 protocols=OpenFlow10 All current versions of ovs-ofctl enable only OpenFlow 1.0 by default. Use the -O option to enable support for later versions of OpenFlow in ovs-ofctl. For example:: $ ovs-ofctl -O OpenFlow13 dump-flows br0 (Open vSwitch 2.2 had an experimental implementation of OpenFlow 1.4 that could cause crashes. We don't recommend enabling it.) :doc:`/topics/openflow` tracks support for OpenFlow 1.1 and later features. Q: Does Open vSwitch support MPLS? A: Before version 1.11, Open vSwitch did not support MPLS. That is, these versions can match on MPLS Ethernet types, but they cannot match, push, or pop MPLS labels, nor can they look past MPLS labels into the encapsulated packet. Open vSwitch versions 1.11, 2.0, and 2.1 have very minimal support for MPLS. With the userspace datapath only, these versions can match, push, or pop a single MPLS label, but they still cannot look past MPLS labels (even after popping them) into the encapsulated packet. Kernel datapath support is unchanged from earlier versions. Open vSwitch version 2.3 can match, push, or pop a single MPLS label and look past the MPLS label into the encapsulated packet. Both userspace and kernel datapaths will be supported, but MPLS processing always happens in userspace either way, so kernel datapath performance will be disappointing. Open vSwitch version 2.4 can match, push, or pop up to 3 MPLS labels and look past the MPLS label into the encapsulated packet. It will have kernel support for MPLS, yielding improved performance. Q: I'm getting "error type 45250 code 0". What's that? A: This is a Open vSwitch extension to OpenFlow error codes. Open vSwitch uses this extension when it must report an error to an OpenFlow controller but no standard OpenFlow error code is suitable. Open vSwitch logs the errors that it sends to controllers, so the easiest thing to do is probably to look at the ovs-vswitchd log to find out what the error was. If you want to dissect the extended error message yourself, the format is documented in include/openflow/nicira-ext.h in the Open vSwitch source distribution. The extended error codes are documented in include/openvswitch/ofp-errors.h. Q: Some of the traffic that I'd expect my OpenFlow controller to see doesn't actually appear through the OpenFlow connection, even though I know that it's going through. A: By default, Open vSwitch assumes that OpenFlow controllers are connected "in-band", that is, that the controllers are actually part of the network that is being controlled. In in-band mode, Open vSwitch sets up special "hidden" flows to make sure that traffic can make it back and forth between OVS and the controllers. These hidden flows are higher priority than any flows that can be set up through OpenFlow, and they are not visible through normal OpenFlow flow table dumps. Usually, the hidden flows are desirable and helpful, but occasionally they can cause unexpected behavior. You can view the full OpenFlow flow table, including hidden flows, on bridge br0 with the command:: $ ovs-appctl bridge/dump-flows br0 to help you debug. The hidden flows are those with priorities greater than 65535 (the maximum priority that can be set with OpenFlow). The ``Documentation/topics/design`` doc describes the in-band model in detail. If your controllers are not actually in-band (e.g. they are on localhost via 127.0.0.1, or on a separate network), then you should configure your controllers in "out-of-band" mode. If you have one controller on bridge br0, then you can configure out-of-band mode on it with:: $ ovs-vsctl set controller br0 connection-mode=out-of-band Q: Some of the OpenFlow flows that my controller sets up don't seem to apply to certain traffic, especially traffic between OVS and the controller itself. A: See above. Q: I configured all my controllers for out-of-band control mode but "ovs-appctl bridge/dump-flows" still shows some hidden flows. A: You probably have a remote manager configured (e.g. with "ovs-vsctl set-manager"). By default, Open vSwitch assumes that managers need in-band rules set up on every bridge. You can disable these rules on bridge br0 with:: $ ovs-vsctl set bridge br0 other-config:disable-in-band=true This actually disables in-band control entirely for the bridge, as if all the bridge's controllers were configured for out-of-band control. Q: My OpenFlow controller doesn't see the VLANs that I expect. A: See answer under "VLANs", above. Q: I ran ``ovs-ofctl add-flow br0 nw_dst=192.168.0.1,actions=drop`` but I got a funny message like this:: ofp_util|INFO|normalization changed ofp_match, details: ofp_util|INFO| pre: nw_dst=192.168.0.1 ofp_util|INFO|post: and when I ran ``ovs-ofctl dump-flows br0`` I saw that my nw_dst match had disappeared, so that the flow ends up matching every packet. A: The term "normalization" in the log message means that a flow cannot match on an L3 field without saying what L3 protocol is in use. The "ovs-ofctl" command above didn't specify an L3 protocol, so the L3 field match was dropped. In this case, the L3 protocol could be IP or ARP. A correct command for each possibility is, respectively:: $ ovs-ofctl add-flow br0 ip,nw_dst=192.168.0.1,actions=drop and:: $ ovs-ofctl add-flow br0 arp,nw_dst=192.168.0.1,actions=drop Similarly, a flow cannot match on an L4 field without saying what L4 protocol is in use. For example, the flow match ``tp_src=1234`` is, by itself, meaningless and will be ignored. Instead, to match TCP source port 1234, write ``tcp,tp_src=1234``, or to match UDP source port 1234, write ``udp,tp_src=1234``. Q: How can I figure out the OpenFlow port number for a given port? A: The ``OFPT_FEATURES_REQUEST`` message requests an OpenFlow switch to respond with an ``OFPT_FEATURES_REPLY`` that, among other information, includes a mapping between OpenFlow port names and numbers. From a command prompt, ``ovs-ofctl show br0`` makes such a request and prints the response for switch br0. The Interface table in the Open vSwitch database also maps OpenFlow port names to numbers. To print the OpenFlow port number associated with interface eth0, run:: $ ovs-vsctl get Interface eth0 ofport You can print the entire mapping with:: $ ovs-vsctl -- --columns=name,ofport list Interface but the output mixes together interfaces from all bridges in the database, so it may be confusing if more than one bridge exists. In the Open vSwitch database, ofport value ``-1`` means that the interface could not be created due to an error. (The Open vSwitch log should indicate the reason.) ofport value ``[]`` (the empty set) means that the interface hasn't been created yet. The latter is normally an intermittent condition (unless ovs-vswitchd is not running). Q: I added some flows with my controller or with ovs-ofctl, but when I run "ovs-dpctl dump-flows" I don't see them. A: ovs-dpctl queries a kernel datapath, not an OpenFlow switch. It won't display the information that you want. You want to use ``ovs-ofctl dump-flows`` instead. Q: It looks like each of the interfaces in my bonded port shows up as an individual OpenFlow port. Is that right? A: Yes, Open vSwitch makes individual bond interfaces visible as OpenFlow ports, rather than the bond as a whole. The interfaces are treated together as a bond for only a few purposes: - Sending a packet to the OFPP_NORMAL port. (When an OpenFlow controller is not configured, this happens implicitly to every packet.) - Mirrors configured for output to a bonded port. It would make a lot of sense for Open vSwitch to present a bond as a single OpenFlow port. If you want to contribute an implementation of such a feature, please bring it up on the Open vSwitch development mailing list at dev@openvswitch.org. Q: I have a sophisticated network setup involving Open vSwitch, VMs or multiple hosts, and other components. The behavior isn't what I expect. Help! A: To debug network behavior problems, trace the path of a packet, hop-by-hop, from its origin in one host to a remote host. If that's correct, then trace the path of the response packet back to the origin. The open source tool called ``plotnetcfg`` can help to understand the relationship between the networking devices on a single host. Usually a simple ICMP echo request and reply (``ping``) packet is good enough. Start by initiating an ongoing ``ping`` from the origin host to a remote host. If you are tracking down a connectivity problem, the "ping" will not display any successful output, but packets are still being sent. (In this case the packets being sent are likely ARP rather than ICMP.) Tools available for tracing include the following: - ``tcpdump`` and ``wireshark`` for observing hops across network devices, such as Open vSwitch internal devices and physical wires. - ``ovs-appctl dpif/dump-flows
`` in Open vSwitch 1.10 and later or ``ovs-dpctl dump-flows
`` in earlier versions. These tools allow one to observe the actions being taken on packets in ongoing flows. See ovs-vswitchd(8) for ``ovs-appctl dpif/dump-flows`` documentation, ovs-dpctl(8) for ``ovs-dpctl dump-flows`` documentation, and "Why are there so many different ways to dump flows?" above for some background. - ``ovs-appctl ofproto/trace`` to observe the logic behind how ovs-vswitchd treats packets. See ovs-vswitchd(8) for documentation. You can out more details about a given flow that ``ovs-dpctl dump-flows`` displays, by cutting and pasting a flow from the output into an ``ovs-appctl ofproto/trace`` command. - SPAN, RSPAN, and ERSPAN features of physical switches, to observe what goes on at these physical hops. Starting at the origin of a given packet, observe the packet at each hop in turn. For example, in one plausible scenario, you might: 1. ``tcpdump`` the ``eth`` interface through which an ARP egresses a VM, from inside the VM. 2. ``tcpdump`` the ``vif`` or ``tap`` interface through which the ARP ingresses the host machine. 3. Use ``ovs-dpctl dump-flows`` to spot the ARP flow and observe the host interface through which the ARP egresses the physical machine. You may need to use ``ovs-dpctl show`` to interpret the port numbers. If the output seems surprising, you can use ``ovs-appctl ofproto/trace`` to observe details of how ovs-vswitchd determined the actions in the ``ovs-dpctl dump-flows`` output. 4. ``tcpdump`` the ``eth`` interface through which the ARP egresses the physical machine. 5. ``tcpdump`` the ``eth`` interface through which the ARP ingresses the physical machine, at the remote host that receives the ARP. 6. Use ``ovs-dpctl dump-flows`` to spot the ARP flow on the remote host remote host that receives the ARP and observe the VM ``vif`` or ``tap`` interface to which the flow is directed. Again, ``ovs-dpctl show`` and ``ovs-appctl ofproto/trace`` might help. 7. ``tcpdump`` the ``vif`` or ``tap`` interface to which the ARP is directed. 8. ``tcpdump`` the ``eth`` interface through which the ARP ingresses a VM, from inside the VM. It is likely that during one of these steps you will figure out the problem. If not, then follow the ARP reply back to the origin, in reverse. Q: How do I make a flow drop packets? A: To drop a packet is to receive it without forwarding it. OpenFlow explicitly specifies forwarding actions. Thus, a flow with an empty set of actions does not forward packets anywhere, causing them to be dropped. You can specify an empty set of actions with ``actions=`` on the ovs-ofctl command line. For example:: $ ovs-ofctl add-flow br0 priority=65535,actions= would cause every packet entering switch br0 to be dropped. You can write "drop" explicitly if you like. The effect is the same. Thus, the following command also causes every packet entering switch br0 to be dropped:: $ ovs-ofctl add-flow br0 priority=65535,actions=drop ``drop`` is not an action, either in OpenFlow or Open vSwitch. Rather, it is only a way to say that there are no actions. Q: I added a flow to send packets out the ingress port, like this:: $ ovs-ofctl add-flow br0 in_port=2,actions=2 but OVS drops the packets instead. A: Yes, OpenFlow requires a switch to ignore attempts to send a packet out its ingress port. The rationale is that dropping these packets makes it harder to loop the network. Sometimes this behavior can even be convenient, e.g. it is often the desired behavior in a flow that forwards a packet to several ports ("floods" the packet). Sometimes one really needs to send a packet out its ingress port ("hairpin"). In this case, output to ``OFPP_IN_PORT``, which in ovs-ofctl syntax is expressed as just ``in_port``, e.g.:: $ ovs-ofctl add-flow br0 in_port=2,actions=in_port This also works in some circumstances where the flow doesn't match on the input port. For example, if you know that your switch has five ports numbered 2 through 6, then the following will send every received packet out every port, even its ingress port:: $ ovs-ofctl add-flow br0 actions=2,3,4,5,6,in_port or, equivalently:: $ ovs-ofctl add-flow br0 actions=all,in_port Sometimes, in complicated flow tables with multiple levels of ``resubmit`` actions, a flow needs to output to a particular port that may or may not be the ingress port. It's difficult to take advantage of ``OFPP_IN_PORT`` in this situation. To help, Open vSwitch provides, as an OpenFlow extension, the ability to modify the in_port field. Whatever value is currently in the in_port field is the port to which outputs will be dropped, as well as the destination for ``OFPP_IN_PORT``. This means that the following will reliably output to port 2 or to ports 2 through 6, respectively:: $ ovs-ofctl add-flow br0 in_port=2,actions=load:0->NXM_OF_IN_PORT[],2 $ ovs-ofctl add-flow br0 actions=load:0->NXM_OF_IN_PORT[],2,3,4,5,6 If the input port is important, then one may save and restore it on the stack:: $ ovs-ofctl add-flow br0 actions=push:NXM_OF_IN_PORT[],\ load:0->NXM_OF_IN_PORT[],\ 2,3,4,5,6,\ pop:NXM_OF_IN_PORT[] Q: My bridge br0 has host 192.168.0.1 on port 1 and host 192.168.0.2 on port 2. I set up flows to forward only traffic destined to the other host and drop other traffic, like this:: priority=5,in_port=1,ip,nw_dst=192.168.0.2,actions=2 priority=5,in_port=2,ip,nw_dst=192.168.0.1,actions=1 priority=0,actions=drop But it doesn't work--I don't get any connectivity when I do this. Why? A: These flows drop the ARP packets that IP hosts use to establish IP connectivity over Ethernet. To solve the problem, add flows to allow ARP to pass between the hosts:: priority=5,in_port=1,arp,actions=2 priority=5,in_port=2,arp,actions=1 This issue can manifest other ways, too. The following flows that match on Ethernet addresses instead of IP addresses will also drop ARP packets, because ARP requests are broadcast instead of being directed to a specific host:: priority=5,in_port=1,dl_dst=54:00:00:00:00:02,actions=2 priority=5,in_port=2,dl_dst=54:00:00:00:00:01,actions=1 priority=0,actions=drop The solution already described above will also work in this case. It may be better to add flows to allow all multicast and broadcast traffic:: priority=5,in_port=1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=2 priority=5,in_port=2,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=1 Q: My bridge disconnects from my controller on add-port/del-port. A: Reconfiguring your bridge can change your bridge's datapath-id because Open vSwitch generates datapath-id from the MAC address of one of its ports. In that case, Open vSwitch disconnects from controllers because there's no graceful way to notify controllers about the change of datapath-id. To avoid the behaviour, you can configure datapath-id manually.:: $ ovs-vsctl set bridge br0 other-config:datapath-id=0123456789abcdef Q: My controller complains that OVS is not buffering packets. What's going on? A: "Packet buffering" is an optional OpenFlow feature, and controllers should detect how many "buffers" an OpenFlow switch implements. It was recently noticed that OVS implementation of the buffering feature was not compliant to OpenFlow specifications. Rather than fix it and risk controller incompatibility, the buffering feature is removed as of OVS 2.7. Controllers are already expected to work properly in cases where the switch can not buffer packets, but sends full packets in "packet-in" messages instead, so this change should not affect existing users. After the change OVS always sends the ``buffer_id`` as ``0xffffffff`` in "packet-in" messages and will send an error response if any other value of this field is included in a "packet-out" or a "flow mod" sent by a controller. Packet buffers have limited usefulness in any case. Table-miss packet-in messages most commonly pass the first packet in a microflow to the OpenFlow controller, which then sets up an OpenFlow flow that handles remaining traffic in the microflow without further controller intervention. In such a case, the packet that initiates the microflow is in practice usually small (certainly for TCP), which means that the switch sends the entire packet to the controller and the buffer only saves a small number of bytes in the reverse direction. Q: How does OVS divide flows among buckets in an OpenFlow "select" group? A: In Open vSwitch 2.3 and earlier, Open vSwitch used the destination Ethernet address to choose a bucket in a select group. Open vSwitch 2.4 and later by default hashes the source and destination Ethernet address, VLAN ID, Ethernet type, IPv4/v6 source and destination address and protocol, and for TCP and SCTP only, the source and destination ports. The hash is "symmetric", meaning that exchanging source and destination addresses does not change the bucket selection. Select groups in Open vSwitch 2.4 and later can be configured to use a different hash function, using a Netronome extension to the OpenFlow 1.5+ group_mod message. For more information, see Documentation/group-selection-method-property.txt in the Open vSwitch source tree. Q: An OpenFlow "select" group isn't dividing packets evenly among the buckets. A: When a packet passes through a "select" group, Open vSwitch hashes a subset of the fields in the packet, then it maps the hash value to a bucket. This means that packets whose hashed fields are the same will always go to the same bucket[*]. More specifically, if you test with a single traffic flow, only one bucket will receive any traffic[**]. Furthermore, statistics and probability mean that testing with a small number of flows may still yield an uneven distribution. [*] Unless its bucket has a watch port or group whose liveness changes during the test. [**] Unless the hash includes fields that vary within a traffic flow, such as tcp_flags. Q: I added a flow to accept packets on VLAN 123 and output them on VLAN 456, like so:: $ ovs-ofctl add-flow br0 dl_vlan=123,actions=output:1,mod_vlan_vid:456 but the packets are actually being output in VLAN 123. Why? A: OpenFlow actions are executed in the order specified. Thus, the actions above first output the packet, then change its VLAN. Since the output occurs before changing the VLAN, the change in VLAN will have no visible effect. To solve this and similar problems, order actions so that changes to headers happen before output, e.g.:: $ ovs-ofctl add-flow br0 dl_vlan=123,actions=mod_vlan_vid:456,output:1 See also the following question. Q: I added a flow to a redirect packets for TCP port 80 to port 443, like so:: $ ovs-ofctl add-flow br0 tcp,tcp_dst=123,actions=mod_tp_dst:443 but the packets are getting dropped instead. Why? A: This set of actions does change the TCP destination port to 443, but then it does nothing more. It doesn't, for example, say to continue to another flow table or to output the packet. Therefore, the packet is dropped. To solve the problem, add an action that does something with the modified packet. For example:: $ ovs-ofctl add-flow br0 tcp,tcp_dst=123,actions=mod_tp_dst:443,normal See also the preceding question. Q: When using the "ct" action with FTP connections, it doesn't seem to matter if I set the "alg=ftp" parameter in the action. Is this required? A: It is advisable to use this option. Some platforms may automatically detect and apply ALGs in the "ct" action regardless of the parameters you provide, however this is not consistent across all implementations. The `ovs-ofctl(8) `_ man pages contain further details in the description of the ALG parameter. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/qos.rst000066400000000000000000000200411514270232600236710ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================== Quality of Service (QoS) ======================== Q: Does OVS support Quality of Service (QoS)? A: Yes. For traffic that egresses from a switch, OVS supports traffic shaping; for traffic that ingresses into a switch, OVS support policing. Policing is a simple form of quality-of-service that simply drops packets received in excess of the configured rate. Due to its simplicity, policing is usually less accurate and less effective than egress traffic shaping, which queues packets. Keep in mind that ingress and egress are from the perspective of the switch. That means that egress shaping limits the rate at which traffic is allowed to transmit from a physical interface, but not the rate at which traffic will be received on a virtual machine's VIF. For ingress policing, the behavior is the opposite. Q: How do I configure egress traffic shaping? A: Suppose that you want to set up bridge br0 connected to physical Ethernet port eth0 (a 1 Gbps device) and virtual machine interfaces vif1.0 and vif2.0, and that you want to limit traffic from vif1.0 to eth0 to 10 Mbps and from vif2.0 to eth0 to 20 Mbps. Then, you could configure the bridge this way:: $ ovs-vsctl -- \ add-br br0 -- \ add-port br0 eth0 -- \ add-port br0 vif1.0 -- set interface vif1.0 ofport_request=5 -- \ add-port br0 vif2.0 -- set interface vif2.0 ofport_request=6 -- \ set port eth0 qos=@newqos -- \ --id=@newqos create qos type=linux-htb \ other-config:max-rate=1000000000 \ queues:123=@vif10queue \ queues:234=@vif20queue -- \ --id=@vif10queue create queue other-config:max-rate=10000000 -- \ --id=@vif20queue create queue other-config:max-rate=20000000 At this point, bridge br0 is configured with the ports and eth0 is configured with the queues that you need for QoS, but nothing is actually directing packets from vif1.0 or vif2.0 to the queues that we have set up for them. That means that all of the packets to eth0 are going to the "default queue", which is not what we want. We use OpenFlow to direct packets from vif1.0 and vif2.0 to the queues reserved for them:: $ ovs-ofctl add-flow br0 in_port=5,actions=set_queue:123,normal $ ovs-ofctl add-flow br0 in_port=6,actions=set_queue:234,normal Each of the above flows matches on the input port, sets up the appropriate queue (123 for vif1.0, 234 for vif2.0), and then executes the "normal" action, which performs the same switching that Open vSwitch would have done without any OpenFlow flows being present. (We know that vif1.0 and vif2.0 have OpenFlow port numbers 5 and 6, respectively, because we set their ofport_request columns above. If we had not done that, then we would have needed to find out their port numbers before setting up these flows.) Now traffic going from vif1.0 or vif2.0 to eth0 should be rate-limited. By the way, if you delete the bridge created by the above commands, with:: $ ovs-vsctl del-br br0 then that will leave one unreferenced QoS record and two unreferenced Queue records in the Open vSwich database. One way to clear them out, assuming you don't have other QoS or Queue records that you want to keep, is:: $ ovs-vsctl -- --all destroy QoS -- --all destroy Queue If you do want to keep some QoS or Queue records, or the Open vSwitch you are using is older than version 1.8 (which added the ``--all`` option), then you will have to destroy QoS and Queue records individually. Q: How do I configure ingress policing? A: A policing policy can be configured on an interface to drop packets that arrive at a higher rate than the configured value. For example, the following commands will rate-limit traffic that vif1.0 may generate to 10Mbps:: $ ovs-vsctl set interface vif1.0 ingress_policing_rate=10000 $ ovs-vsctl set interface vif1.0 ingress_policing_burst=8000 Traffic policing can interact poorly with some network protocols and can have surprising results. The "Ingress Policing" section of ovs-vswitchd.conf.db(5) discusses the issues in greater detail. Q: I configured Quality of Service (QoS) in my OpenFlow network by adding records to the QoS and Queue table, but the results aren't what I expect. A: Did you install OpenFlow flows that use your queues? This is the primary way to tell Open vSwitch which queues you want to use. If you don't do this, then the default queue will be used, which will probably not have the effect you want. Refer to the previous question for an example. Q: I'd like to take advantage of some QoS feature that Open vSwitch doesn't yet support. How do I do that? A: Open vSwitch does not implement QoS itself. Instead, it can configure some, but not all, of the QoS features built into the Linux kernel. If you need some QoS feature that OVS cannot configure itself, then the first step is to figure out whether Linux QoS supports that feature. If it does, then you can submit a patch to support Open vSwitch configuration for that feature, or you can use "tc" directly to configure the feature in Linux. (If Linux QoS doesn't support the feature you want, then first you have to add that support to Linux.) Q: I configured QoS, correctly, but my measurements show that it isn't working as well as I expect. A: With the Linux kernel, the Open vSwitch implementation of QoS has two aspects: - Open vSwitch configures a subset of Linux kernel QoS features, according to what is in OVSDB. It is possible that this code has bugs. If you believe that this is so, then you can configure the Linux traffic control (QoS) stack directly with the "tc" program. If you get better results that way, you can send a detailed bug report to bugs@openvswitch.org. It is certain that Open vSwitch cannot configure every Linux kernel QoS feature. If you need some feature that OVS cannot configure, then you can also use "tc" directly (or add that feature to OVS). - The Open vSwitch implementation of OpenFlow allows flows to be directed to particular queues. This is pretty simple and unlikely to have serious bugs at this point. However, most problems with QoS on Linux are not bugs in Open vSwitch at all. They tend to be either configuration errors (please see the earlier questions in this section) or issues with the traffic control (QoS) stack in Linux. The Open vSwitch developers are not experts on Linux traffic control. We suggest that, if you believe you are encountering a problem with Linux traffic control, that you consult the tc manpages (e.g. tc(8), tc-htb(8), tc-hfsc(8)), web resources (e.g. http://lartc.org/), or mailing lists (e.g. http://vger.kernel.org/vger-lists.html#netdev). Q: Does Open vSwitch support OpenFlow meters? A: Open vSwitch 2.0 added OpenFlow protocol support for OpenFlow meters. Open vSwitch 2.7 implemented meters in the userspace datapath. Open vSwitch 2.10 implemented meters in the Linux kernel datapath. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/releases.rst000066400000000000000000000254031514270232600247010ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======== Releases ======== Q: What does it mean for an Open vSwitch release to be LTS (long-term support)? A: All official releases have been through a comprehensive testing process and are suitable for production use. Planned releases occur twice a year. If a significant bug is identified in an LTS release, we will provide an updated release that includes the fix. Releases that are not LTS may not be fixed and may just be supplanted by the next major release. The current LTS release is 3.3.x. For more information on the Open vSwitch release process, refer to :doc:`/internals/release-process`. Q: What Linux kernel versions does each Open vSwitch release work with? A: Open vSwitch userspace works with the kernel module shipped with Linux upstream 3.3 and later. Building the Linux kernel module from the Open vSwitch source tree was deprecated starting with Open vSwitch 2.15. And the kernel module source code was completely removed from the Open vSwitch source tree in 3.0 release. Q: Are all features available with all datapaths? A: Open vSwitch supports different datapaths on different platforms. Each datapath has a different feature set: the following tables try to summarize the status. Supported datapaths: Linux upstream The datapath implemented by the kernel module shipped with Linux upstream. Since features have been gradually introduced into the kernel, the table mentions the first Linux release whose OVS module supports the feature. Userspace This datapath supports conventional system devices as well as DPDK and AF_XDP devices when support for those is built. This is the only datapath that works on NetBSD, FreeBSD and Mac OSX. Hyper-V Also known as the Windows datapath. The following table lists the datapath supported features from an Open vSwitch user's perspective. The "Linux upstream" column lists the Linux kernel version that introduced a given feature into its kernel module. The "Linux OVS tree" and "Userspace" columns list the Open vSwitch release versions that introduced a given feature into the included kernel module or the userspace datapath, respectively. ========================== ============== ========= ======= Feature Linux upstream Userspace Hyper-V ========================== ============== ========= ======= Connection tracking 4.3 2.6 YES Connection tracking-IPv6 YES YES 3.0 Conntrack Fragment Reass. 4.3 2.12 YES Conntrack IPv6 Fragment 4.3 2.12 3.1 Conntrack Timeout Policies 5.2 2.14 NO Conntrack Zone Limit 4.18 2.13 YES Conntrack NAT 4.6 2.8 YES Conntrack NAT6 4.6 2.8 3.0 Conntrack Helper Persist. YES 3.3 NO Tunnel - GRE 3.11 2.4 YES Tunnel - VXLAN 3.12 2.4 YES Tunnel - Geneve 3.18 2.4 YES Tunnel - GRE-IPv6 4.18 2.6 NO Tunnel - VXLAN-IPv6 4.3 2.6 NO Tunnel - Geneve-IPv6 4.4 2.6 3.0 Tunnel - ERSPAN 4.18 2.10 NO Tunnel - ERSPAN-IPv6 4.18 2.10 NO Tunnel - GTP-U NO 2.14 NO Tunnel - SRv6 NO 3.2 NO Tunnel - Bareudp 5.7 NO NO QoS - Policing YES 2.6 NO QoS - Shaping YES NO NO sFlow YES 1.0 NO IPFIX 3.10 1.11 YES Set action YES 1.0 PARTIAL NIC Bonding YES 1.0 YES Multiple VTEPs YES 1.10 YES Meter action 4.15 2.7 NO check_pkt_len action 5.2 2.12 NO ========================== ============== ========= ======= Do note, however: * Only a limited set of flow fields is modifiable via the set action by the Hyper-V datapath. * Userspace datapath support, in some cases, is dependent on the associated interface types. For example, DPDK interfaces support ingress and egress policing, but not shaping. The following table lists features that do not *directly* impact an Open vSwitch user, e.g. because their absence can be hidden by the ofproto layer (usually this comes with a performance penalty). ===================== ============== ============== ========= ======= Feature Linux upstream Linux OVS tree Userspace Hyper-V ===================== ============== ============== ========= ======= SCTP flows 3.12 YES YES YES MPLS 3.19 YES YES YES UFID 4.0 YES YES NO Megaflows 3.12 YES YES NO Masked set action 4.0 YES YES NO Recirculation 3.19 YES YES YES TCP flags matching 3.13 YES YES NO Validate flow actions YES YES N/A NO Multiple datapaths YES YES YES NO ===================== ============== ============== ========= ======= Q: What DPDK version does each Open vSwitch release work with? A: The following table lists the DPDK version against which the given versions of Open vSwitch will successfully build. ============ ======== Open vSwitch DPDK ============ ======== 2.2.x 1.6 2.3.x 1.6 2.4.x 2.0 2.5.x 2.2 2.6.x 16.07.2 2.7.x 16.11.9 2.8.x 17.05.2 2.9.x 17.11.10 2.10.x 17.11.10 2.11.x 18.11.9 2.12.x 18.11.9 2.13.x 19.11.13 2.14.x 19.11.13 2.15.x 20.11.6 2.16.x 20.11.6 2.17.x 21.11.9 3.0.x 21.11.9 3.1.x 22.11.7 3.2.x 22.11.7 3.3.x 23.11.5 3.4.x 23.11.5 3.5.x 24.11.3 3.6.x 24.11.3 3.7.x 25.11 ============ ======== Q: Are all the DPDK releases that OVS versions work with maintained? No. DPDK follows YY.MM.n (Year.Month.Number) versioning. Typically, all DPDK releases get a stable YY.MM.1 update with bugfixes 3 months after the YY.MM.0 release. In some cases there may also be a YY.MM.2 release. DPDK LTS releases start once a year at YY.11.0 and are maintained for two years, with YY.MM.n+1 releases around every 3 months. The latest information about DPDK stable and LTS releases can be found at `DPDK stable`_. .. _DPDK stable: https://doc.dpdk.org/guides-25.11/contributing/stable.html Q: What features are not available in the Open vSwitch kernel datapath that ships as part of the upstream Linux kernel? A: Certain features require kernel support to function or to have reasonable performance. If the ovs-vswitchd log file indicates that a feature is not supported, consider upgrading to a newer upstream Linux release. Q: Why do tunnels not work when using a kernel module other than the one packaged with Open vSwitch? A: Support for tunnels was added to the upstream Linux kernel module after the rest of Open vSwitch. As a result, some kernels may contain support for Open vSwitch but not tunnels. The minimum kernel version that supports each tunnel protocol is: ======== ============ Protocol Linux Kernel ======== ============ GRE 3.11 VXLAN 3.12 Geneve 3.18 ERSPAN 4.18 ======== ============ Q: Why are UDP tunnel checksums not computed for VXLAN or Geneve? A: Generating outer UDP checksums requires kernel support that was not part of the initial implementation of these protocols. The kernel modules shipped with upstream Linux 4.0 and later support UDP checksums. Q: What features are not available when using the userspace datapath? A: Tunnel virtual ports are not supported, as described in the previous answer. It is also not possible to use queue-related actions. On Linux kernels before 2.6.39, maximum-sized VLAN packets may not be transmitted. Q: Should userspace or kernel be upgraded first to minimize downtime? A. In general, the Open vSwitch userspace should be used with the kernel version included in the same release or with the version from upstream Linux. However, when upgrading between two releases of Open vSwitch it is best to migrate userspace first to reduce the possibility of incompatibilities. Q: What happened to the bridge compatibility feature? A: Bridge compatibility was a feature of Open vSwitch 1.9 and earlier. When it was enabled, Open vSwitch imitated the interface of the Linux kernel "bridge" module. This allowed users to drop Open vSwitch into environments designed to use the Linux kernel bridge module without adapting the environment to use Open vSwitch. Open vSwitch 1.10 and later do not support bridge compatibility. The feature was dropped because version 1.10 adopted a new internal architecture that made bridge compatibility difficult to maintain. Now that many environments use OVS directly, it would be rarely useful in any case. To use bridge compatibility, install OVS 1.9 or earlier, including the accompanying kernel modules (both the main and bridge compatibility modules), following the instructions that come with the release. Be sure to start the ovs-brcompatd daemon. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/terminology.rst000066400000000000000000000023441514270232600254450ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========== Terminology =========== Q: I thought Open vSwitch was a virtual Ethernet switch, but the documentation keeps talking about bridges. What's a bridge? A: In networking, the terms "bridge" and "switch" are synonyms. Open vSwitch implements an Ethernet switch, which means that it is also an Ethernet bridge. Q: What's a VLAN? A: See :doc:`vlan`. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/vlan.rst000066400000000000000000000263201514270232600240350ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ===== VLANs ===== Q: What's a VLAN? A: At the simplest level, a VLAN (short for "virtual LAN") is a way to partition a single switch into multiple switches. Suppose, for example, that you have two groups of machines, group A and group B. You want the machines in group A to be able to talk to each other, and you want the machine in group B to be able to talk to each other, but you don't want the machines in group A to be able to talk to the machines in group B. You can do this with two switches, by plugging the machines in group A into one switch and the machines in group B into the other switch. If you only have one switch, then you can use VLANs to do the same thing, by configuring the ports for machines in group A as VLAN "access ports" for one VLAN and the ports for group B as "access ports" for a different VLAN. The switch will only forward packets between ports that are assigned to the same VLAN, so this effectively subdivides your single switch into two independent switches, one for each group of machines. So far we haven't said anything about VLAN headers. With access ports, like we've described so far, no VLAN header is present in the Ethernet frame. This means that the machines (or switches) connected to access ports need not be aware that VLANs are involved, just like in the case where we use two different physical switches. Now suppose that you have a whole bunch of switches in your network, instead of just one, and that some machines in group A are connected directly to both switches 1 and 2. To allow these machines to talk to each other, you could add an access port for group A's VLAN to switch 1 and another to switch 2, and then connect an Ethernet cable between those ports. That works fine, but it doesn't scale well as the number of switches and the number of VLANs increases, because you use up a lot of valuable switch ports just connecting together your VLANs. This is where VLAN headers come in. Instead of using one cable and two ports per VLAN to connect a pair of switches, we configure a port on each switch as a VLAN "trunk port". Packets sent and received on a trunk port carry a VLAN header that says what VLAN the packet belongs to, so that only two ports total are required to connect the switches, regardless of the number of VLANs in use. Normally, only switches (either physical or virtual) are connected to a trunk port, not individual hosts, because individual hosts don't expect to see a VLAN header in the traffic that they receive. None of the above discussion says anything about particular VLAN numbers. This is because VLAN numbers are completely arbitrary. One must only ensure that a given VLAN is numbered consistently throughout a network and that different VLANs are given different numbers. (That said, VLAN 0 is usually synonymous with a packet that has no VLAN header, and VLAN 4095 is reserved.) Q: VLANs don't work. A: Do you have VLANs enabled on the physical switch that OVS is attached to? Make sure that the port is configured to trunk the VLAN or VLANs that you are using with OVS. Q: Outgoing VLAN-tagged traffic goes through OVS to my physical switch and to its destination host, but OVS seems to drop incoming return traffic. A: It's possible that you have the VLAN configured on your physical switch as the "native" VLAN. In this mode, the switch treats incoming packets either tagged with the native VLAN or untagged as part of the native VLAN. It may also send outgoing packets in the native VLAN without a VLAN tag. If this is the case, you have two choices: - Change the physical switch port configuration to tag packets it forwards to OVS with the native VLAN instead of forwarding them untagged. - Change the OVS configuration for the physical port to a native VLAN mode. For example, the following sets up a bridge with port eth0 in "native-tagged" mode in VLAN 9:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 tag=9 vlan_mode=native-tagged In this situation, "native-untagged" mode will probably work equally well. Refer to the documentation for the Port table in ovs-vswitchd.conf.db(5) for more information. Q: I added a pair of VMs on different VLANs, like this:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 tag=9 $ ovs-vsctl add-port br0 tap1 tag=10 but the VMs can't access each other, the external network, or the Internet. A: It is to be expected that the VMs can't access each other. VLANs are a means to partition a network. When you configured tap0 and tap1 as access ports for different VLANs, you indicated that they should be isolated from each other. As for the external network and the Internet, it seems likely that the machines you are trying to access are not on VLAN 9 (or 10) and that the Internet is not available on VLAN 9 (or 10). Q: I added a pair of VMs on the same VLAN, like this:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 tag=9 $ ovs-vsctl add-port br0 tap1 tag=9 The VMs can access each other, but not the external network or the Internet. A: It seems likely that the machines you are trying to access in the external network are not on VLAN 9 and that the Internet is not available on VLAN 9. Also, ensure VLAN 9 is set up as an allowed trunk VLAN on the upstream switch port to which eth0 is connected. Q: Can I configure an IP address on a VLAN? A: Yes. Use an "internal port" configured as an access port. For example, the following configures IP address 192.168.0.7 on VLAN 9. That is, OVS will forward packets from eth0 to 192.168.0.7 only if they have an 802.1Q header with VLAN 9. Conversely, traffic forwarded from 192.168.0.7 to eth0 will be tagged with an 802.1Q header with VLAN 9:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 vlan9 tag=9 \ -- set interface vlan9 type=internal $ ip addr add 192.168.0.7/24 dev vlan9 $ ip link set vlan9 up See also the following question. Q: I configured one IP address on VLAN 0 and another on VLAN 9, like this:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ip addr add 192.168.0.5/24 dev br0 $ ip link set br0 up $ ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal $ ip addr add 192.168.0.9/24 dev vlan9 $ ip link set vlan9 up but other hosts that are only on VLAN 0 can reach the IP address configured on VLAN 9. What's going on? A: `RFC 1122 section 3.3.4.2 "Multihoming Requirements" `__ describes two approaches to IP address handling in Internet hosts: - In the "Strong ES Model", where an ES is a host ("End System"), an IP address is primarily associated with a particular interface. The host discards packets that arrive on interface A if they are destined for an IP address that is configured on interface B. The host never sends packets from interface A using a source address configured on interface B. - In the "Weak ES Model", an IP address is primarily associated with a host. The host accepts packets that arrive on any interface if they are destined for any of the host's IP addresses, even if the address is configured on some interface other than the one on which it arrived. The host does not restrict itself to sending packets from an IP address associated with the originating interface. Linux uses the weak ES model. That means that when packets destined to the VLAN 9 IP address arrive on eth0 and are bridged to br0, the kernel IP stack accepts them there for the VLAN 9 IP address, even though they were not received on vlan9, the network device for vlan9. To simulate the strong ES model on Linux, one may add iptables rule to filter packets based on source and destination address and adjust ARP configuration with sysctls. BSD uses the strong ES model. Q: My OpenFlow controller doesn't see the VLANs that I expect. A: The configuration for VLANs in the Open vSwitch database (e.g. via ovs-vsctl) only affects traffic that goes through Open vSwitch's implementation of the OpenFlow "normal switching" action. By default, when Open vSwitch isn't connected to a controller and nothing has been manually configured in the flow table, all traffic goes through the "normal switching" action. But, if you set up OpenFlow flows on your own, through a controller or using ovs-ofctl or through other means, then you have to implement VLAN handling yourself. You can use "normal switching" as a component of your OpenFlow actions, e.g. by putting "normal" into the lists of actions on ovs-ofctl or by outputting to OFPP_NORMAL from an OpenFlow controller. In situations where this is not suitable, you can implement VLAN handling yourself, e.g.: - If a packet comes in on an access port, and the flow table needs to send it out on a trunk port, then the flow can add the appropriate VLAN tag with the "mod_vlan_vid" action. - If a packet comes in on a trunk port, and the flow table needs to send it out on an access port, then the flow can strip the VLAN tag with the "strip_vlan" action. Q: I configured ports on a bridge as access ports with different VLAN tags, like this:: $ ovs-vsctl add-br br0 $ ovs-vsctl set-controller br0 tcp:192.168.0.10:6653 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 tap0 tag=9 $ ovs-vsctl add-port br0 tap1 tag=10 but the VMs running behind tap0 and tap1 can still communicate, that is, they are not isolated from each other even though they are on different VLANs. A: Do you have a controller configured on br0 (as the commands above do)? If so, then this is a variant on the previous question, "My OpenFlow controller doesn't see the VLANs that I expect," and you can refer to the answer there for more information. Q: How MAC learning works with VLANs? A: Open vSwitch implements Independent VLAN Learning (IVL) for ``OFPP_NORMAL`` action, e.g. it logically has separate learning tables for each VLANs. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/faq/vxlan.rst000066400000000000000000000042021514270232600242200ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====== VXLANs ====== Q: What's a VXLAN? A: VXLAN stands for Virtual eXtensible Local Area Network, and is a means to solve the scaling challenges of VLAN networks in a multi-tenant environment. VXLAN is an overlay network which transports an L2 network over an existing L3 network. For more information on VXLAN, please see `RFC 7348 `__. Q: How much of the VXLAN protocol does Open vSwitch currently support? A: Open vSwitch currently supports the framing format for packets on the wire. There is currently no support for the multicast aspects of VXLAN. To get around the lack of multicast support, it is possible to pre-provision MAC to IP address mappings either manually or from a controller. Q: What destination UDP port does the VXLAN implementation in Open vSwitch use? A: By default, Open vSwitch will use the assigned IANA port for VXLAN, which is 4789. However, it is possible to configure the destination UDP port manually on a per-VXLAN tunnel basis. An example of this configuration is provided below.:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan \ options:remote_ip=192.168.1.2 options:key=flow options:dst_port=8472 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/group-selection-method-property.txt000066400000000000000000000113711514270232600306140ustar00rootroot00000000000000Proposal for Group Selection Method Property Version: 0.0.3 Author: Simon Horman , et al. Initial Public Revision: September 2014 Contents ======== 1. Introduction 2. How it Works 3. Experimenter Id 4. Experimenter Messages 5. History 1. Introduction =============== This text describes a Netronome Extension to OpenFlow 1.5 that allows a controller to provide more information on the selection method for select groups. This proposal is in the form of an enhanced select group type. This may subsequently be proposed as an extension or update to the OpenFlow specification. 2. How it works =============== A new Netronome group experimenter property is defined which provides compatibility with the group mod message defined in Open Flow 1.5 (also known as ONF EXT-350) and allows parameters for the selection method of select groups to be passed by the controller. In particular it allows controllers to: * Specify the fields used for bucket selection by the select group. * Designate the selection method used. * Provide a non-field parameter to the selection method. 3. Experimenter ID ================== The Experimenter ID of this extension is: NTR_VENDOR_ID = 0x0000154d 4. Group Experimenter Property ============================== The following group property experimenter type defined by this extension. enum ntr_group_mod_subtype { NTRT_SELECTION_METHOD = 1, }; Modifications to the group table from the controller may be done with a OFPT_GROUP_MOD message described Open Flow 1.5. Group Entry Message. Of relevance here is that Open Flow 1.5 group messages have properties. This proposal is defined in terms of an implementation of struct ofp_group_prop_experimenter which is described in Open Flow 1.5. The implementation is: struct ntr_group_prop_selection_method { ovs_be16 type; /* OFPGPT_EXPERIMENTER. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* NTR_VENDOR_ID. */ ovs_be32 exp_type; /* NTRT_SELECTION_METHOD. */ ovs_be32 pad; char selection_method[NTR_MAX_SELECTION_METHOD_LEN]; /* Null-terminated */ ovs_be64 selection_method_param; /* Non-Field parameter for * bucket selection. */ /* Followed by: * - Exactly (length - 40) (possibly 0) bytes containing OXM TLVs, then * - Exactly ((length + 7)/8*8 - length) (between 0 and 7) bytes of * all-zero bytes * In summary, ntr_group_prop_selection_method is padded as needed, * to make its overall size a multiple of 8, to preserve alignment * in structures using it. */ /* uint8_t field_array[0]; */ /* Zero or more fields encoded as * OXM TLVs where the has_mask bit must * be zero and the value it specifies is * a mask to apply to packet fields and * then input them to the selection * method of a select group. */ /* uint8_t pad2[0]; */ }; OFP_ASSERT(sizeof(struct ntr_group_mod) == 40); This property may only be used with group mod messages whose: * command is OFPGC_ADD or OFPGC_MODIFY; and * type is OFPGT_SELECT The type field is the OFPGPT_EXPERIMENTER which is defined in EXT-350 as 0xffff. The experimenter field is the Experimenter ID (see 3). The exp_type field is NTRT_SELECTION_METHOD. The group selection_method is a null-terminated string which if non-zero length specifies a selection method known to an underlying layer of the switch. The value of NTR_MAX_SELECTION_METHOD_LEN is 16. The group selection_method may be zero-length to request compatibility with Open Flow 1.4. The selection_method_param provides a non-field parameter for the group selection_method. It must be all-zeros unless the group selection_method is non-zero length. The selection_method_param may for example be used as an initial value for the hash of a hash group selection method. The fields field is an ofp_match structure which includes the fields which should be used as inputs to bucket selection. ofp_match is described in Open Flow 1.4 section 7.2.2 Flow Match Structures. Fields must not be specified unless the group selection_method is non-zero length. The pre-requisites for fields specified must be satisfied in the match for any flow that uses the group. Masking is allowed but not required for fields whose TLVs allow masking. The fields may for example be used as the fields that are hashed by a hash group selection method. 5. History ========== This proposal has been developed independently of any similar work in this area. No such work is known. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/000077500000000000000000000000001514270232600227315ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/dpdk.rst000066400000000000000000000357301514270232600244150ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============================ Using Open vSwitch with DPDK ============================ This document describes how to use Open vSwitch with DPDK. .. important:: Using DPDK with OVS requires configuring OVS at build time to use the DPDK library. The version of DPDK that OVS supports varies from one OVS release to another, as described in the :doc:`releases FAQ `. For build instructions refer to :doc:`/intro/install/dpdk`. Ports and Bridges ----------------- ovs-vsctl can be used to set up bridges and other Open vSwitch features. Bridges should be created with a ``datapath_type=netdev``:: $ ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev ovs-vsctl can also be used to add DPDK devices. ovs-vswitchd should print the number of dpdk devices found in the log file:: $ ovs-vsctl add-port br0 dpdk-p0 -- set Interface dpdk-p0 type=dpdk \ options:dpdk-devargs=0000:01:00.0 $ ovs-vsctl add-port br0 dpdk-p1 -- set Interface dpdk-p1 type=dpdk \ options:dpdk-devargs=0000:01:00.1 Some NICs (i.e. Mellanox ConnectX-3) have only one PCI address associated with multiple ports. Using a PCI device like above won't work. Instead, below usage is suggested:: $ ovs-vsctl add-port br0 dpdk-p0 -- set Interface dpdk-p0 type=dpdk \ options:dpdk-devargs="class=eth,mac=00:11:22:33:44:55" $ ovs-vsctl add-port br0 dpdk-p1 -- set Interface dpdk-p1 type=dpdk \ options:dpdk-devargs="class=eth,mac=00:11:22:33:44:56" .. important:: Hotplugging physical interfaces is not supported using the above syntax. This is expected to change with the release of DPDK v18.05. For information on hotplugging physical interfaces, you should instead refer to :ref:`port-hotplug`. After the DPDK ports get added to switch, a polling thread continuously polls DPDK devices and consumes 100% of the core, as can be checked from ``top`` and ``ps`` commands:: $ top -H $ ps -eLo pid,psr,comm | grep pmd Creating bonds of DPDK interfaces is slightly different to creating bonds of system interfaces. For DPDK, the interface type and devargs must be explicitly set. For example:: $ ovs-vsctl add-bond br0 dpdkbond p0 p1 \ -- set Interface p0 type=dpdk options:dpdk-devargs=0000:01:00.0 \ -- set Interface p1 type=dpdk options:dpdk-devargs=0000:01:00.1 To stop ovs-vswitchd & delete bridge, run:: $ ovs-appctl -t ovs-vswitchd exit $ ovs-appctl -t ovsdb-server exit $ ovs-vsctl del-br br0 .. _dpdk-ovs-in-guest: OVS with DPDK Inside VMs ------------------------ Additional configuration is required if you want to run ovs-vswitchd with DPDK backend inside a QEMU virtual machine. ovs-vswitchd creates separate DPDK TX queues for each CPU core available. This operation fails inside QEMU virtual machine because, by default, VirtIO NIC provided to the guest is configured to support only single TX queue and single RX queue. To change this behavior, you need to turn on ``mq`` (multiqueue) property of all ``virtio-net-pci`` devices emulated by QEMU and used by DPDK. You may do it manually (by changing QEMU command line) or, if you use Libvirt, by adding the following string to ```` sections of all network devices used by DPDK:: where: ``N`` determines how many queues can be used by the guest. This requires QEMU >= 2.2. .. _dpdk-phy-phy: PHY-PHY ------- Add a userspace bridge and two ``dpdk`` (PHY) ports:: # Add userspace bridge $ ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev # Add two dpdk ports $ ovs-vsctl add-port br0 phy0 -- set Interface phy0 type=dpdk \ options:dpdk-devargs=0000:01:00.0 ofport_request=1 $ ovs-vsctl add-port br0 phy1 -- set Interface phy1 type=dpdk options:dpdk-devargs=0000:01:00.1 ofport_request=2 Add test flows to forward packets between DPDK port 0 and port 1:: # Clear current flows $ ovs-ofctl del-flows br0 # Add flows between port 1 (phy0) to port 2 (phy1) $ ovs-ofctl add-flow br0 in_port=1,action=output:2 $ ovs-ofctl add-flow br0 in_port=2,action=output:1 Transmit traffic into either port. You should see it returned via the other. .. _dpdk-vhost-loopback: PHY-VM-PHY (vHost Loopback) --------------------------- Add a userspace bridge, two ``dpdk`` (PHY) ports, and two ``dpdkvhostuserclient`` ports:: # Add userspace bridge $ ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev # Add two dpdk ports $ ovs-vsctl add-port br0 phy0 -- set Interface phy0 type=dpdk \ options:dpdk-devargs=0000:01:00.0 ofport_request=1 $ ovs-vsctl add-port br0 phy1 -- set Interface phy1 type=dpdk options:dpdk-devargs=0000:01:00.1 ofport_request=2 # Add two dpdkvhostuserclient ports $ ovs-vsctl add-port br0 dpdkvhostclient0 \ -- set Interface dpdkvhostclient0 type=dpdkvhostuserclient \ options:vhost-server-path=/tmp/dpdkvhostclient0 ofport_request=3 $ ovs-vsctl add-port br0 dpdkvhostclient1 \ -- set Interface dpdkvhostclient1 type=dpdkvhostuserclient \ options:vhost-server-path=/tmp/dpdkvhostclient1 ofport_request=4 Add test flows to forward packets between DPDK devices and VM ports:: # Clear current flows $ ovs-ofctl del-flows br0 # Add flows $ ovs-ofctl add-flow br0 in_port=1,action=output:3 $ ovs-ofctl add-flow br0 in_port=3,action=output:1 $ ovs-ofctl add-flow br0 in_port=4,action=output:2 $ ovs-ofctl add-flow br0 in_port=2,action=output:4 # Dump flows $ ovs-ofctl dump-flows br0 Create a VM using the following configuration: .. table:: ===================== ======== ============ Configuration Values Comments ===================== ======== ============ QEMU version 2.2.0 n/a QEMU thread affinity core 5 taskset 0x20 Memory 4GB n/a Cores 2 n/a Qcow2 image CentOS7 n/a mrg_rxbuf off n/a ===================== ======== ============ You can do this directly with QEMU via the ``qemu-system-x86_64`` application:: $ export VM_NAME=vhost-vm $ export GUEST_MEM=3072M $ export QCOW2_IMAGE=/root/CentOS7_x86_64.qcow2 $ export VHOST_SOCK_DIR=/tmp $ taskset 0x20 qemu-system-x86_64 -name $VM_NAME -cpu host -enable-kvm \ -m $GUEST_MEM -drive file=$QCOW2_IMAGE --nographic -snapshot \ -numa node,memdev=mem -mem-prealloc -smp sockets=1,cores=2 \ -object memory-backend-file,id=mem,size=$GUEST_MEM,mem-path=/dev/hugepages,share=on \ -chardev socket,id=char0,path=$VHOST_SOCK_DIR/dpdkvhostclient0,server \ -netdev type=vhost-user,id=mynet1,chardev=char0,vhostforce \ -device virtio-net-pci,mac=00:00:00:00:00:01,netdev=mynet1,mrg_rxbuf=off \ -chardev socket,id=char1,path=$VHOST_SOCK_DIR/dpdkvhostclient1,server \ -netdev type=vhost-user,id=mynet2,chardev=char1,vhostforce \ -device virtio-net-pci,mac=00:00:00:00:00:02,netdev=mynet2,mrg_rxbuf=off For a explanation of this command, along with alternative approaches such as booting the VM via libvirt, refer to :doc:`/topics/dpdk/vhost-user`. Once the guest is configured and booted, configure DPDK packet forwarding within the guest. To accomplish this, build the ``testpmd`` application as described in :ref:`dpdk-testpmd`. Once compiled, run the application:: $ cd $DPDK_DIR/app/test-pmd; $ ./testpmd -c 0x3 -n 4 --socket-mem 1024 -- \ --burst=64 -i --txqflags=0xf00 --disable-hw-vlan $ set fwd mac retry $ start When you finish testing, bind the vNICs back to kernel:: $ $DPDK_DIR/usertools/dpdk-devbind.py --bind=virtio-pci 0000:00:03.0 $ $DPDK_DIR/usertools/dpdk-devbind.py --bind=virtio-pci 0000:00:04.0 .. note:: Valid PCI IDs must be passed in above example. The PCI IDs can be retrieved like so:: $ $DPDK_DIR/usertools/dpdk-devbind.py --status More information on the dpdkvhostuserclient ports can be found in :doc:`/topics/dpdk/vhost-user`. PHY-VM-PHY (vHost Loopback) (Kernel Forwarding) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :ref:`dpdk-vhost-loopback` details steps for PHY-VM-PHY loopback testcase and packet forwarding using DPDK testpmd application in the Guest VM. For users wishing to do packet forwarding using kernel stack below, you need to run the below commands on the guest:: $ ip addr add 1.1.1.2/24 dev eth1 $ ip addr add 1.1.2.2/24 dev eth2 $ ip link set eth1 up $ ip link set eth2 up $ systemctl stop firewalld.service $ systemctl stop iptables.service $ sysctl -w net.ipv4.ip_forward=1 $ sysctl -w net.ipv4.conf.all.rp_filter=0 $ sysctl -w net.ipv4.conf.eth1.rp_filter=0 $ sysctl -w net.ipv4.conf.eth2.rp_filter=0 $ route add -net 1.1.2.0/24 eth2 $ route add -net 1.1.1.0/24 eth1 $ arp -s 1.1.2.99 DE:AD:BE:EF:CA:FE $ arp -s 1.1.1.99 DE:AD:BE:EF:CA:EE PHY-VM-PHY (vHost Multiqueue) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ vHost Multiqueue functionality can also be validated using the PHY-VM-PHY configuration. To begin, follow the steps described in :ref:`dpdk-phy-phy` to create and initialize the database, start ovs-vswitchd and add ``dpdk``-type devices to bridge ``br0``. Once complete, follow the below steps: 1. Configure PMD and RXQs. For example, set the number of dpdk port rx queues to at least 2 The number of rx queues at vhost-user interface gets automatically configured after virtio device connection and doesn't need manual configuration:: $ ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xc $ ovs-vsctl set Interface phy0 options:n_rxq=2 $ ovs-vsctl set Interface phy1 options:n_rxq=2 2. Instantiate Guest VM using QEMU cmdline We must configure with appropriate software versions to ensure this feature is supported. .. list-table:: VM Configuration :header-rows: 1 * - Setting - Value * - QEMU version - 2.5.0 * - QEMU thread affinity - 2 cores (taskset 0x30) * - Memory - 4 GB * - Cores - 2 * - Distro - Fedora 22 * - Multiqueue - Enabled To do this, instantiate the guest as follows:: $ export VM_NAME=vhost-vm $ export GUEST_MEM=4096M $ export QCOW2_IMAGE=/root/Fedora22_x86_64.qcow2 $ export VHOST_SOCK_DIR=/tmp $ taskset 0x30 qemu-system-x86_64 -cpu host -smp 2,cores=2 -m 4096M \ -drive file=$QCOW2_IMAGE --enable-kvm -name $VM_NAME \ -nographic -numa node,memdev=mem -mem-prealloc \ -object memory-backend-file,id=mem,size=$GUEST_MEM,mem-path=/dev/hugepages,share=on \ -chardev socket,id=char1,path=$VHOST_SOCK_DIR/dpdkvhostclient0,server \ -netdev type=vhost-user,id=mynet1,chardev=char1,vhostforce,queues=2 \ -device virtio-net-pci,mac=00:00:00:00:00:01,netdev=mynet1,mq=on,vectors=6 \ -chardev socket,id=char2,path=$VHOST_SOCK_DIR/dpdkvhostclient1,server \ -netdev type=vhost-user,id=mynet2,chardev=char2,vhostforce,queues=2 \ -device virtio-net-pci,mac=00:00:00:00:00:02,netdev=mynet2,mq=on,vectors=6 .. note:: Queue value above should match the queues configured in OVS, The vector value should be set to "number of queues x 2 + 2" 3. Configure the guest interface Assuming there are 2 interfaces in the guest named eth0, eth1 check the channel configuration and set the number of combined channels to 2 for virtio devices:: $ ethtool -l eth0 $ ethtool -L eth0 combined 2 $ ethtool -L eth1 combined 2 More information can be found in vHost walkthrough section. 4. Configure kernel packet forwarding Configure IP and enable interfaces:: $ ip addr add 5.5.5.1/24 dev eth0 $ ip addr add 90.90.90.1/24 dev eth1 $ ip link set eth0 up $ ip link set eth1 up Configure IP forwarding and add route entries:: $ sysctl -w net.ipv4.ip_forward=1 $ sysctl -w net.ipv4.conf.all.rp_filter=0 $ sysctl -w net.ipv4.conf.eth0.rp_filter=0 $ sysctl -w net.ipv4.conf.eth1.rp_filter=0 $ ip route add 2.1.1.0/24 dev eth1 $ route add default gw 2.1.1.2 eth1 $ route add default gw 90.90.90.90 eth1 $ arp -s 90.90.90.90 DE:AD:BE:EF:CA:FE $ arp -s 2.1.1.2 DE:AD:BE:EF:CA:FA Check traffic on multiple queues:: $ cat /proc/interrupts | grep virtio .. _dpdk-flow-hardware-offload: Flow Hardware Offload (Experimental) ------------------------------------ The flow hardware offload is disabled by default and can be enabled by:: $ ovs-vsctl set Open_vSwitch . other_config:hw-offload=true Matches and actions are programmed into HW to achieve full offload of the flow. If not all actions are supported, fallback to partial flow offload (offloading matches only). Moreover, it only works with PMD drivers that support the configured rte_flow actions. Partial flow offload requires support of "MARK + RSS" actions. Full hardware offload requires support of the actions listed below. The validated NICs are: - Mellanox (ConnectX-4, ConnectX-4 Lx, ConnectX-5) - Napatech (NT200B01) Supported protocols for hardware offload matches are: - L2: Ethernet, VLAN - L3: IPv4, IPv6 - L4: TCP, UDP, SCTP, ICMP Supported actions for hardware offload are: - Output. - Drop. - Modification of Ethernet (mod_dl_src/mod_dl_dst). - Modification of IPv4 (mod_nw_src/mod_nw_dst/mod_nw_ttl). - Modification of TCP/UDP (mod_tp_src/mod_tp_dst). - VLAN Push/Pop (push_vlan/pop_vlan). - Modification of IPv6 (set_field:->ipv6_src/ipv6_dst/mod_nw_ttl). - Clone/output (tnl_push/push_vlan/output) for encapsulating over a tunnel. - Tunnel pop, for packets received on physical ports. .. note:: Tunnel offloads are experimental APIs in DPDK. In order to enable it, compile with -DALLOW_EXPERIMENTAL_API. Multiprocess ------------ This DPDK feature is not supported and disabled during OVS initialization. Further Reading --------------- More detailed information can be found in the :doc:`DPDK topics section ` of the documentation. These guides are listed below. .. NOTE(stephenfin): Remember to keep this in sync with topics/dpdk/index .. include:: ../topics/dpdk/index.rst :start-line: 30 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/index.rst000066400000000000000000000024071514270232600245750ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============= How-to Guides ============= Answers to common "How do I?"-style questions. For more information on the topics covered herein, refer to :doc:`/topics/index`. OVS --- .. toctree:: :maxdepth: 2 kvm ipsec selinux libvirt ssl tunneling userspace-tunneling vlan qos vtep sflow dpdk tc-offload openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/ipsec.rst000066400000000000000000000206051514270232600245710ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================================= Encrypt Open vSwitch Tunnels with IPsec ======================================= This document gives detailed description on the OVS IPsec tunnel and its configuration modes. If you want to follow a step-by-step guide to run and test IPsec tunnel, please refer to :doc:`/tutorials/ipsec`. Overview -------- Why do encryption? ~~~~~~~~~~~~~~~~~~ OVS tunnel packets are transported from one machine to another. Along the path, the packets are processed by physical routers and physical switches. There are risks that these physical devices might read or write the contents of the tunnel packets. IPsec encrypts IP payload and prevents the malicious party sniffing or manipulating the tunnel traffic. OVS IPsec ~~~~~~~~~ OVS IPsec aims to provide a simple interface for user to add encryption on OVS tunnels. It supports GRE, GENEVE, and VXLAN tunnels. The IPsec configuration is done by setting options of the tunnel interface and other_config of Open_vSwitch. You can choose different authentication methods and plaintext tunnel policies based on your requirements. OVS does not currently provide any support for IPsec encryption for traffic not encapsulated in a tunnel. Configuration ------------- Authentication Methods ~~~~~~~~~~~~~~~~~~~~~~ Hosts of the IPsec tunnel need to authenticate each other to build a secure channel. There are three authentication methods: 1) You can use a pre-shared key (PSK) to do authentication. In both hosts, set the same PSK value. This PSK is like your password. You should never reveal it to untrusted parties. This method is easier to use but less secure than the certificate-based methods:: $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:psk=swordfish 2) You can use a self-signed certificate to do authentication. In each host, generate a certificate and the paired private key. Copy the certificate of the remote host to the local host and configure the OVS as following:: $ ovs-vsctl set Open_vSwitch . \ other_config:certificate=/path/to/local_cert.pem \ other_config:private_key=/path/to/priv_key.pem $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:remote_cert=/path/to/remote_cert.pem `local_cert.pem` is the certificate of the local host. `priv_key.pem` is the private key of the local host. `priv_key.pem` needs to be stored in a secure location. `remote_cert.pem` is the certificate of the remote host. .. note:: OVS IPsec requires x.509 version 3 certificate with the subjectAltName DNS field setting the same string as the common name (CN) field. You can follow the tutorial in :doc:`/tutorials/ipsec` and use ovs-pki(8) to generate compatible certificate and key. (Before OVS version 2.10.90, ovs-pki(8) did not generate x.509 v3 certificates, so if your existing PKI was generated by an older version, it is not suitable for this purpose.) 3) You can also use CA-signed certificate to do authentication. First, you need to create a CA certificate and sign each host certificate with the CA key (please see :doc:`/tutorials/ipsec`). Copy the CA certificate to each host and configure the OVS as following:: $ ovs-vsctl set Open_vSwitch . \ other_config:certificate=/path/to/local_cert.pem \ other_config:private_key=/path/to/priv_key.pem \ other_config:ca_cert=/path/to/ca_cert.pem $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:remote_name=remote_cn `ca_cert.pem` is the CA certificate. You need to set `remote_cn` as the common name (CN) of the remote host's certificate so that only the certificate with the expected CN can be trusted in this connection. It is preferable to use this method than 2) if there are many remote hosts since you don't have to copy every remote certificate to the local host. .. note:: When using certificate-based authentication, you should not set psk in the interface options. When using psk-based authentication, you should not set certificate, private_key, ca_cert, remote_cert, and remote_name. Plaintext Policies ~~~~~~~~~~~~~~~~~~ When an IPsec tunnel is configured in this database, multiple independent components take responsibility for implementing it. ``ovs-vswitchd`` and its datapath handle packet forwarding to the tunnel and a separate daemon pushes the tunnel's IPsec policy configuration to the kernel or other entity that implements it. There is a race: if the former configuration completes before the latter, then packets sent by the local host over the tunnel can be transmitted in plaintext. Using this setting, OVS users can avoid this undesirable situation. 1) The default setting allows unencrypted packets to be sent before IPsec completes negotiation:: $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:psk=swordfish This setting should be used only and only if tunnel configuration is static and/or if there is firewall that can drop the plain packets that occasionally leak the tunnel unencrypted on OVSDB (re)configuration events. 2) Setiing ipsec_skb_mark drops unencrypted packets by using skb_mark of tunnel packets:: $ ovs-vsctl set Open_vSwitch . other_config:ipsec_skb_mark=0/1 $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:psk=swordfish OVS IPsec drops unencrypted packets which carry the same skb_mark as `ipsec_skb_mark`. By setting the ipsec_skb_mark as 0/1, OVS IPsec prevents all unencrypted tunnel packets leaving the host since the default skb_mark value for tunnel packets are 0. This affects all OVS tunnels including those without IPsec being set up. You can install OpenFlow rules to enable those non-IPsec tunnels by setting the skb_mark of the tunnel traffic as non-zero value. 3) Setting `ipsec_skb_mark` as 1/1 only drops tunnel packets with skb_mark value being 1:: $ ovs-vsctl set Open_vSwitch . other_config:ipsec_skb_mark=1/1 $ ovs-vsctl add-port br0 ipsec_gre0 -- \ set interface ipsec_gre0 type=gre \ options:remote_ip=2.2.2.2 \ options:psk=swordfish Opposite to 2), this setting passes through unencrypted tunnel packets by default. To drop unencrypted IPsec tunnel traffic, you need to explicitly set skb_mark to a non-zero value for those tunnel traffic by installing OpenFlow rules. Bug Reporting ------------- If you think you may have found a bug with security implications, like 1) IPsec protected tunnel accepted packets that came unencrypted; OR 2) IPsec protected tunnel allowed packets to leave unencrypted then please report such bugs according to :doc:`/internals/security`. If the bug does not have security implications, then report it according to instructions in :doc:`/internals/bugs`. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/kvm.rst000066400000000000000000000060651514270232600242670ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ===================== Open vSwitch with KVM ===================== This document describes how to use Open vSwitch with the Kernel-based Virtual Machine (KVM). .. note:: This document assumes that you have Open vSwitch set up on a Linux system. Setup ----- KVM uses tunctl to handle various bridging modes, which you can install with the Debian/Ubuntu package ``uml-utilities``:: $ apt-get install uml-utilities Next, you will need to modify or create custom versions of the ``qemu-ifup`` and ``qemu-ifdown`` scripts. In this guide, we'll create custom versions that make use of example Open vSwitch bridges that we'll describe in this guide. Create the following two files and store them in known locations. For example:: $ cat << 'EOF' > /etc/ovs-ifup #!/bin/sh switch='br0' ip link set $1 up ovs-vsctl add-port ${switch} $1 EOF :: $ cat << 'EOF' > /etc/ovs-ifdown #!/bin/sh switch='br0' ip addr flush dev $1 ip link set $1 down ovs-vsctl del-port ${switch} $1 EOF The basic usage of Open vSwitch is described at the end of :doc:`/intro/install/general`. If you haven't already, create a bridge named ``br0`` with the following command:: $ ovs-vsctl add-br br0 Then, add a port to the bridge for the NIC that you want your guests to communicate over (e.g. ``eth0``):: $ ovs-vsctl add-port br0 eth0 Refer to ovs-vsctl(8) for more details. Next, we'll start a guest that will use our ifup and ifdown scripts:: $ kvm -m 512 -net nic,macaddr=00:11:22:EE:EE:EE -net \ tap,script=/etc/ovs-ifup,downscript=/etc/ovs-ifdown -drive \ file=/path/to/disk-image,boot=on This will start the guest and associate a tap device with it. The ``ovs-ifup`` script will add a port on the br0 bridge so that the guest will be able to communicate over that bridge. To get some more information and for debugging you can use Open vSwitch utilities such as ovs-dpctl and ovs-ofctl, For example:: $ ovs-dpctl show $ ovs-ofctl show br0 You should see tap devices for each KVM guest added as ports to the bridge (e.g. tap0) Refer to ovs-dpctl(8) and ovs-ofctl(8) for more details. Bug Reporting ------------- Please report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/libvirt.rst000066400000000000000000000061551514270232600251450ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================= Open vSwitch with Libvirt ========================= This document describes how to use Open vSwitch with Libvirt 0.9.11 or later. This document assumes that you followed :doc:`/intro/install/general` or installed Open vSwitch from distribution packaging such as a .deb or .rpm. The Open vSwitch support is included by default in Libvirt 0.9.11. Consult www.libvirt.org for instructions on how to build the latest Libvirt, if your Linux distribution by default comes with an older Libvirt release. Limitations ----------- Currently there is no Open vSwitch support for networks that are managed by libvirt (e.g. NAT). As of now, only bridged networks are supported (those where the user has to manually create the bridge). Setup ----- First, create the Open vSwitch bridge by using the ovs-vsctl utility (this must be done with administrative privileges):: $ ovs-vsctl add-br ovsbr Once that is done, create a VM, if necessary, and edit its Domain XML file:: $ virsh edit Lookup in the Domain XML file the ```` section. There should be one such XML section for each interface the VM has::
And change it to something like this::
The interface type must be set to ``bridge``. The ```` XML element specifies to which bridge this interface will be attached to. The ```` element indicates that the bridge in ```` element is an Open vSwitch bridge. Then (re)start the VM and verify if the guest's vnet interface is attached to the ovsbr bridge:: $ ovs-vsctl show Troubleshooting --------------- If the VM does not want to start, then try to run the libvirtd process either from the terminal, so that all errors are printed in console, or inspect Libvirt/Open vSwitch log files for possible root cause. Bug Reporting ------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/qos.png000066400000000000000000002103511514270232600242430ustar00rootroot00000000000000PNG  IHDRZ)LiCCPICC ProfilexYy8߷{&spdvyscL)JATH*dhPJE$L}5|s~Zkλ:QÃaFBB"l(.;+2\ ϥub3d=#B|X+<" *B`}3G "6/A J()1^~XPPz^Hm.zT?:T߂D&6 مG#WcBzBhoXό7yr!_>Cd P;%C=-`-#[#c!(m37<=)_iWOi lg1"n3D0QDo>0`d[f 2ݞ9P \0DR?T"ABp2" `9燌) g߳Q9 K"sl󶭋tHל%FVvV_hy"ZB ;*h]6Z#0hkFߘx5G=r/ \_ Ca~Q]dHRLC%)r7m{mbOE$DE 8-$qEF⎾y^1dd@" gyԁ0f ď? ) iPJ@9.F @S`| ;؀ !2BB$@Z!dB.B( : P5t u@gkh B0 &07, *.l{a?x9p\Mp~ Ÿe@ѡXQ|() JerE"PI,T> UjEFQs54MFSRH^}$t6} ݄B?GЛ" #QØb1~XL&SƼLbcXVVkubc v38vñ$p8+pU:^y#WPT|;444BjV޴񴹴x^Sz|7~NΆ.. }15AOC&ׄ%"(L!9j=qL/MoJM~~AA!!C?#-0>#1&2L#YBH٤ Iɐɛ)8E 듽i r7y,l|2&,,q,E,YFYQ¬;wqdG+ll:l>lYl l/)A'؛ه9699899889p\\\ʹyùra 3K BѥS (]>.>h |O6ES*y  R:#+","$),<#&b* R+N(-OLVLE,HSqX\Q_H_P8'L#**Y&9$EҕfNnq9!+)(,[!VIL.UU| Aŝ;}vJK1SS񧒲RRҬr J}UA655%(F/RA53">T Z-RQm>mvoJi]1@:y=Yz+j(c,'L#FF~FF ƊM0&&'LLMLM̔ͺ v,-",ZwvR2Բ XZg}kcmSd3e+g{׎lnWc^>CC#j'N2ΉΏ\8\\Z\q˻ w=GqOƞ"{>pp vNupAQ=M==x}93{wO߬\~@abI`IJUPUVSpCMGPРЮ0g޷aQ ElbF.}ѢчbbbVccőBO'%\܏ﵿ߁c$Ϥ΃N&'_J^:hyMZu77fg7nv޸%}6;;w&]not|{ދ.'{z޽yڃU6?RzԧwO4+ KOM@!:22zBݬ*952_R^H }."/;&oШȥE^M_ּ }68*&gL-jw[umBluO9Ĝ\\#v9{%jgWyB,5$9CBCg^k̏:k/c?PbGR3))!i҇820?r=hvc999sN;yӉQg <:K:Ow~dɅke*Yp/K{i=}LxɻuN$={aRmP`8jηʆO$;fAq2:ztLl]sCK烾(/`뾦/y|]ڇ?~~.J5dbp4,4Ӵ=JlB ѝބADG$0ؕ99/p pPrB҈H!R8s\IJ9VSNNHsL[L'DQo@0ͨɬ9ւRJZFVN^AȑӉ8qq}uυn=u'nPKq=.ngwᇃ<{gyЇWLnnxadѵиЄdTYGO'^ϋI[jtkY{êڭ C/ }ϡ4蛘vBDk/u | $,+YYŐqG [2i˜\#Y>K0BKlb~ْW+dS|4BPU3Q8YU]s^7W/I?PHؘxƤY+%u6IjvW;:;;.㷗wqwSȣ#/?PD . q % 7هw/"%R+GŘx$RX $$DĊPbC_2oܿ@ AWo:8%5 >-}]]]ߵ3@r !7FΏ>ثw!!"4< &o",8, :~a)xï(~)*U@}B"=t(E,L V`7qʸeG_Jd Jg8|*6x^̐э9ɜԃ2=d Kf*"K+;;VJW88]\]I}~+JX!3aAHdTS $} edv(J(+ȩ hjjhы570Z76u42ii`uzVr:8oRwram}zto ]?! z_,o܋VIߧ\I<䙩s5]v\$gr韣*+뮨,t&ε^L#u&| F.ѩEɸc~g/8^+56zm2sG%ʲJٍ[?G@!'2F ;JRg@RCK#$ A{8(jD|&Ê3| (6*uՃZB H~>d6H.~ +Öcq{pŸa 7M5 m m F$? zWBI~yx2|Y>7Vp`óc?ȡɱɕmdzM)d.4*XMARDXHtLlCeE%Ies7h\Zs$5]< #yPZqحىvsqnuޝ>O _L f 3HHxqpCڙ=Y9Y,O_K[.}!"oܥ[5G, W0ilYl}VyPOiL}ܣ7~vyKWoᇛ{~0Y:<8dJXxSqр'A*MFi4^2E*BH ZY` >(JYP8:]p#=3eaOc_xq:REɣU=Bҕ@Rߘac`& 2yȭa,,.Aϱ9S[B);_.P/&@xHdV Pt:,}CfJKIηJ*wHZGt< LLͽ,,C&mmGœ7]AE{z;bn"ws;^5Y_I?-C]=1rtҳNjRŔ_)^VM,pscV6ۣw;iv?T|qY3#_x I ۏľx|p䳩ƙď/J_Vn.F}:Tj^} ~5uՍs?LRDDCʏ#[[KH$?Olmmmm,G ?Wl c{q6OO~{/ pHYs   IDATx `ս'%H  T/%(hA-hJ(p+>@U(TAKKl TP噄lMvNv7!@v(3gw>gfw9s&NF$@$@$@N <$@$@$@$y@$@$@$ iY   j$@$@$@@&je   9@$@$@$ iY   j$@$@$@@&je   9@$@$@$ iY   j$@$@$@@&je   9@$@$@$ iY   j$@$@$@@&je   9@$@$@$ iY   j$@$@$@@&je   $ %PqoWL{OPŴ:﹬3؏8wԮꈏ[_[r7V~gF!>.7# :T[u礘AY 鋶m5XYSsinƶwǩ#ۺv;}IUysҗm]o%  `?MYjhK=-Qj} NnhE6ilW<2dJ䑖:d OK($@$@fOcANf7[#0lz[1mccQik' l{i'M4f̤uyVs\b]7l8HHHҸTzCL1'U[v&l_nJpƚ}/_7ާÑ5=GҬN> T&F|W{$E[_[$L$  @Ms$j̙GqX{oLIKOrrjc<<Ihd=g᭥W 4G/#F&uan$  Xb>wBBd앪eܗ&Ǥ+&ɂ-g>ۙRG_5;_ַONJKsIMO`*GK$@$@|Fȡ7 Sp\,qL>u=Gmq)Djq;:ǷmGſ$@$@ @M -G [6m0MYbz;L tLMكqc["欜>"eԍyFHHZ[̸wȡlKf;:fRb-3yجjDM:kQ2dxC^ $@$@-B4-ݷV֛[Q]钽ٸRRx:RvWذ*qm~N/z7c0 qɎRLcg]11s|[ݹ}R-2a_}|C֤QM w?Փ5jGS[O,/ \ jaGL]J6mP⤙ NsBgƣ>ME!};Ǐk׭X0[)cpL~3=1lV{!Xoijl+gZ"<†HqSHHJiJ*™F]N^=e}Byx9 K\ڴwً3ީ76.Ƹc7R[YQG?RBE4 OJ% 짹XԢRS!Y9droOY#] wZxʞXZm_h,C2rN DNj?w\{͡LGtt 4@X]]]1H a "=11-Vh\$.P' m& %@MuGIHH8dA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @ ܺ$@$@$@N4Nt .j;ZN$@$@$$@MdA @[`4@XX "Q"H  qƇz ZaI+#G1|fG$\ +JG4ͅ @EDDsHH ƂZ~i 4tϔDGGCְ92  _'i$x5Ӡ4⚑0"4(9i*++^yU@ f!5qތ %kpB4KLH|L4 n k p.q3IrUV]]mNםq;t @CNPUhsk]f,t%UAS^^nN>t @GM/..]vQQQ|#}H@` }QHH? iݺ5~i4UG&22aoΞ2eJaa׌$&&{"LH|F 2kJF555hϺw.!4111MIbl+"`A1= ~7 Э['N\l%\xȚ'OFFFHEȅpuDIߵj儦$ DMJE6X9|;wUQ0G h{I2&,UDj1(tĚЬhUE ,4z}ӢCL\XyAkW?| X?Tl3|ƣJ_4M$@$p8Ӡ)EW%C 2oC[H2( kUW푦ԫY\Zviq1VD ¤$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$@MsHHH PC- $@$@$@4<HHH5M0"@$@$@$IJ k~cĴ{flNu[8\u^zj4vvz}b4\5ey/K`=Sv5GNxN#|}ZIO)%"B+++}d% ?$@Ms6mkm+F{5\ߎ9{brBC%S?,ݮ[GF^#ۣۧ)mhD{|V~'k@ط:4_S= ^SviJ jaIHi.xEbCapG:ž= SSSSi(: tjTѲ[s߁BwW_?!g X7|I[,9 ,j_>驉4a}dx2meg?RߏOd״CM+uTZ.9U/zh"('ϸ j˪C_>c$ܳcJ)C!iʎBy*0a"\yВ8:V;Ef8ua-Tу;5tjb *j[]ƨCtGxڳ5'_)jץSijVx6-Q}Zfєl( A$@$Ul>/_pvFZzoԜteb~;f 0"AKd4u;m0­2b4*&|}bM\>7Ȭ |Vs319ءٟ@m B$@$M4@2Q8k:h-xd>%xG Q;Ѧ#'d%f0(TIEq_Ze9 U,7 jfç#=}tc*:|ksmx}0x:azW5rS=ЏHZJ(-cEZɑ=^査xWu,Wncs2»'E#JcT'!-ސ_'a^N[nhy |[5Z9Z8׹8HZ5MP߼?{t,+^4ZBv7:iwh?S{*Ť[O-kۛn_lR`*ެ* *Q?QZ}<{sۛn^? HHHcO䩽e}]ThQفSi[Sn#m"yj+ċQ[XXن+! 4תX j˫DVϟ'ƄUվf;Ra3뒢2aP\}WWLߍj.3U^Ɍ" @MӜX^a=A}Ac֬rT t4gȬ % "NHHB5MV= N$@$@AE&!  %@MUς @P daHHH d Pӄlճ$@$@$Ti:Y  Y4![,8 jNHHB5MV= N$@$@AE&!  %@MUς @P daHHH d Pӄlճ$@$@$Ti:Y  Y4![,8 jNHHB5MV= N$@$@AE&!  %@MUς @P daHHH d Pӄlճ$@$@$Ti:Y  Y4![,8 jNHHB5MV= N$@$@AE&!  %@MUς @P daHHH d Dl)x]mon$+g :z ~   iAVڵkw٠?/ԩS~n囇-**a ~Nzbeobt;c 񃻺\g㪿lq܀:ǓOٝo|7.bot *Wۉ:؁sNxSO-ZtVWW1}ѿ=Ν K0[*bΜ9/BAA%pQQZ"ڷo?{lj sa^sbKcX޴Q@ڶN?@g,@ֵwдcGOj0IfUȊS\if<04MS Svj1bu]W^^^SSPkƋ󮭭Eo;#?я&M]\Br!222&&)v1 x=ۆ vWWTTl6To?O~⃊@( Wv(e`Rܗ=~Ѽ4 Ļn3(^zBimLӘ򊏶4[^ܹ.2P NM4ژ6m@j`ʫ4/,,\~e|SRR`6,ĐsV68i"N> 8ƍ@\~xnIif(H7~1[1t)fٴhgTڅ!M5M6DoK*z8`ųg#fOBjX{kȸ:B# Ú~z<4hciڶmkXiЎW^`lSbB(j}iRޮ_{,PP6~XV}uAc[~Ǽgi>n؃ )o~mҗe;eLY܇vgj+oykqI˜'4Myy9D2g< s'?C#_}sVcƟO~82Ogg rO/ӳV+_OB+޿yKKԻ<--c8TL8Hm 췏\,I\lCSn}_e>e˿97Nn[ϭzR۸i 9"i'\zZ{ٗk=}eЀ"rm*ۼ9HK|pSSaZ@վKf ޽؆СC:tҼ` B7dw}wf^Ç`ٿ 6,lpW+?0a0(Z꘣tfw\hfBǽgŤiY(oZƔ!J{?#LCfZ$mkbƒ|fwe* ֵSc& SUka2V{=+ӦQ3;ؐ!;W2{?͡XT>KAb1euɊHϞZ]Pii创) l#)/s IDATB1YiKv=6,H֝buisߝp?YN6v3v;`ubæ0fnj5^>o|pg{hanB@)ϐەA;{)EH3 .t4qwqG֭6Ў"URUR|oW"܅h~"\pY&߸ؕ0xMzh'`1]ޚ?Lvҧ̿GѴF 69vJ*pDJϘ2 ymKb|k)ѓ9gd={'##t=ITh4po4[ִaW2o54ɞ,SM~7M y͑Ν{ M a=-sѻ0gpF(ޚi4iZRh'<|wIֿME#OUhp V.؅A^`?V^-W^x1CjJŗX0GPb]ߖ/_.3mIst.K6.ϔJX] ]p*ڲᑙ[Q:þ~ZƢ-¡drGm-KfdYH@)NI3Jv>/HI5vLG`Oͳd6=)شf͎}6n'^ MYMWm;Rt|206smiٖ6e}X-]Y]Qrp#J*JKlSCɰ㏝UڌչF*ۜ2ódƞS}ǫ/0tz$m< G|FFwy2dz~O^Brpz~gϝ;`p{ɚ!A4&醇e]Cmr L?RUDeީ]Z߳gZ{>8WeƆ>뿞:k\iq' .ƻ~z^F/|TKӿkܥugnx?0bۻN[&松g`y[wMH:u#U#{$$ hzĦ0%S{$%踎-Z|vqq X~S~-;QF"O}v#s庳ı3aԥť5/.R@xٲesBv[Ν%o7{p`C`l>,IJXLQߌ3sÅlzV*1<,w Tkq==`eմʆ_|ߐ}4uztSY;vֹcyGy߭(&otci5r$c̡nSP쩛سo-bu4#F88QoUD,8{8ۄjǨ)>ݷ(ӑdQ/n9==R1'q^vȺz;ӁJG&qf;f#mcFh(k͚cӴ0VrW꙱^0\&jip]`UGy+k\Mi\\V{ hؔ6Ň^K,%q ;9s&~G-^]|ArqLo-^-SBmSǕ̞ɘzߑ#4-Ϭwٱj!gz J0flqu9EHr7K߽x7$9nCER[W*~]&%~e>#g.ڱaI[`$pq_Z3xx i̼P[f--5fV6cbIqq3*ݾ}?Z܄MSNխP5{֎61W:Z {M&&:DR4tIG#X-EzX';bLc%o 59-r:sZd|8y0U!.L F{B OLX B,7Y*-4XR;P/y= ^'u(xs_Fdסjk&} Xto[7NE^kf_rQC0f7֭]Ljٳ6ȉ#!#Nz=KWkKfع+;ͼ#m&ӵ90)e ijGL] J4sܩ{NXxt֧Ȟ* |ɞWռUc^)<DŽȵOq͞rɫL>9wtYϔo}i4yG<#ӆݥ9>bg1޷r֠K/ةs%6Nٲ>#̩w9"g̥y}{%Lt`pA7EVʇ6+t4yZ!O\C9KBhqNVo?<@&9A\%71LX}^qn0M@ 9N=XN\L7Y'z 5;rW -G_&~-qA8#M#M\Qig12f,md8 GMSS:|M쓋/LmrX08e2i;5:]GYԔCxt쪂4P)X癑Wz evUYɆ92 L/^><&`F0ԍ_ A߀V V0I"[u2&h/O%@Ȩ|"0Wo#$!{G͏1:>؈ AMG$@$iZuPZfL$@$@AAJ~S+M7Fѐ"T6 B: E js>$@$@Ө;8\({#K0&FT`0'SQQl\MC%yIDl$"5Sw ]r4gK2r^~8SݳCx)3=$lVkeIH#kZcӨk "n9{r|\S"J` §`sDIołrv£X]Uxr# &࿚Fn={fvEk~q޽;n.q4% _|96!!%ر9JxqX[nA,b*.c.f(Мt^utcӍ?>KR\CF%Z8T|QF{=)imCF/!es wqUW]Ol*" 7|#/q!DFxeMA{/tTDLz|NڶmrT%J~afdǎb$;v'6sjsڴiiMa$X{キuVsH >b#C..N)Sf:Dv_|Utqè^{ꫯ\o}J}4!  #৚F^G0M#فm3~mQ/Dc""6D )2d,.v:P@(Hd'6 R{שS'heCv(> 2ѰBHHv(8 6|/++[Rܑ, _(:t(Td$C]8d†(a-\>L̡Hjy|7VŸqG!{=E@C#WPwx$b8\""}4QH !$R&MDDv#+a V!e _v8Qff&x) " m28;1ʀ(R0„%hݕ?!"[ "FP JV16 =4Q4v"%1Nd]svcvA(JёN,s 2}?.;~һwo9D`vXN4B: vII!""` 9YhNqE2.$̳q>d 0bIH QX17qCGFMp+pG[ ,/T^p AAccno= G"("64*;EHyP l$>pr )#Yd'$ QY'a48*ƘIDKA3aC$b(ᐌ`AxKp# (;q|$"HH$CtE`Xc(E@xI  D\J8^@!;dKT^!MXtrTQ;]JƬ Q"P%`!#Ba! BC/ (?42ʸe.=7kܲeK†8 A8D7t)BFb! Br!;il9Yy7) " 6)M)(9kIH S}w^pk8PՀCl(JGrT-\Sb! b1J,hes)D&=*CM #tODiʙ &%.=GGT*s@ u>[K  iDsUKorn$!_FTya@Uw=uO>$@$@$$ OØg,G3cOޫAy srr\`B o@HH PӸ3H|C˦y睘ʠLq}뭷w:kB8QlF_,k>C?q>!E' #@M @A@۾}'ZpO?4I00+CPK;V6SS:/M TzXb 04>0+7glٲ'OR5mR(CՂc41d^{ ,b>cf'.Z꫑]K.ϴg1>O2AhJ`$@$4^(^5~ӟC>6v+=\&PEEwƌBX}S}H=cRY a.3SF' j@׆A%v0C!W5q 즦;{lJQ1 U㓥̙3:7n\e=Ԛ7`$@$@ [7zH)VCS뮻%У>咱0t]m".idfLoBS'O:BK3IH PD@6}tH7xM7Aw t4#tàt2qDs?:tCet 3jZGl+W{]wIP0< 6 )j!\Pw^4ʻCj-0q gW7RgfhMe2SNݺukQQ n.t6 5kX|BgΜ9x5WL' ୷@>ܹs甔PQ;}5d;vb T&p3TWWeg:Lox~j ! ל"t,|"c̘1ϟoYK; \X~Μ9lHOmB{"- *СC^Bl_u9_g$+/nذzdv<_zL & ο6/,K"pցjA*AF&;36&dddlذoa$pA XspMy#iTѶ.v$O67X[᭢G\%c2RT:q/[ '9_!Uq9;XEl'm# 8{,d^̯\0V@ѫObhݫm5UϖЕD-)g|nj{lXq%ԄX' 'G}O^Z"֘79@Sअ)++3 5r׺׆MWHKPŅx,J s% ?Q\ A>hL~V5 -#h*0 5T p!38EOゅ$@$@@ǘzǸCA@aNX«L+ @S@UizƔV{sVNn@4Ʀgd>KAS, @ oHP|bQCeUD|C4ͺgEL4gHH.Ǟ.VSߢ^#)&}S ZuRѦBoG Xެ51g A!t>,* @i;HN_px\a11o)?='MwpsHk cǛ2Nt @ iZJ|qc?f͚ՋH3eoڒێ-3Z$cy kZ5var7Y`3HxG3R˹/m }>A -sapHӾj鴌Ep^5T'WYn9`kˍHHp>M3I[8TfD& ?^Of\Ω0Z>wI~ kOqXMo д'椤DFk+&#(_w}ۆtDCAO?Lntك$ּd HiFڴq (޹y֭׭eAc;gk+XӪ9rӎ(1B4yX$i<2{]K07Jϑ`:oZq{5u'ԁ!hlG7{m ]G>7_]dIH +,wQi5X+#Tʲys{Jk>׮sKe0Be(yp[Mފ,Qmꄘ5{T-E[vx#;ܣ `ZjޜE$@$iRREXM g-^){*cJxe#'}Z:WfGD6gL,t] \(^ZҧܕޣS[!.IH[{ Mz{:4hI|uJCfr*ziՕ$P[cbE }նU^t_A M޿cX,(+Rnڧ@[QK8eJ&LVuL^RQr9ks6d?Xkk`HdzqL;&&_:IH7wD $xQ6wȰa#F [\cKS{;N=V3%\ss:pڃ7E_WQ0YhYC&ꇊ7t;lذQ%/GK$@$i?ݣu ߶RI OFo3l!YwQ=|З ʕd/6|dKC'c[0th2(82!MmmE6-0cMSώ%  @&~殽9ǃ*+ٿXL5oyhz⍹/6 [7,/CK2,-, 棺  /X"I8{ pc`K_\kak{Q30 G4v,hW"##X:EZl02KmmX)QQQ_ZkYo9>>J كlڂ Y4{MtԪUqb4KUW4Hʿk׮=y$,;Ć  (l|s @cit:ƠIkݺ5vcbbjjj5F宪*//OJJ*|4?o#F@Hl͔6Uʋu[MEĞXf5Do;SR| 9m] K rC5K( i3VnCҟH5^Ҧ≹M 4&|C\$ ,j{K mL4~nAXVu֩ݻwGf1?CQ3gΔgzrҬZM(B VTT4iҤ?Xx-++SSNR񙦁%~_H5ȆU5u>WOWCedd̙3=5[l9dߌEF6Uxx3GRu裏IMM5jԚ5kTك0Yl0@{W]XO$@&@MSp1z@Sگ_?8woE/BvvvΝǏ=`>MBBZhizNd[oѣG_s5Ǭݻ7|W\KMB #sEuN>㏣?˗! V):Mqe˖aZҠA6lg!ǔ>}Hɐ2K^ &K$@> @M(z b*h}Çcn ƧwfaAà^~~>d"J/ӈ B$2ԃ],(pAIx5PӴp]4 :qĝw9yd+**Nt,8p"hDΛ73Qx4x n EVcL_a: JKK\~ D`x D}_hp>M^KPӄO:ᒟ2~aa!` (  r7E߿zjl}Rs[cP͝;o90.\+6C5)) 2 2 .vetha5iuz .%\\xNPw)YWp_."]=5RM/O3X4hh"NjN4qU&=^?ƊW^y%;PM;<׈KJv[n4V7ܦ$0$:RH_ |P|ܠ5VH\;a(ؠohq:F{iB`.rx"Ջ94+o!O*15>(fսKX! h!BaR0A`#PCAR&6 GB# c ?V*Dt Ra=jWґ+5d >p܁'o}™(3i\xAo[FAx%/XW5{'֯_|  Yaf 0dկ~5MA E߀5Hh& x."ts3g?xS-@vš⟣ob!!~:+)j%r1Cp҅ʁ1]l7tT_5 w&0͘1gϞ2؀Fo(5@tgϞuTZRwkjf0NC>2bDNbLFol_qm>!wl7_P8P|U5y X1c5?xjرRIMM{~)u 9vw A "jFP*[{:!$nn6f"#JUqB2f>dnR9})f D _<`=9"!E4~]ݸV՘F0V2eʔoFC#x¥.m_ćtP~P%7f͚t4|0u^l>4Y@L~a~qq1fM[(]|t1_Y 6=;&;v' P{u5x뮃â5Q{h`m=:'dv0VՃ4:[K `p‚oE1oO 3AxO?lTr Ӡ ;6a{j A4P鸆6c 1c /Э[7' U"h[~c50<~n$.< Vx8a cJ38A%#,:^'Bຓ ~¢TDrdd .8&0*6M0t"6^YcOo^~{pxJ{gvڅK@`:F㝜* 6\;x):[TUY4_Lv5zxCH,| '}VkUuUue%@K+ fG۝q!{̢,qv7 sT +\d{ZQ^z ?H$L6mڵm߾CǎIW\ѩ]tB%?fq! YcBwHp=BUX\h65;-- O? 7V׋XE|k?8͞=K.x^P\>d\ϡ)( F k (?{cyy;|7*Q,ORneIl!CN:}[=z\s5rƗz*_`WXq+:DGH ꖋzxcyŸ'NiӦk6z_q Ka9jtܸqrRhp $+CICclqz6 ׻C_#%^ (-.:;c?8ps171l}wC[lxB֭ .p'F6UV(Fpc!:B5MU5hE` ?ƺ|7o}7y ^Ÿᆸc̒F<Ȟ}Y44aɰE@ ѠF4~تNv֭~}yLsI#-ԛ ou%^ƻXU q9l|MNNC4,?*5KKM.W\_U-/޴w={`wߕ 6h)>6 #q赖Q'1c( ཱྀ]̡.>utbbJVWZt/?ّԌ ''njj fwjՃJqΟ/mY8pwuf vM`D(\8<IhK1j@\\!kDOw}!ܼ+xw@-aFX`އÆ Ll|y $t @ʜ?]T\q ePon]P3 b§ ܒ"£:]ӄ˝G/ƄȨػF9ڷwg0Wk"ZEFhJ۹ **Gmݺuڵ[OLLRY~]T1:>i '@Md o\4h?'WZյk9s .dæK~_:v_}x /oCwӠt hfbMɹ]SdUg H4Ȕ*k϶Χw}j7DEGAR5Zmzz޽zC{UIIUµ\"DAF$&/-9'NB|gpcl}ɸE06evS`St7t/nVx h_׸Q*/r|:h ʕc!Ƙ0@ujC.:&0:ŹkpTQZG?odY ,MciSsl5u74{jjll wֵx C_uuU5u6(\銁7 a={] [>bŊ%K0h)]p}a` {.OQ mEzkp cçA_W(koV@(NSDp¢F4r}p rB? $<VS5E脱';g8/~Arw+#",Ȋ%׿.2;<iixWf/kӠw! !Sila#/8iPJN))  |/СC·i `7>.slƞp7@yȧ# 8O.y_\VE^%֪zc~)cVe%Ui#n?\ң<~/}Th WWV*UT6P4(_|Bҽːe[XcqFtёcvO!51.Ƃ~x!Ф @ r@1 +}3>l[v4q;AL|dD1س_(!{v?}Ά 4Xz=VtAvXXq IDAT1V׭k|BB6qw7a{=OJrr'_CCWO;Oj%U??Ӊu +WR@n(Yc* 6(>ϗW~dOm8ܾ~?aNSO]xhx󛯏tKWW6x׆7a9>3IڄhQKcT?/WemUQÓWi[t0[ F,Waze0FSTW{2 |bjgr0үjfTj7+ð$04hh*18_cbbw2wO<%Q%ƭÆ*h7w3;AD 5 NtlyK~orMވHH@BHZjXL~9x̙s_ӪUdC }~߁=b5=QSx|7oU=Sb ,=w"u8c+[GAT/\k\ҟ_h*__;PwIԟ). \,)>[< C N1\FDa޵>{]1!E*1!9&6o\C8 aD>5[ְ Z/P+X8I]m]:`Maaqdx$tv (z @O5dbf0-;-g09M۷ϯ˽ǂ:<]pw''NnOj/=t\Qi1p)>[uhKLT+ @  7*zx]0vHt H3EJ%n8 CDC c(L;[\ k4"94n~=0CSnV7M8AU55u,)c *i0fomMeM+gVizs{uCڵKsU8c/ \rE6dbvHlAI 4 u<֦:?%pP(u|Zm|.k۵q7}9[Jͯ1ª*2}uo] n(wul4/,=EhӿO\CƉލz9}c),ooDAQ뇏gMLH+x!7^K#=22̺Pa0`/>kO$⦶ZUswa7i'6sN[!9>/Z\(I0Y4 v뗈{F 4 lܸTV >NLttNM}|\ŵݾZiՋ, lcqM IB!H!(^MlM \1reVˮofVWj\s|3s7眙Y6$Q>{x `;ݙT ܃UYcSR/Wfţ:l̉&p xXWCi@;Gf$4l=zqqfcv `Upbb[7ƶ%wd%J|$%'(}~"qb<)y;ݒZBoxӠ9٤Z#lQ> ZG|F. //)XM?H~>1p"^xSf5NB{/j96?f,0+k, -Yl5$*zda g0aVQ040vHI?rtɉXR܅s /p;Y >}͎_zK.D>k81qٙ] 7|s޼yl:58?3G*~iS,C 04hP;eJTt!V8'mHO8Հu!D*xgy @* >Zǰɮ$'aIYYUZ\{^S(mj;Nk^3l?^λ8p9J \!pϞ=6l;h2'Wܰ,@Jq iD[ofIуN:#}S|_hnnEYMf+)} WBҩg [ $(i)Y(wMD\do zprha&3N\gĔ4k {w:jD!|'+VHHHF WƒY(o7= q K].t,]49E'a;AOv[}DQ 1J\}aC/Sd7 ELFϝS|]/$caY%^SI@,#*320}=.ڷ\z,dw kP4'^>!)l.pDVe F F[[O8^t.i`~CtmZx!f'CLlOF-l<KZZ%1%6MC` Ji0T84ګkJ$۷dV$0^D]?Az(z AFЫEAOY삛MqI“+ჂG@fƈ)^#`W]2f8&!dXJʃ9&䑪j:8?L:ccb :%|y`FPEe(A k['@s⌁-fBkT cQ}}͉ZתnVJ{9L8X|(i\֫T&F\6.ps1) ݞ=i*o6 NtNyeo=gNwqbg+:@?Cp4X[B;_w0&#@0Υ `,܋YiԺRK8{˕DB`;[U!ߙzVU:Ib6LP =yN5M9+ayѣsFf9|vRUjkfM:MB6p>= >'1)fݧ# N˄N S2UӀ H\zKĮBuNF+<5O- `)k`zTl-j7[:op!1f3οRĖtIsbi-o*UU9M')//R邲pL+pp0^\in3BV+H=+NWT"* cޖ*M?O01%)WOK3R{,MG~(|*%fn18{26i"wLxQB<ɝXnP:X㘑sqln:z)薰Pt|qkS=K(MΘsQF΂-O.b%":ɀ"猏 Cl(@4HR%`CN8뼌;_踗$_FhnEZLh2ω{fɓSjɘpΒv5Z%EM.@Rմ669a'F/փ݄nϞ4i,8wZyM<;!3.%^baXu b2[ 7 {T,/I@eт33%ɐ=%'W~ǴuMmR1jYJ)RJ؃A>Y n4ѩ{ t{8.DliWpGM39! O9k,EH+4?n ]H7t†Mحɭ2g@(nrR`1g. s 'uX4@!Icy1>qNT[4d 2EU*ܰA!B$+-SOPi4$ⴴ`sΟ?_=E8PXXػ6Sqުqsnw\t+\X;c]1`0l9P "۳uq*WarzO}2N,jw01 kib QLh~Xj 'D;ּɱ*)rXPc{jf Ƀp{*;Sk 2^ 2Z MM>~h6h(?hgO_}OJfRq՟zi/WQtt:4q 'jn)yJwOj%ĂBuƨ(ASlxg[@6&;]IgdrYV`;7Rp݋WAuazb CNqK2YWn|祚]:挍;R! ŝ{R=溤 ]cӢZn# 1쯹=:,00r.f?; CokR3飌FDjxdX[O)4 48Dz 75x*&:a♒1¦!ï:\bzcj\A:9jDa~sPDCჽܒlԕTkmnHIKro5fg= + IDAT݊>&m4%oӾp՗_,ɄH{𸬚bՖE!),5SJY# v8mW\厞&FP?.OAPQ{<6)seN<{yUvlV j}iw¹ E<KXfwZ;dkS+:!Y1x3 2zG5{v.##Ǧ'O-%{Ҭ@bX’gZ ^[wWg' ވ:e>yxLH6 e- B2T[ǒE^/ ͥyLh|2wZm*+&s(&NA?hR*QBr'&%% M=ꆪ!}3x$;ԾⷐkαƍV 4ޥI[ PIZ5)Rϟ[wv`6jl OMd{IXKck`?%hFO]ȷC^/pغ)jWFu:nkC)8^Gdx,y@C!D>p1aj`puYP2h0G.w[ǧy_=Mm;L]93&\-%,iѓO.;V):ytc:_1er)N~ѨMp׳l:f'Em{9L"Y7Gt8'iXjTNo۳M!B @`'͜I( &x"#'ND1,pYx)YEQAZ|ʶ@8M`+4ɋN쬪TAvW)a,䱊kFbVw|I揊dʊv}lGL,7cdYyr_%}yoqi䘆Y1Go5DQ}QIYisyP ;ۧBDQt4f"4Ɛy5:6CoP ]L;v,JJnn.kuiT~7]>//fA،*{ilL9celaC~ǏRn[8 ˇq %;k4M?8nae-pVcԗ4e`j9 $gtop-8':^ק)]w^Yb \g oָ raL!)ۣtf=匮a8J>Bb>v v|hFú$xF;\ث/^  PAbd*#0;yvxrtS۞9dLϖT.Z4ᢎ\%>-l^/==vWrgMaDc* 5OIش{˶W_wNwh+תԨNbR^TΝ+8 %Jq3b]:\H {|A8 ՗`_c^$7&=@$p061@!xwn5L60܌7vqi0Z|~ekj\ * qkӪbW>*]NLL\`/# 3 OȄ^8Mu=?kkh.Kݱ}$'PeY,wՑ/;:ht8/aOOޱ?ZS@TJF:]'3JɈ Z=5s-ӊ .{f_~'->cI8ǿ?|'o πƪ5zTpE'B=Eomb_> Fn" >9b18qIIB>+HaE`FkNd͚Ke]H>cƌiӦaNPg'2hB 4=gw/~'2Qr6Ԗwd*vc$+ǰn=ci:VBj嶞'+lZϖfre{*[li3p ~'U>NQr8,-є{/pԒ}"X[/?K6ՠTAl2u^CLB=ߝl*o4 Z=Sp- ^ \L&ϸ 44Lg<'ef͚w]z,w&44?mO~<>=뀼ѸvjwqDUwݙS'a,k1o /r2HHӴC!4Ugsy}v/]ҭ-2.nˎiPxTAF˘jwT/ζefSK[~6Ōxh-Β]f AIaJ'M(31-&]Idk) J㞘3=m,KRG%Ie/qa73<׏MOh̙l2,*(fq #0Nu]  l9uq@s/A?GDž0cKہ]]N穃56:,##uC~4 DKFb?|A:w<Y[N@9vo:ZlXI@G͜hɉ!ZT,B.* *fW-Akܹ٧2n_9݆vghرa]%Z0*ӧ/_d2q "}@QӫQ'L۾ii䷿-N0I|>̘lͨO@4&)@¯ S/;ݠZVxXarLeC=VTFu3LgiG|=DLUo:N8 1?cH -yN VaJ &F+paDS>D)&$Vg^.;) 4w}7T/FkDWp @hԹBwq|1 dmT4l ˎќ`>t0QNlfi$#U6b/,c1XMVSUՍ,1*L$s 4ʨUj02fl5}Q0!0x&6l6éml Ȼ6m}\r5T/~뭷i3[M"QزI4/nUڛ ^Ϙ|~W o9̄`g4(ѫu0]Gҗـ( SԻ댺6oXTan,31B%!V5tQXX5hP0 R)T4 *kr9hԽq0's"B1;/SjȳpÙm-D`Dn3[˥9.:K;Wa`r@XaBa=1aaAiDȄ}*bbK {<(8PeQ&LpM7Z ',3%^,G>L󷇮^6p4h8IR3JjMŠwV غh'K vD^}2\m}!3y1|eF=$#~B6:v/|H#ˌ ͙8}JP"f2\w's͛D+#*6:%e֡/^: j`4N5Xj0+- aˌ3rٽ{ƍpII (_ʞz96]p1Gf!LfD ]2N~ %twFVL\gT+v,J$(kSm߈fM:jE1dY79c{&9w)[RչR9RTx CLRIaB`pCCt05lF,Px@qWLb TFRX[j.sd~a׮]} g}"38 x=fQvd3،kJEC)`I08 :% bD2Yj[4YhUF]:G|B`"TkKūL>1d·I{ Se$5tt<lխJѻ3"],ёz̛Xb!GNF=HCCAрp_)mvVyR5V0'?~ɒ%O?|\YYyet s3.{\RR ,M=6^#ޞT@C ]yˆӠ.y@oDyjYm.եr~YB^@ nm6!3Ϟb6W\cV롭\̬1aiSal`ƈQ&hh[WP n1*eK]]csS0kaV,&A1qc) v0-15i3ZvvT-+/@hhhK2ڹhpIJNȄ f`a[pv[^Ae|\H/ 4/\a;N:]|9, :$zz&3a^d@rIuu_}14hHaTle^ﺜέ{n{ 2k!ar™M)):rZZdMM &CA033|> AeF(aW8 Kddf8sR`IO-[ 䃓#&'Ifpfww<70d[i`Lwȅ׵4['Dt|<Бp`|N#ZA?K lxdddNyA!\/t) 3BC>H !BF(Ў1ËG. F!9 rRԇL1(YEv/P*-糲F.WD!\" E AU΃΀&T5`380|MoA@ wA _,<peVo=`FQ8 *n@ hM(VS]Sn(88@tG8 ?0-Yor0b 3W1)+$k-HA4v*# o~Ty-"H ] 9iP9a㊶wYG]]x̎οPψ'`:G074@"Zeoو8#TA#lr*ʕ6;ˣ"ְe`7`,RPvA#Jk 犗t"Y/<sm{ {”ӈztWYo@Ɖ q jjģU nUQ0BXv(22r Q2qq6(ͅb(…03KpN""Ú4zEoڮ{â"p` x%FJ-CDPPEc'P8e}!o tpF>&NB @4ZfeC# ^/ ˠwyR*P-F"6#3 @\?[2^e4tA8 5~#   +AISkDBV5B "a883üY\oh'aFZ@P><3DĂH :1tD4#q2Aݡ@t~AS"FX #pл |1f&| x 7D1 V:+60s8ڴaxۆϖ P+Ԇ4=BC7zG@&᫧AKAX $8t%eaa'Pڪ8e6΀p^$x H_ ^:(Ïq<($# =n!UN* !@p+b, ?)9%zFQ[ IDAT&\ft V=BR+&keRn<  aO4}[ 8M` B $0 7i<(-,PP0Q7rFPXÉP@c`pbFF~!@_pQ4Xt]%B mp$&&Bv6c1w =`@pP?8&J3|>CM]4aV]j*!@`{IX[k((s / $ #@fR l Ѹ B"?³TDf !jzhM. A8MU`Tм(!@!q,X'= )i|z!@@8 "MO󞠧ADk +=O!O0? ]&BZ -a:T5XXgi¤ҩ!@iF/?| 7aa{ bP B 4A^#}A.a{"z?!@aq!*/WP!@@/WF=Q?-Y|FM8"Ee&B.HO3\H{i@\L2j(_޷o4裏"T*zXHUZO!B  Ny=Z~[z!97?_!@;iG2`*\py#G8qBW_E.ëWFN:!@ )iLEPT*x{"""PÇW_70BЄJE! z\NM njsM7a4oiP B` N3Dx@ւ p;vod0phtCaB @8Pi@T2Ѱ@%..nʕ: ¢AP! Biӌt pYf-X@n!%M-e D jL;v%X@_{bbb~ B#{6h0 г!@qЫK$X'mNz1H m-fs[tzK1"ҥ4*7ILk榢DniJtjsiXJ4w|Ry#V b4ÉH t͉_| evN;wo4:J-)=[t5''߱+R#|`}~Cq|oW8~x̩6њ઻8p /vF}:r!3\`b#/{u~~@@2zUޗ~HtXǵ6lk &G5XF1DAh ~Gq=OՆ q Ìqa|d^ MӌLV6+!Ў,.

ՇTh ^Ņ$\qq2ϟ%s"b##B ;1Cr /!lngG+LAGf3M{'/_Vs+~W2~$/3iPzXylncZĞj-zt&::b=o4ҢOn8 :!@NK藼Xc)"":B\`$-zA!N_$40Sxu;9wڧ%ꑵ4~e.|huׯ]#aO=CeWv=s[yO얓,4y1 eۼu#y;7G>gJ1u\^m\Ņ$ݴ?հKސ,ҴKnS! # ;5W(@V}]2mk.wыW\e4mX7f᎛}ז$̜_ycker"T&hxwâ4ID=B B Xׯ5]^L} lc?~Q'-ZlJ $ؓec9]^H|0-MәE4hPw>#Mі7~wϣ~_YubMpӶP-۶mՉgyșmÆ]E<kY˶68\Rp|D ~FsO70aWIu6o:p+0ص؝$ToO伂23K:c2 X(EU$@;n6Xlg%|FSrtKqKn5kߏ׉]|C&Ѱw.{]%t4>}*IJLdLIx#NJJNoB B xO1y,]X{x+n',[b%۹ْŋȋ)eFջ2B8ᑯR`vz~٩=kۥL.J;Y#eky!"_Ͻ-fJVQݼI4ZxAB HC+kL`HL?c&3khZ{RnTS ȮI4FaA&3,/2m/ҸE׏QsME+],8;\,.*bx+tڳSFB_$,0x(= )pUg6flH&0% miC `n6EԾV2:$n|&͘ ϙmNek6nK!qk/}%E I2m~~M{g/2 >}湒-1 L`XMH^aB Nӎdn=l|dO!`yQY_p?>b<y!;gqS+/roF_DWSW0G]u.D&)ӧɕ2O{=gHv65-Z{tD)!@4+uڕG=(e#/_ 9V WgxO K-nɮڸa5_ϼu=  :"@#ˋSYsc7Y=[^cjګκrów ç YoaK!@0/$,RcC`XpǕ/ʋr+˿xRV<[GA9WcZ=_FîW@Wtń0ܚ{ϫ@1(1k~gkM) ш_܄fGj'6}Fntj(z%w =gJyy\2)w9m:F6t 󔔻p՜ ֳ+ַ̉?lùVa-qs}'ߍ0q5m p#@fm]ߛYfMu9>|FFIAFBv*B_#n> sr :"@x/B BNgQ B B#a{x"zjmcMN4 | h h&鮻j<>]h 4(Wpѥsi`%ˤC.k`~GՁ]uC߮ǿ!|%=DDD׿NvCm?҆--m D8EA\.bXNum۶//W7|gN6hQ###qVTH٧5qF!]rrrll,k g`ђv;00͢ M[ٿhː(h'}y=b"|l5j… SSSu:zdB/YBAuMP53 pԁ 1** gO|QO4b0 -~O{]/8ҥKQM[nszɓ'^ yt QH+j)9o4qa9NxШ y- > mC QУqJޛxc5kٳGDFZd̙3Tvx:/"h ¨wQ h(//:uiPB6 CGFR@HCP]BA`d;Si&~OR/諐MW9)>68h',rcinnzVaH Y\g~_Mğ7o5\ M 8ƆGO#2 >ӪN-0Np } c#_J=&u4}߯8HYY51`?IkJ8@rHEH j1:-uYAN#\LnQ^Qdq))qRzݿUfee?8q"2B +0_`SCG%wjj0Į"',~!YTh 48@nlܸS'3Ъ^z?̘e?/~ M: CCmlx[ӌ84 $5P`?W_!  Mf#@ >l@)wyE9s# 3`3PG}_-i­Ƈ_Akp~c;;;va B o9_z)^6Hʕ+aoI&4p-Ӊ( N:to`@ Ak` @kӉ ^z~A_<̚5 1cƀl'al>>5o$N `/1V{ciʔ)}$(]B\\\ "&3"m8cfE]44FF668_%PJ}B8M`H@@AaG{H3Ov-њ`KÃP`02jy{go=&&Ix tB8M'@?i;v ={6VOX$ZO)-B"LGb" &026uJc,|9u]w=6<7l5Fu4IK b;~L?񏯺* ؄bfa zQO 7 Ū.9Pp/Y^^w\x$# D!N E3/2+-Zm DCgl0M?i)HI!`Ddee%"_~oJ ]!tRlf߾}蛻vD: %fpc-<3 6њKAlKFG44/&CK i hz/F(,ć"7h @@gZ[[YV޾tm&y]`PGLJȴw lp} ]yR`_̶̇ Acg(ܺqWy!pzD3? /[t+,rJ@bhH14ARQMAV`Q⋱O<ӟbphM*l7߄X9s10-- ;h8v]YRw q+Ak|IXmn-[|k G@?9z(M[nRRR3ݣFW : @Í5XrxجY(ҥKA}*Nzbv酽" h3k7n<묳" #`J PxK..qqqG`__`b3aD©i©F`ԇ~$$$`=ӉI}R6 $ ̎;}ě`fiڴib(8f0g' U(]B`D N3{@@5pu]hͧ~'0}/!Z~t9@{衇N IDATa[n;0t3ʀlF, Lo]B4]\z_Z#58cal\c>GpRֆ5@b^ulZSS#^3|,<B#،llI=3$Ai6(/ @[#Ӛ۷oGDm ΤK ezСC06m۶M555Ν+|&g`{U fAekC8M7 .Âee7vA~'LZ њ=ʔ?3):( Vپ馛Jw26}J+H N$ٔi 9558^9y?v5aFB7y8y U%e|ML7*^0Z#Pk$ÇGHv  c~__^?.nou@ i;2Кj;v s9X+ fRP~GEGT .3.fBPaqEqG_ZÕ5NDb >w$:(z`@SG06 )N8ښI&u,| @&Tj2<YŅL(,ZSVV.{=քGsR  6A_ bfl T~"@QF@(䡊ښ_ k߆DHWmz;>" l mMRRXxHw/05~M.iBnCdր@[Z ]{k֬'YM J&"`6ߝw޹sNQŒ_Wg}63xP@OL5*? N)aG@&kk, Zi 7܀2Ifk^oИq`I@}(N'k2ML!4aS!WP|5ڍP eO~sa,K&j>t 36ps(3hɣG DBPIq#J #mA.?Af+^zf`6Mēv?%K@_:z@J*l N奲Ba+e2e֭[߽{7|n.q!U\o( Z)5a, 6ƌEhM\C8aMo Y6eof޼y` )~!Plٲeٲe3_1ksd60ٛ/E&Dm  |h #\jXva4#*gʕE&ěB`OpkL{BO|}ZCkdl"G7E#@&+x!D&y'Ep몫"#TWlL޺Hܹ./<#[=4(0D|ðCepڵkyߒu]|Š>0%A ,(( I7oYHNNiAשADu;C 4!UT%XV + X6b769VkMv=.xź=DoЪ/&B:˯'%lsZVot{#[CyT\\RRdm:.JSjTF{iumNdې"5I1G"Vxs/u/k񯢮Zbk:jA7ztjވ@\p#BA`ᥣ#2"DY{yih%2 g}RQQ7m4#0~xNf`=JPWӄk͇A%jF~i,tcƍNQL]n]N=ǣbz V2TLUҁ@*v{5~W\duF d"4hHgmKVOSӪ,59#i1O!76{Y`ҌO^+ct "+Ok[S`WiY3ħF>oY{eSB[z5ѓM`3bQ`al{}@aBi2F߰au>ӧAy,5'eu-5&k}pݑj@t13z 7TRVNҥEdGGM!js道cYDiD_V]f5J9榎IbJi2Nړ9\WJ6ȊrҨLIPWo ,;9X͈љ }Tzf׮]wy? ^]Tsssa]ٌ06꙾d N3 Cm ӟ'J5xLZ#2_Rײ-zga-cbT¨1,<'rRO05.Pda$qޓ>{bTJח<]C]YexUz_A|anGpIalgR <i!߃F5 aK~̘158g^> M|_i9&MMVg.hs '%R4[e -A+sTs3CM|A >:X3Fu $<@uuHԮ&gM& @/0R"AnY[aLo+&##wZ#sG5۪M-vsw.QFFҠ 2c\Ax׊I,ߘ pvco >fM^67'p\#1؆6 $iyc35jFB/ͳ:VuwUuկ__}UC3$N{.Rp\_b\1/ǐkXLba3GuSr[ R~-d|cUD.PHh!Ϻf'ćj.l^cǎaVTii)&:a1`3{bMHl% Z @F Lm`$Hˬ5X9C̙3qK{?p@G{ۏi8&X2̂aOe*'$se ȊO (Vfp*-SBgݾo/O9(z/JV=M$ߵ;c^>^|`C!,7׬Y) @a,#kҦD` @TT l v(8u=z0CSç{{ p9pgo3BJ_@q=qy=FaR#P!ISxv{~saT:;:::X^*Xx#hkD qH$-TYZkGbͫҿsp[%]^ݤ4cꓠo2?W!\*j\gœ|4yw `?Tl.< f6]i ."hiMOGs.Xy+n/SͲ XM.U ('ґn) qD}W9֝/ezϏʙ|/KfCxp,2*αFlI^ГHӌ.g;U`h3?1sN-IDk}rmyʆYìLO;J`;sѫ'yRUbT;cRtצ~YɁE2i ,6ӇN8tezH&oVt\ne?ȭ|rI&Hd@'QAs#Dϯ>M)V>?N#:@H=53I>%Ԍ$ɻzӅ9$d+=Yӊ`!M23pi3 S0,=7]Sw:W7J**gU1žuK[z]?,i iǦ,4XٽGMJp)Ba_unam/4uO9_Y ;%ex/ ( JKKt$r~)9X6+'#,"6nzN6tH.\jmCϾVZIVxۢ*:2P1 Ynӿ|sRiB֌In 7@"Ӓ F>;D}32Y]ҥKWXfSMغ.zꗺ]#j:)\T.W)PofQPÉ@ &q!>-,YJ$O͐Komjf]o|pe܌,$`3r F݉9^a㞴\ D N`i2֌æ/d/U Hg45rNFYҐ t11 7Ŧat ;MR&L8zC҅>EO.{Ű$d𓩤`uzcR]jveb=t71կ@~@*~Rx4f*c8p]O4*|g&cf_'9"BWX5cdMt #_YץY X}л_ffi7gi9m_],ajV M.=A4{1$Dnpt?]BD +з!+Q`FCٮI&' ;niV"Diu h…F'ʞ EIRD@98 C5fs*B%L4M6n6 h留o5w@.MS#@*Iuz``*2%ˠ"˘w쨡ΠdeJ!7F@4!/)W;rW9qc$sTnxiIv^l)a˭Zd>MnxIzlKyR&+K_:٧2H*cZuIѠ뿐7iҤ|M$$Z @F m#\,n7c45ORJ jCWn^ M/ 3[0҈KEEEVUד&AMji|̃ }}}5>IU7L:d~f- DzA\5M9Nl*pi%$l  B`4~BB0w,FA<f⍘JK!=:&\9pú:E7Ð MťeŅ0 we G|JcdWD, d $9gp/ PAsF>Ga1 RT@~Ƨ4)¯? Ӱ;qrnuq\kϓZҮSoU> \n+|R,6K[3yyyi*PO.8Ԛt$AN+`j_?`THₒnN<w5qv=$Π;׶i7խz+fӶdaWWSicB&ŌWLqoCi@~5 'AvBY{Rt["01o  &h_e;'1wItrHTDiٲEFodOni帍-݀,(7na-gn^Y@@0AwEi9CVM@?s= ڏ5qr\=힡i:Y+fJ>AP3bfV KT+{IDAT!>콿o^a؞)N0`IIh}vҪÉH' 2MkhnqeOMk#[9B>>q-ڊ0Զ7>r9~;g|O YzB:ѱ_*p,{|uDɳV,P-@~/]5l|;Ҏ ~떌2\~L_~l\y&n10裝  V9&*s99L4Z[x:ک*ֵt >t<s}ҐE]p S3l|hFAI@b/ĴJ6l nXٯ/jXS%;v=_ZN:s71AS*7y{gwp-[`ˊ)nߺMw SƧWٳE z{>Lݮ?ze vE/"D cL1wz["xw"HЫ\o#bb_hLl󳚔:/->5ydsD^r{};̹}dI9UW 0`1 @@ n8 #qӜDHِz!2 ͛1ѩږUΫ2xܵ-rv-k ?..5y+f7YCZ¿>~|R "eEŸZO689nB۠VaiFv5f5Txlm4C9}cJճr>,6 J5-] F3l 윕RzHl"[a7plaww=(*XxMw>J&h"'j>磂?~l֞t4tXV xҞzG 7)?prضG"V 44'I&@v$M0 /~U(3]>}mwMODݮvnk9 D]w:[#6O-{B[QѸ&"fL(DO.߳oHg kc,# `a0&aqeɆÞBG"@G4MRɉ%>6 W ߘm3sbjw<ʵn%kAFAlhG+/r^,fEVڟZ?<ٲehu}-j !PQ3\rz ;{mYˊqk9"KrO/ Ai]90Y$ t xiM.zXD M;?w:Ba饣3צYǮ[`y疂t狞߾}#Fnmaʗ0qss*mΦ&סEjaଌ+9biq8vm"^Qo`\MhjAΆt潭jϯ3f;VMv7tx3ig4Ju@dyLxx:HlVIz C4P4iGi!xhδ/ţOD0*}]2-]l\ K⒒0|:a PSt 1Iz)in eJ5 ? a8 Oj )C< ;J£\XC6M)zWJSMѼ/hll`۲mbUa o4oYx}6x;v2.:G*zv_ uq.f76;\Y`~"F5>Llk,{İJcn3|$:jXԜtK/|3lmVxK .oD)l)=0Yj0f=o^Qqo^9&Sj,4Xf0$cߟ~nݮхX%u\fZ1fT)2~+S-{jp_aÌ KgqC,ew ydJ[xƶ-65+Y_Ј2laceC*7JN>W(NIw. 2S1S*@ D xODT&HX& zq`q+(=aWLaxK gxQguxR'(*|\YWlfUY0m0 G+X$oI4"q'YFIe<)f1 2 vT/5rp|^қlv^4R />ڍ5}\{aBuwr5g q8;?<;/g1 c5x¨cCq"@2iL)aKa(H&kp7H!YP+>iZ'ul<#r UW&|_X>ѫ|rY Jsr,|oq6DM*4#@&㺌*<:UD 4Td Nq&ngPO}yʄ;Jt.׼Vz\|Hr(2h+>Uz}2ܜ+S} ~3` CАf» $I&@&q% CC-"3 ^W ˂j!@\ z,ݢbNz"*]s+TwHhy2+g7]9!Ip~RdI #Jfd f%83A)ی: *VP^"@ni%F3 j 3ɨGHHHA#VA@R>s1[;I2?-<'m(Hz%  E;'; (VB)AYCP85YB2vh64RÊ; dnV M2ԌI'E'ec숚AaRqfp&ig{Du'N*n.&_Fujbvl]ޜnf,)5|x^ $ADI=#DxE22Y/3' "l-(+L@ͨN,0dQL*{H- Ms){&`5V#j|P!kyZk0 Ŝ3~s/flRkj,NK\%S;8'K3_wR4=Ռ (&GYE`u`u)H25W S*4T'}G1&O1Y8@`zxs>h{JUǗr])c0;/cFjna.#k8s%΂ Qc5ṕ8B lgGWpaSQ hi&(؇ʆ}e!aTqNEwuƞa@1^pI)y,oTl;)@ x^K&]QBc8:1_iK%y%9]'9G/K9=G`͠`R88e*vDz\aGDZQ# ,=˒o(U1  2U 77Lʆ+B0 꼒挃anPaEޤMowW92'ɼWE4rXRo1pVAe%AYau ZNsޑFN6>GRE;L2D A@F4i`fةzw DE##GdS"@iSjф7eO,3@@0A*ȁday=$ NcQtC;PE\cW^2AfDQVXi,X0.6\^)"~]$p l|Dд@Qa hFSiLvpmĕqpud\t#@&Z4U,82} aG&ea8~*/1C@!B݇<̺2?PWa'%&VBp0^&UH͡R31XL>B[0*M MnF,U0J&bpdv&bT5ԠV!*i"U0 ;eq!8n!; bB٠|+/#WALE9 >0>>1ׯ\grP)6EPY,|l  t P3lb aV!P"pH:C*A Ml,#8UKr i7̒YDU@H6K GlTQ%,w8L^` L:A{%x'cXNh, }X-FT b7<]$s0 &A05MS3hk_j3  &@&x P 8T HT"#NY\DբP F 5,0XعsP/* "\CG,(Y,z%W&P A 0A"82X~%ر||(6͈#d S6LȲ /JfDZji2b ,^Ͱ!'pA2>Vt"0!HLF**MTq1#L0}4ڀH|Kəaza"RpDifR M8T2H KLa&>jFVifwQ ")J` "Ii4hm`meEFՌ+^DtʐRa6ތ -dL$D IPŊԸ2E_:#]!D$idҦg4%0a5VL6Eˈ D"@ҟ,'"@ YO4Mw15"@&D7S# Dd=4Y@"@  @FL$D"Hd}S DhiMt35"@@ M]L $D" i4H"@ YO4Mw15"@&D7S# Dd=4Y@"@  @FL$D"Hd}S DhiMt35"@@ M]L $D" i4H"@ YO4Mw15"@&D7S# Dd=4Y@"@  @FL$D"Hd}S DhiMt35"@@ M]L $D" i4H"@ YO4Mw15"@&D7S# Dd=4Y@"@  @FL$D"Hd}S DhiMt35"@@ M]L $D" i4H"@ YO4Mw15"@&D7S# Dd=~CIENDB`openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/qos.rst000066400000000000000000000134501514270232600242700ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================================== Quality of Service (QoS) Rate Limiting ====================================== This document explains how to use Open vSwitch to rate-limit traffic by a VM to either 1 Mbps or 10 Mbps. .. image:: qos.png :align: center Setup ----- This guide assumes the environment is configured as described below. One Physical Network ~~~~~~~~~~~~~~~~~~~~ - Data Network Ethernet network for VM data traffic. This network is used to send traffic to and from an external host used for measuring the rate at which a VM is sending. For experimentation, this physical network is optional; you can instead connect all VMs to a bridge that is not connected to a physical interface and use a VM as the measurement host. There may be other networks (for example, a network for management traffic), but this guide is only concerned with the Data Network. Two Physical Hosts ~~~~~~~~~~~~~~~~~~ The first host, named `host1`, is a hypervisor that runs Open vSwitch and has one NIC. This single NIC, `eth0`, is connected to the Data Network. Because it is participating in an OVS bridge, no IP address can be assigned on `eth0`. The second host, named Measurement Host, can be any host capable of measuring throughput from a VM. For this guide, we use `netperf `__, a free tool for testing the rate at which one host can send to another. The Measurement Host has only a single NIC, `eth0`, which is connected to the Data Network. `eth0` has an IP address that can reach any VM on `host1`. Two VMs ~~~~~~~ Both VMs (`vm1` and `vm2`) run on `host1`. Each VM has a single interface that appears as a Linux device (e.g., ``tap0``) on the physical host. .. note:: VM interfaces may appear as Linux devices with names like ``vnet0``, ``vnet1``, etc. Configuration Steps ------------------- For both VMs, we modify the Interface table to configure an ingress policing rule. There are two values to set: ``ingress_policing_rate`` the maximum rate (in Kbps) that this VM should be allowed to send ``ingress_policing_burst`` a parameter to the policing algorithm to indicate the maximum amount of data (in Kb) that this interface can send beyond the policing rate. To rate limit VM1 to 1 Mbps, use these commands:: $ ovs-vsctl set interface tap0 ingress_policing_rate=1000 $ ovs-vsctl set interface tap0 ingress_policing_burst=100 Similarly, to limit `vm2` to 10 Mbps, enter these commands on `host1`:: $ ovs-vsctl set interface tap1 ingress_policing_rate=10000 $ ovs-vsctl set interface tap1 ingress_policing_burst=1000 To see the current limits applied to VM1, run this command:: $ ovs-vsctl list interface tap0 Testing ------- To test the configuration, make sure `netperf` is installed and running on both VMs and on the Measurement Host. `netperf` consists of a client (``netperf``) and a server (``netserver``). In this example, we run ``netserver`` on the Measurement Host (installing Netperf usually starts ``netserver`` as a daemon, meaning this is running by default). For this example, we assume that the Measurement Host has an IP of 10.0.0.100 and is reachable from both VMs. From `vm1`, run this command:: $ netperf -H 10.0.0.100 This will cause VM1 to send TCP traffic as quickly as it can to the Measurement Host. After 10 seconds, this will output a series of values. We are interested in the "Throughput" value, which is measured in Mbps (10^6 bits/sec). For VM1 this value should be near 1. Running the same command on VM2 should give a result near 10. Troubleshooting --------------- Open vSwitch uses the Linux `traffic-control `__ capability for rate-limiting. If you are not seeing the configured rate-limit have any effect, make sure that your kernel is built with "ingress qdisc" enabled, and that the user-space utilities (e.g., ``/sbin/tc``) are installed. Additional Information ---------------------- Open vSwitch's rate-limiting uses policing, which does not queue packets. It drops any packets beyond the specified rate. Specifying a larger burst size lets the algorithm be more forgiving, which is important for protocols like TCP that react severely to dropped packets. Setting a burst size of less than the MTU (e.g., 10 kb) should be avoided. For TCP traffic, setting a burst size to be a sizeable fraction (e.g., > 10%) of the overall policy rate helps a flow come closer to achieving the full rate. If a burst size is set to be a large fraction of the overall rate, the client will actually experience an average rate slightly higher than the specific policing rate. For UDP traffic, set the burst size to be slightly greater than the MTU and make sure that your performance tool does not send packets that are larger than your MTU (otherwise these packets will be fragmented, causing poor performance). For example, you can force netperf to send UDP traffic as 1000 byte packets by running:: $ netperf -H 10.0.0.100 -t UDP_STREAM -- -m 1000 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/selinux.rst000066400000000000000000000165651514270232600251670ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================= Open vSwitch with SELinux ========================= Security-Enhanced Linux (SELinux) is a Linux kernel security module that limits "the malicious things" that certain processes, including OVS, can do to the system in case they get compromised. In our case SELinux basically serves as the "second line of defense" that limits the things that OVS processes are allowed to do. The "first line of defense" is proper input validation that eliminates code paths that could be used by attacker to do any sort of "escape attacks", such as file name escape, shell escape, command line argument escape, buffer escape. Since developers don't always implement proper input validation, then SELinux Access Control's goal is to confine damage of such attacks, if they turned out to be possible. Besides Type Enforcement there are other SELinux features, but they are out of scope for this document. Currently there are two SELinux policies for Open vSwitch: - the one that ships with your Linux distribution (i.e. selinux-policy-targeted package) - the one that ships with OVS (i.e. openvswitch-selinux-policy package) Limitations ----------- If Open vSwitch is directly started from command line, then it will run under ``unconfined_t`` SELinux domain that basically lets daemon to do whatever it likes. This is very important for developers to understand, because they might introduced code in OVS that invokes new system calls that SELinux policy did not anticipate. This means that their feature may have worked out just fine for them. However, if someone else would try to run the same code when Open vSwitch is started through systemctl, then Open vSwitch would get Permission Denied errors. Currently the only distributions that enforce SELinux on OVS by default are RHEL, CentOS and Fedora. While Ubuntu and Debian also have some SELinux support, they run Open vSwitch under the unrestricted ``unconfined`` domain. Also, it seems that Ubuntu is leaning towards Apparmor that works slightly differently than SELinux. SELinux and Open vSwitch are moving targets. What this means is that, if you solely rely on your Linux distribution's SELinux policy, then this policy might not have correctly anticipated that a newer Open vSwitch version needs extra rules to allow behavior. However, if you solely rely on SELinux policy that ships with Open vSwitch, then Open vSwitch developers might not have correctly anticipated the feature set that your SELinux implementation supports. Installation ------------ Refer to :doc:`/intro/install/fedora` for instructions on how to build all Open vSwitch rpm packages. Once the package is built, install it on your Linux distribution:: $ dnf install openvswitch-selinux-policy-2.4.1-1.el7.centos.noarch.rpm Restart Open vSwitch:: $ systemctl restart openvswitch Troubleshooting --------------- When SELinux was implemented some of the standard system utilities acquired ``-Z`` flag (e.g. ``ps -Z``, ``ls -Z``). For example, to find out under which SELinux security domain process runs, use:: $ ps -AZ | grep ovs-vswitchd system_u:system_r:openvswitch_t:s0 854 ? ovs-vswitchd To find out the SELinux label of file or directory, use:: $ ls -Z /etc/openvswitch/conf.db system_u:object_r:openvswitch_rw_t:s0 /etc/openvswitch/conf.db If, for example, SELinux policy for Open vSwitch is too strict, then you might see in Open vSwitch log files "Permission Denied" errors:: $ cat /var/log/openvswitch/ovs-vswitchd.log vlog|INFO|opened log file /var/log/openvswitch/ovs-vswitchd.log ovs_numa|INFO|Discovered 2 CPU cores on NUMA node 0 ovs_numa|INFO|Discovered 1 NUMA nodes and 2 CPU cores reconnect|INFO|unix:/var/run/openvswitch/db.sock: connecting... reconnect|INFO|unix:/var/run/openvswitch/db.sock: connected netlink_socket|ERR|fcntl: Permission denied dpif_netlink|ERR|Generic Netlink family 'ovs_datapath' does not exist. The Open vSwitch kernel module is probably not loaded. dpif|WARN|failed to enumerate system datapaths: Permission denied dpif|WARN|failed to create datapath ovs-system: Permission denied However, not all "Permission denied" errors are caused by SELinux. So, before blaming too strict SELinux policy, make sure that indeed SELinux was the one that denied OVS access to certain resources, for example, run:: $ grep "openvswitch_t" /var/log/audit/audit.log | tail type=AVC msg=audit(1453235431.640:114671): avc: denied { getopt } for pid=4583 comm="ovs-vswitchd" scontext=system_u:system_r:openvswitch_t:s0 tcontext=system_u:system_r:openvswitch_t:s0 tclass=netlink_generic_socket permissive=0 If SELinux denied OVS access to certain resources, then make sure that you have installed our SELinux policy package that "loosens" up distribution's SELinux policy:: $ rpm -qa | grep openvswitch-selinux openvswitch-selinux-policy-2.4.1-1.el7.centos.noarch Then verify that this module was indeed loaded:: # semodule -l | grep openvswitch openvswitch-custom 1.0 openvswitch 1.1.1 If you still see Permission denied errors, then take a look into ``selinux/openvswitch.te.in`` file in the OVS source tree and try to add allow rules. This is really simple, just run SELinux audit2allow tool:: $ grep "openvswitch_t" /var/log/audit/audit.log | audit2allow -M ovslocal Contributing SELinux policy patches ----------------------------------- Here are few things to consider before proposing SELinux policy patches to Open vSwitch developer mailing list: 1. The SELinux policy that resides in Open vSwitch source tree amends SELinux policy that ships with your distributions. Implications of this are that it is assumed that the distribution's Open vSwitch SELinux module must be already loaded to satisfy dependencies. 2. The SELinux policy that resides in Open vSwitch source tree must work on all currently relevant Linux distributions. Implications of this are that you should use only those SELinux policy features that are supported by the lowest SELinux version out there. Typically this means that you should test your SELinux policy changes on the oldest RHEL or CentOS version that this OVS version supports. Refer to :doc:`/intro/install/fedora` to find out this. 3. The SELinux policy is enforced only when state transition to ``openvswitch_t`` domain happens. Implications of this are that perhaps instead of loosening SELinux policy you can do certain things at the time rpm package is installed. Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/sflow.png000066400000000000000000002473731514270232600246110ustar00rootroot00000000000000PNG  IHDRitLiCCPICC ProfilexYy8߷{&spdvyscL)JATH*dhPJE$L}5|s~Zkλ:QÃaFBB"l(.;+2\ ϥub3d=#B|X+<" *B`}3G "6/A J()1^~XPPz^Hm.zT?:T߂D&6 مG#WcBzBhoXό7yr!_>Cd P;%C=-`-#[#c!(m37<=)_iWOi lg1"n3D0QDo>0`d[f 2ݞ9P \0DR?T"ABp2" `9燌) g߳Q9 K"sl󶭋tHל%FVvV_hy"ZB ;*h]6Z#0hkFߘx5G=r/ \_ Ca~Q]dHRLC%)r7m{mbOE$DE 8-$qEF⎾y^1dd@" gyԁ0f ď? ) iPJ@9.F @S`| ;؀ !2BB$@Z!dB.B( : P5t u@gkh B0 &07, *.l{a?x9p\Mp~ Ÿe@ѡXQ|() JerE"PI,T> UjEFQs54MFSRH^}$t6} ݄B?GЛ" #QØb1~XL&SƼLbcXVVkubc v38vñ$p8+pU:^y#WPT|;444BjV޴񴹴x^Sz|7~NΆ.. }15AOC&ׄ%"(L!9j=qL/MoJM~~AA!!C?#-0>#1&2L#YBH٤ Iɐɛ)8E 듽i r7y,l|2&,,q,E,YFYQ¬;wqdG+ll:l>lYl l/)A'؛ه9699899889p\\\ʹyùra 3K BѥS (]>.>h |O6ES*y  R:#+","$),<#&b* R+N(-OLVLE,HSqX\Q_H_P8'L#**Y&9$EҕfNnq9!+)(,[!VIL.UU| Aŝ;}vJK1SS񧒲RRҬr J}UA655%(F/RA53">T Z-RQm>mvoJi]1@:y=Yz+j(c,'L#FF~FF ƊM0&&'LLMLM̔ͺ v,-",ZwvR2Բ XZg}kcmSd3e+g{׎lnWc^>CC#j'N2ΉΏ\8\\Z\q˻ w=GqOƞ"{>pp vNupAQ=M==x}93{wO߬\~@abI`IJUPUVSpCMGPРЮ0g޷aQ ElbF.}ѢчbbbVccőBO'%\܏ﵿ߁c$Ϥ΃N&'_J^:hyMZu77fg7nv޸%}6;;w&]not|{ދ.'{z޽yڃU6?RzԧwO4+ KOM@!:22zBݬ*952_R^H }."/;&oШȥE^M_ּ }68*&gL-jw[umBluO9Ĝ\\#v9{%jgWyB,5$9CBCg^k̏:k/c?PbGR3))!i҇820?r=hvc999sN;yӉQg <:K:Ow~dɅke*Yp/K{i=}LxɻuN$={aRmP`8jηʆO$;fAq2:ztLl]sCK烾(/`뾦/y|]ڇ?~~.J5dbp4,4Ӵ=JlB ѝބADG$0ؕ99/p pPrB҈H!R8s\IJ9VSNNHsL[L'DQo@0ͨɬ9ւRJZFVN^AȑӉ8qq}uυn=u'nPKq=.ngwᇃ<{gyЇWLnnxadѵиЄdTYGO'^ϋI[jtkY{êڭ C/ }ϡ4蛘vBDk/u | $,+YYŐqG [2i˜\#Y>K0BKlb~ْW+dS|4BPU3Q8YU]s^7W/I?PHؘxƤY+%u6IjvW;:;;.㷗wqwSȣ#/?PD . q % 7هw/"%R+GŘx$RX $$DĊPbC_2oܿ@ AWo:8%5 >-}]]]ߵ3@r !7FΏ>ثw!!"4< &o",8, :~a)xï(~)*U@}B"=t(E,L V`7qʸeG_Jd Jg8|*6x^̐э9ɜԃ2=d Kf*"K+;;VJW88]\]I}~+JX!3aAHdTS $} edv(J(+ȩ hjjhы570Z76u42ii`uzVr:8oRwram}zto ]?! z_,o܋VIߧ\I<䙩s5]v\$gr韣*+뮨,t&ε^L#u&| F.ѩEɸc~g/8^+56zm2sG%ʲJٍ[?G@!'2F ;JRg@RCK#$ A{8(jD|&Ê3| (6*uՃZB H~>d6H.~ +Öcq{pŸa 7M5 m m F$? zWBI~yx2|Y>7Vp`óc?ȡɱɕmdzM)d.4*XMARDXHtLlCeE%Ies7h\Zs$5]< #yPZqحىvsqnuޝ>O _L f 3HHxqpCڙ=Y9Y,O_K[.}!"oܥ[5G, W0ilYl}VyPOiL}ܣ7~vyKWoᇛ{~0Y:<8dJXxSqр'A*MFi4^2E*BH ZY` >(JYP8:]p#=3eaOc_xq:REɣU=Bҕ@Rߘac`& 2yȭa,,.Aϱ9S[B);_.P/&@xHdV Pt:,}CfJKIηJ*wHZGt< LLͽ,,C&mmGœ7]AE{z;bn"ws;^5Y_I?-C]=1rtҳNjRŔ_)^VM,pscV6ۣw;iv?T|qY3#_x I ۏľx|p䳩ƙď/J_Vn.F}:Tj^} ~5uՍs?LRDDCʏ#[[KH$?Olmmmm,G ?Wl c{q6OO~{/ pHYs   IDATx |T՝$3$d^!-P U؊n[/jlb_D>}tkgUA u%"o{ 3If<{LLwəs{sι<ƒHHHH  MHHHHH@%@ @%E;    jG~HHHH%@,)ڑ P;3@$@$@$@$,j`IюHHHHڑ    ` P;Kv$@$@$@$@Ԏ K1XR#   vgHHHH XԎ #?$@$@$@$@v HHHH    chG$@$@$@$@ @%E;    jG~HHHH%@,)ڑ P;3@$@$@$@$,j`IюHHHHڑ    ` P;Kv$@$@$@$@Ԏ K1XR#   vgHHHH X` id~NZLlafۮ߮ްSE)W=3HՑ @0xcJKXxմW]WOPߨ +R=0HHHP;xvJM?07i!!o/|}YxrQ|%Z%۫ˮXt Z%  /w /$)ޫB#UT8qӋ}N^lQVSٱR, 4vvhEr&[-䁭ԴUav}u$  x!qx驾])vg_}ͶuV:7GM{r~֍s|IX W>+f_vf   =j3LL|Y -Wn9xʦO{}'+}կqwqVҪth\xW   v0,[^[,h2iɚEqaE #6pd/s5 a0LY"3.#  zu.&4уچ0ZBr v\J]zIj}w5rұ:b4UbUjoL_=ϱ @d p1|t.)Vax<#|Oͻ|7^{ +}m-@L(-Wݯҥ w$@$@$@!`x<)#<8mڂCSf~~% d ͎TK"nL$@$TX   Y 3 @RvLfcIHHHW{IHHH P;&Uw$@$@$@$+Ԏ$@$@$@$TX   j^cf   H*ԎIl, c13 $jǤn6HHHzEڱWHHHcRu7K$@$@$@"@+|L$@$@$@IE1%   ^v>f&   "@Tƒ @P; 3 @RvLfcIHHHW{IHHH P;&Uw$@$@$@$+Ԏ$@$@$@$TX   j^cf   H*ԎIl, c13 $jǤn6HHHzEڱWHHHcRu7K$@$@$@"@+|L$@$@$@IE1%   ^v>f&   "@Tƒ @P; 3 @RvLfcIHHHW{IHHH P;&Uw$@$@$@$+Ԏ$@$@$@$TZǃDjB1H`0+%  Hc۶mַb$@СC_M6 RbaH+vKKK)7لx!xԔ~N$@$[!!GvvvbH*@pj5͐z䬄HO 欥vLOa#/4f3Lw7.Y 2DЎqV j^$H ,>BG|233!R> ! }qǍN0L! eNS_w/M0 @wA;mza8DH}!JcaB&ocKK|aHH $v $Œ;@0B j凐YHH@jnj~1a>lHt @;FIHB a#FGǨe$`rq1! %qPhO$H_@&! $$A$a$@$@$@$"j1 $!j$t6HHHB$@"8f#   $$@현& @Cl$@$@$@$d   cHHHcv:L$@$@$@!v  @vLNgIHHH D <y\f6ǃ2œlxq,;͎ޡo$@$@D1ބX+"]̘4@[ڴ< iSߝ'Yx0  SԎ}qG>>{Aseetl>qo2R\uڰb ;MS*ĩbeGC&0O(ΏOrꥻ$@$@Fڱ{Y_gJsT8QCƳu>dFNic답>hd?cht ׈Uֺ $@$@$k9#o,b jէ2R~ twc<mqasY鐺;2/ o} >Cj3 @_vK޺; J֘S;&Sbc&d:^{2`?/wGo;"Gxrs$@$@$=zB֜75O)SI zc?KtgMH]زW2 ":"s?j748=C iB$@$@1F1: 3)][8mEuѰtO1ػ'u]cnR7;%vN>鈴]>+Hi=b$96HH I P;F[903ګ P{ѳ=?lOʜlM v+a\0{ j?ϛ?q$@$@qC[q<mcb4}OX{g-Wm|՞̥(2VR4*Ϝ;] {i@d;޻\(~%i>g6=>K$@$'Q;co4;`|ti=BtT>w,KSTmdj_z?آX8aJ{2sݯa ߑ @luӁB.fI۹wk?r,t;5D#FxN ;F+~21r8ğҶwgI6]zWМc1"l?دtŎb+uډq. @F{˪: GotYn%0$2#|$HH p:?l? O1xV$   d'@HHH'@<+Z @vLOO$@$@$@v -IHHH P;&''   P;ϊ$@$@$@$ @gEK   HvԎ `IHHH xԎ% $;jd$@$@$@$<jYђHHHc~   chI$@$@$@N1?l? O1xV$   d'@HHH'@<+Z @vLOO$@$@$@v -IHHH P;&''   P;ϊ$@$@$@$ @gEK   HvԎ `IHHH xԎ% $;jd$@$@$@$<c쁀ݶ/`>#3ԬHH P;&a$@$@Q%~G8I_l_{-o,͋hUlˎ%ԎFZZZnnncccEIĸ̙31d-==HNS{֬C6?#wϚ8,Emwftl^ {5zQ {o t#L1 ^TVu߷#W4l[M{!S+f͓Mpڵ♪ x9~è`% B`0+EG,]?q]]]%\V(vDAA%Kb#. #I *>޲tʚW,|>O_6]gߑ+,&;舗_~m}"333??1"=I Lyf=!qz?#bț+zEa٪¦Ǖ(6X&jlsgj0_"C7%M^jrٜIjG!7xC[o͚5nDNCdLLaE2o#vq7|S {7///!c#",J̫Ph͒?Ҏ+a/FDgplo}r8`i6 -cx\<7>#'Jr|C%je`FPѪ<<@U]jvcKKޙwP"qr,'7j=DЎ999&-rzNW'O ILL2xf!Q IDATG_uDkkk;}_3<*P1G 8 y]ߞ]:QGI'KgƵ/kU/b xdZr˳Q =ʶ/\)LTKkvS;{tYEZn|l7}7Yp*3ilX_WVu:·boju|ۼyϝTq`MF0l~Vc:a(E3PmԖ ?}_WW 3+-2L%ۅmw~G+|t>\a`0R>؁hG{g#Z}|0L$ܲU(b$e1i !<7eUͯղe&*^w53/N5_F^ص R5iET춾j8sBW徰NZ3aWxgRk`}RYF͠1Z&Jo;:e"{-z;WN[/[y E^^uڱ}ͫ:FOEr3C5M-n`NE|KԎ}I2^h"$ƍ 7>CE`{2dn޼c!/2vO@tnRC,?nX9Fx %(!_7K\=l W3nR%7Zd6R)NV cC|gu4r5w~nZ WnLX77T?s)Ӵcʔ3gLvM~3nn~eŮr/ P;%˫KhG|c=6h 6.dX,x}Hryp/x! 8~W`@l`x Θ1C}9eӖH z0m['(Tjãܡ|y+J-l|+?k}mTxݟSt]LgUj ]=Yw9&c_Ο"*eP;ˬcW8/^cl@ ׎x}a˗ ˬAlx!f///y9,%:m۶=B#KPEӈH ,<:SurΔӡ =_k E\_)_}W-+Rj[֯R=8;gGzKܼg84 d\?s KNVcIꮈ;q)Ԏ"tqqOLvv6ƺ*GA$a:RFGD`NL,:#R;bne[%2 V-6C#j..wQ叛˦dn~/,1lO,732^>u ą>,vr߫ØnRY1{+q ᔹ: bspWϛ4zp'.Y?Kb^y_j8E Z&:1R )H< fnhuv>^&%>♀wbuY|cZy/nTê+Vv9,xQ2R&Íe)bWu\T以1UWQm&h]_iȢӼTe#*{˥Wƒˊ+|KR9 r&0Fc$L1܅a-H 1@İ,Z<C Le \:%j`G ""rH LD=DZͫu,P '0=t_WSM7oڸnizw>@!N(]l n1]Rn 7Tk7vx!CQ_-~r( û){'MT%HCwtFwIڱK,)4L ŢHH d )vu% ,qn7wLd\Oq/.ctdxE7HH7ީݺ8`e;/=go*f8!q8(M>:}% yc=괟bM[cǛek؎6ӈv,G#P<9,;;'eAy{ȅre.D#Ɛ $K4LEHL(p"%,Q%e@_N"K`F#B'"(j.Qo{ȅ(du=d@~5%*{^xZ"XIcZE!G`Fڪn3a)jfE)((̅gϢ̈xam---gΜ|EIQ[V1Gnė=z1]G,"EH$/oCfr}"#E^KT= ts='ˑ$*.??{n(텱,}QM'իO8!2 'a "Du8υ "3"I 'o6Wb?ڵKo~gr3ϟН~->,}'NHz뮻OTo~ǁ"_ ĺ3c< @BH(/cӦe?BZC+]fEEEˌȅ#1=#7|3ƨP_CuЅ#GDF@\{ȸo߾.Cхz?!+amm-*2 "!+QQ?F9@~"]Ǐf~~KsQ3ֿx"l֡^D@_T7d¢"`06OD@98 aB#",T #2j^7n1" 1Z! GXD:/}KDxE(6WdPIb /jgDXY` DĈ0Չ$z(\VH1lȅ(ՉH+~ae q,ۂS4Yֈ࠯N"^EY@3J,Q_< q fԈ%^VT$Y$S BBՉoqH'̄%.|0ɎQ'A.#3" .dXP 7PDxE.d??':E$ fK?Ei $^[ ʶK  H* v$qU61#ć(%2LWY*'Y`x G@ JqEǁH yChF.@Ȃ"4:J*QjQO[' `C6 AJë~^ɼeEEw~ʌ~~" ~X0:)"t(8  A`,@^HgwWVA]"\2>"a?=e  $!82(jp})/zcUb)3$!pA:@V$"^š.sLC s![$A` U ,EF*,EiR]uYcwP22(Y֫<m9+3" bC.}"Ȃ\ˌCDF ~y12,jG]8Dh#'̄0`J}9"R,ˌ.sH ̈qG a& H6sV+p%Pt*R$.2ˀ,A#jJ"\A.qti/ Lշ*D~d"&P s!UDW KK}Q"c0f~BΨ3bQ$P^(apI8PK f"^~e @bH(( 6yEG@L`ԗe]=aW] 20{o   8%Ӷ%͑:8 }] vzx.ٳ7TH(~$ 3mI%:w@S֬YtKHţ_z%$alXli.)ΛNIHH Ԏ]@(! 1VO~#ڵkO>0OzĨ$UKH!pç~ZjG`_|b $  !@;}  U u4 6#al.#`qTB#bJkA'|"z#ux44K# !Ԏ1~fq7"76+xR{~m *:/ìQoWo[t Me $@$@$cST?EE%&o!RN:u oKJJ<-FNɢ  ]wm݆.\ 0{#pHHH :C T vdcn믿sKT !Ń|=/`޼y#P0L$@$c[v..h"HIM7t7 I1*S#VD/ 2OOH< @v ]-2z;S cA8X%KD{ xqc+ HcD{ qqn @vB,X`׮]X8rHf\ c!qo;&K.E/ R,`G< L`ؔСC*{9QСC'N&ᙟڱ>Q\~=nd ħܐb{".PX~ TWWcqjTW_$/TI ^oʖ-[D/Ox≟gxldCD/uoD8^W#G 5@xEvqz(BUbE_8=5FGyOmQ߀Ѻ>W#z #7@L?=oӓMÉA1왊E8rh[l0큥e8!999:|h\Mnl׫%Ck IDAT?9SӞX1'5S[gNl[F{\hZҢοV5aņ$'<{-l5PP;t55N fH1_دw7ʻ{_>K t%'٘Qv Oxl}UOE%߯b2C9zK1ĈAGǩcϖ @d.س1F{n) ##W#?$@$@Q&dp}CJ usnғ(1:M% X&(Xv%-|8~ }~- @,c,N8|+?$SQQUSY(>z/IHbcIDvvC]XyM8e=}(>>l* $jtqÿ>__X9H-g )/oPM25@uc4Xa쁊fEIt@eHH A P;Fc*mZ{Չ?ؼysuUOWUoY[{ޝՕsb]*67 \+a7Jyl)-4v|@ٜMό -& e6V[RTw=1:-  H\n,,V$̚zsMK6l[ʨQ'ƫnF)rߌ)yK X5F)箟jl>/ZKVԞZZ\l4|1J(;偱j7hR$*ƎZ"XTOB?~˼ HH p1xӚ رkמ/E=u|߮_~M㴅?, Uh;-L&|:l)V}{L{ !c|5xtkvvavG!] w5kM{Rdv $>Dk͞^j''*O7u&\bo{^UXrwv++49{/AzLjgJὖ6: V-^ig>}{Se3W5]gF%膿췎"  @~,1 gRz%ޕϙ_>4Gi!ՑÎ7pDTՖha,+c/6kk~: KPI˔ӅU+QY;K(Œ.v?yI$@$@Oa7:ZXxk ee ]33)+Ijh \ֳG- @LvoHe @ĕ1 4t:yY)Q'Kv.fܴ3,^ح;IztkCp|yojGF_ $jtMe`[!+kQٶltuTQ*w{O 5cؘ2+g_) ,Jzx1ڛpgw_#7ih ('#,& w* :;0w2 '1RQVC{=_h>,CRxŐb@e %E1(i@H/cP\P ƚnYZreAE%'M+ftaye*8_ީΘWz ;IÆ}V)1Tɛbu l8gkm5ի**j8~eIev@mqi\*/I)++M72I5{ׄH"Bs6nۣl[K{rN$ O4ڐRl6 n'5-eO9[q8݆ 'MH7k|PjXhV>Ͽh>uߞXg<<"o?uX.Y4r6^yxt԰@_<k+2g:]jE3ɺʉ/"y&{<>Vl7=Xq#A(HDK2GH‘_XX6:^vC}m2 FJn]*.E(6ӱ>?v%%$jKMO3K3`Bp;ۍ(18ct >Tz q:ڀu1Y; @ q?=kqaLU&O=']wCcc#6  ӎ%tXfOC֑+-5u̔{L#*!ZjkgJrߜPU F<SJJ怒~'𥁼ZtLVᠾGa0ev _X $4ns8l܌TLOJzQ|1$Nْ v9ةG'R;?hRyEfksr(ˉZp;}Һ^5nxU?jGp\frYnG3l.Gk+]}V>+8y*'4c@YRQoh0 $ lhs`I1\ѿXx5`Ea|L&|$| dddbˤЋS;g wge瘚8"]O*,-NOOosFlx-|_9Q 7=ݘY KoKHoIPLmwI)![[[?c˖-OFfAƢ"46; /| c$}q2Oiokn=pˋGGz79 Ćo})[*mWm̻h: aB O>sj 8Bv"$h~)QO^5jM ?v>3_:K1vdHg(x@Lڱg>ݦk9vsʈn3/ +uZZZ ю__җnSBDIEͣ].Off&-V(hß'l&G999w~'2>B'Uq?* cFmK"Bxy#!Mؗ;c;t\_pۄ=ۜV'\v7n0gIn6bWLMVO3fO݅zܠ3N.OK7A晌6҂V 6iCFg3Zڌ[g23asњ&kii ņ~YYfT+3&17|PhԙcgA#@XlW| 10l6v (FiO~{Rirଊs(N\N26hGB<F( b; 6c':9^ZZkX@#9 MN hÃcΜ>[Ui8-9ؐZ1Geeè#KKljۃAeC}@рî8p59[gh6g[q%W_vaO?9 /݉] ~+yyÇw5AzBf31cŜuV'+4Ĉ#$/ϻjoEmN`RJnnգF]lj`[ _x P;لvn' ̜8a Ԉi^{M1b to)'xBQI|AqD]4MhQ8s;~$``Y nτ`ciGxS%H  @v:nCAW@~FG#~nD%ÀG;nP2 ]aܳG1M) R0O68a<ÑMWOjOJ*xg,\R );B*j^ p=FSVn6CJ9+CvSZ]CYyXfbzV@FsKγ_u ZMTTs`J4Er"z+dݹ) 6iA)?\j;rq@HIzqA)U (2a SGXt)Fw܉'qa"#Uazh2*A)CX<>|_)KJJOyf -.T#~62>ҁƲ|(:4%5H: u Y joTlIh5F}5ݓbN5fC4Ԉ_{ DVN920cu:b8Ỉ ÇggϞwrJLU[R_*%l0%!q@;b>zڵ6 KK_}տ𨸸xԨQB;0@(rR_ZAiWs6vT67bo<_g66jeh65ٮ,-mnl9w:oĤXCyj;9wVN4k/KE:/6~AC8XYjfb!zԈ՜PKC?W' O :ګ;m!;'Bܙ?pIOLjL;ʼn,Kv~nn}t:ζEEvRkD; ' СLcpb7 zE|᯾jhG,W_O_y7tqR'v{ i޺ގ #H~;y7XHupC%';zAPKF mC N1̙-wJ6{)cSY Nd/qsKcKՙ¶:u- 5nEyCϜ9UCˢ蜼|yZy`jlnkl3d7|vľ(<#3M<lhwac@brI6f](%rK9imu];mwjK1r /#3- @Q!GGՎ#;M-2})MW]3׷nwCcТLR;pԩSwqGCCƍ]`6#qTUoƉ'2\iVXKZc=H]2Ԏӱ48ڍd[eb h|qVTgJE Ғa.7TEZyi^/H]NWnpVjmun>YW ^x1NhaqƐ1Vjp iXW4A1H1i #&L0z'N`K1wu|ĴUN}s8lݺ7 27x#1:GJ @|q mlXdmQVB:w$'/7GZWLGꖖP t^ë8Dw^g'.) H}a]F@fcqvFQ hX[N*.y8# \S27˜O AÐC,McШbA|=0%$##r6g}vɒ%:&.∱Ċ;< [?jvp -w'Xص 2q̶F@d 'p2W"98pA*v"lߴ8A|;Z-V.!/ MJ]<:(,E.Ge"/jDX*$r ¶>B'Z b=ŀY@|Bo\i<fΜ{~{~=`*6~%$gX=c@nqVŁӨ8"CV|s!bmqbx $x*4PfH^y   DxsAQ(U@Yb1${>\/pC(y6EUz9;+lx-dX- Gv\SR\T+S :BDVTT;8wTB$O޼y3"qr|'q?qMBcH \pbP2 Gl쀳7NMPl8c:b` KH F L qÒ$X$B b(6(YWH+YU XZZHQrhD80c Z[-V]f08p>8H=k?⌉=*Ug>N88QWH7p~M>΋5NDYTe!p>+fqv;?#Fd7ϝosUjPH @$ *lDxڨ"Eilֿ-`?nB J!TjϮ:5[u+uu{9~g[y~:# \[&wC 7/IwU{a /ZA`8\:d'Hogk.kvwq;sN\F ڇ对n ChDo0Kdb8!2iP Vچy95Q;"i䷃K=h ='RjP4u*b\R x5G繇ߔTp4fxy%6ҥKG_ r83_y啈q3 Ǐ0"YSSqi瘁  z{Rnu?=Rrˍe^ЩeYMvt'';nvhL &'T?A|ĸ=M@p4PWN^d}A3JЉ0dQǓH*a%NOHbbQl-l0VsłG+88R) sUV#<[ׯ_l2J9&Qv7^j\n+Dr  0"8Yy$ ; ƠFNe<m|jyO;y5חdM='awg7d։r) g!5}D}2ڈ& EEwCI30~39-L~;&=_/c8|7^z 70{5LɏåBYYfӟ4Qs qL;)f # ܍hוwA;?mNj \N8Fj4_8z8ph"> Mg\g8>[p ImFl,?;R肴,&2,NOQ4$%zh LiWCx4X.4UGj,#J8PNh$&1Hr(%) 71*VnCy9G̶ԧ>FtwUc Qq|ݻ2ȁ~ 11Y3 hOγf9%Nk=GqJX%HҼ  =a=G@{{!BŌRW])[.QEonS h:B!XC!yx0B֞\̏iO4f8qb|xH|&kSt]V1NAϚұMG6 ;M x%|^l^u۞~_{S>vmhL?|3TG}EE.]80 >ј?dGtzݳ2 |6O;%\1J@R ȥΈ~T5$ b"a3"N^i:ЀfI\bہ6ΤZ U{z1N;8qLln-4תU@, M T 7g1RG) /ZGR붦 Jm-@?]y'&_߼RΫ&>e:siiUdTfE#[XSƯܑ}#g\D-^IAi-IǛ9>pgPA_.knhlRr[#*ac0j*^Eu7NIu޷FW÷%Z)>Ԓ@Hmx5)]sw742^4?k k^R$!&SoGp6Ge~_谶uLY5˙,m;Zg$S, ,'dF9WЭnۺQ_>:PL=2qu=Vۛݾ?z̭3Nn~K_|b,?L` Uc=U[r;DAA7y\{v=E!]}{:rr^*la+gx|Ӏ+ LF8]88tj&p,E=Ņ9ݚ/7UQ^@NjI\S,]='{X/BkߕP#I'Lpw]'yVybi $8JGaX&;Nͳ1h*lP)]gXu=Ao mP~-km bܚ[SUL= RS }ndsۏ8`pԞ'1I}AJ{ej seƀOyZ0Z[ں Xz_yWතm`JỄV`.8_c/]Y?3=i7pkf'=*%&48vK[sf;h'֬ lr,4IO[sCa4#!q4tޓ" x5L/?/ɖPG7!h 6p"nϥ='۫satRb ?fi2j路=Q83ٯ+VK'{`5tS}13? Q8Vd}ȴ wg/s;Iì)+ F-^s_c_Gg/um_tmH+:̲-n첅톍G,x$'# q?o2J47RX1X}sK^NnZ5 d_/!Jrc(tb12JZ?{՚{w>B]jƉud$튼/us!v`VMTlÃ]K33=PRNS/a_82:BnVTf{`¼>kHM \=-5,g)HՀ<'jfLRJb 0^BQpAMGgf.0^C =~8|>6,׸/Z==TMT%۽}sc_#*V;41ևTO2I$3kKhvu{ dNj{)Vr@78vsnQFp'q J4IEX$<q3 AW1ne̲$h3fB|~B՜s,Onl^"&CA+\f:8tْ3].nq)rC*5M*Ƒ82 wbQQohb$"CKj`{B=/ɦ/ ̏vqs:́}P2/lN[(pO?N8ӧN ϺLUmexs"N/)ē%F^&1T%s \첚@s0זYїV$O1 a aw,tw.FDTPne[[#؊ISoh)R+WeCL%ްpxK[yE Lr2v.p}XV#q#G#{X.2G(jmTf HFh&A %gp }fR8t0Yfi. =qbCI*cEwWkN8ew[KSC/eeUZ{='7y~w<#db̠ )CAm%c;i3EdM0f2ZZZf̘A2{Kf,v4Kf Nsa~F. uԂ˼<qщ~Ј#B5VUUqIv.ttt@(xTa9S_.kjjH57@0aWpm1gx`<%bγ'ޝGj Qճ=p##ܙPG뉷< ¬Ӯ =Rv;ºαwۋ[mCݨ b?M4JuDڡKxya{6R!PP2O_"FYNtRYpI.C!>u9N唁ZfjT*p++.9uj*Ҟ[cjv3@{M|W/01I@ث=7i5MV+IqUrdx^Vaɠ=>71EJ p"Wb,G(9Ktzvt ;[3n "epx 5R'/IEE|01:  o""U  Kґb*IJaNWyeZԠ#P ju-$BH >fү3K.Dꌜ$;ùao0NbDV ema4"gn jUg0F;N4ka+pګi/;?W'0(HiCFWLzJ!r1ja$>u8+ i@Ơ/XT9j ^u'Ƃj׹xk{[&gK^vaW]-1SE*H7"H+:,K谾D?:m7bʢ h[Y"l+:l՝: XYkK' l-C:Ibe[OaEE%%eN'Mg%}h 40!c `o8e0Y`2hcS0D6Bn8:iOzk6ҕ)3D Ic^1[,=F<]&= h#$R3-ڼS Z>T8hXB?+/:CJ>_Ri0 uk%j.gNd֊͙v`.1.JT}I*ѯub1:ni@HT}&^g΢!8 a9sXFbC$qE8oF'c HnR%>'s4h->Β :%;c*%w2;-)KGCб2D| }-I2 hrցC}t@+k4ljaT's%f|i/XbRs*JDDL&.S"j a:¨KfىlDP"Vr֩ s̱`AX Y(,qfZumstH4D˲Τ" Dp#Hgf#kUO aKI@MD]V)C@c|x4˱; Sp_,z'x7~TfL΄t&*36 xA@HQehdbaѹ6/d@tb8f*œ0?8P4AJ\t.IիE""n蹭654H+\z3Yj䵉 W Č}(0B^JrѢEC(ZItxPV"F!()e_SI8[<$5WYd;c;{6x`/..s%wn>:Es.l'a^Ro3fѡ}êȞ yΛ]xdH)9## NxEճMon;fk.X9K;'!M6֠u(w 3 d3ۨƠá$Fwqd<]7zP2s0YbdWJbcL锨T3Yc,$%VDϳFf$d,Cd!",1ő %6F_Z Pq-ZXc4 ~`~ ҷqSu?-_u[KΛW^a,[hj=ShέIJkݽ3Y&%͞*X |gysfn-Na~g< ;eT~pQ O'Lk٤)ǯQmy5E%L/ÇXO@dG>V1̈́,e;CM$ N% %_F+<ZآҦg3LE%rPAچ "PEAEX3{ j˭G:!U_Z\~8b47PR@C7:{Bc-*53ju/ 9xW,:];O7GFvwjʘ[Æ WFϭշJTăE,bpYV֯)ZlgWҥqgj$ǫ3`c#~p h:˕ d۲ 5Wif34(R)iLm@f$#e7Bqt6m$ܣi&L^02 (qFB8(#՚ #$[@ P5h%cbz5e^?$lH=}CUD_}s.2ĘK־c[Ovm=s⮇W_p qjyjM@㠬=~as[suAmuGa`iͣ 3|N iVǡ#1^:TpGf'Ť(N!jgH1eH2ҦvtؕD%p%0FQ2vYhC4fMi040!eizV:MO+{*Ϲ~_ //C|t )21H~7c7sW,m?诸~r`={E}jxK>A@C{8Ɨ-Ҕ;li1~Fؕ" )Ҿ8A(z 1I#h>Z德E+̀vn ;|fƮ=i'yM/O""ݧ|tosc=G `{?}{H!?eK vGO_>'WD`P+AnzquɬwUNT-H|љxXg۳s;kem]tX3{nï44mz0\/{޲5_̓q _o0s8t%Wk 3\SXR_psty.҂m&bP- Z )^)ч]301Qz1[cLh1si2Ţ3=%I vH*G6mm&%w]#+4zƚs4l9tJ=[\l/uF޽t߬%5 =Zz[-:@" jo9m̖85,?щYy`C3Ѩ׌\/R ;Sn=Q 'dR١őz09V= Oׅ^'SCg=R.v5NjnwF4Te*k i8kLQ$q4o- Ayu_~M/JO~$Tosbvhk3͹tZCy '>t&cw>oAnHW[ʜ]_.=3teTjޢʙfc-g<|HE\ @Jh_䈗!R%p)uz v nnPt^vߕ>0-+]VvzyP䁽$JЎm>{kn5W~xƴ9JۚշUdN3*W|A>/ix;zܮS͇[Y?>#KO㚹k̟o :rF4 p^uεENa\Szp n*@ }~]7"IA@H7dYO 7[_^\Nktޕk]ٟƁq1k{x}}Q{ F̋39CG_ސ2Z'82wb{[XPq*Sک=*Ff^rKZ^.2p[㞖\aAQ2 0SG]8deme.[P'mQVS/t\v/ ذO「(q8yWybwY^7{zb5l`We=uNmִi? D;^c 3._pEoXT/Ѱ~gCNӏWaA@Hrdw, "xGFsbY3SΓ4z` gw~vk.a`2Bz jHb㏇A96#G!4lnힵqٝVaE ;ڻÃsF>Iui p to= @GK5:G`LZƁgcL:M)));2C9J dT`5Ϛ+9ln$2Tm|,VQ<'Kk{=s$ǫp#ذ`=͎F$U0WuЧ͓* xhkk;um4WfxgYQ>Fz"J8 $9T㹦d`ghA!nmNtP+iw,*r$r #[4,z]j@#kjmFyA`dZZZҚLeFI@8[5=ˠT;ƒ 00 Y'^ w\h GIw6 @OԮu,{AuAqU4 1^r99a>a(m7zx{^0AG֨O FyA`jMER4z& pcfnkmE$gMC > <$G&Lr} u7 YH<CCmM[vfzs˲<FD*i6so1/{zzz;aLR5!2:NMqFL( 4yx@%VFD`L3bΌLޛ[A`:FϸopDZ:MY-v GID3HY^AgvA- ڏwmWďpvםkӝvy.7+z9"Hwo#UKf+3hQN8@}=@w?l9`?EዞEU/ҨI8EU%AGיYUN*5IA߇)j5)U @,ǹQLicÐ6!|1ABՉsPJsD {"]=:! Oj9talK7w^Vi2E`n&2:}T4ќcERuS27H~Y,zy7^j," }"6P^@<^AA4gT-,.ɯ@C3H&H3$tL#Hi4c,=ؾ}m`UM],/W&9U T7#B-z-ʈf3UBz#]NGU^*L Y-!:O%;;{b?j P+ + =,<#(0(qE w. irk mvQ:cruȈSxqK T41ڻ~6 EcYI~VG.ͦS(/ANj՚fžhYbڪ TPdRz`a(A`tZ[[^yK,Q_q.:3KC,ep|Q8S#9LZᎣea"~tS;VFf&ugьI4D$y3lgJ.7lۖPܬvZG}|j[9?U4+e̳I>wD DfYj$0~q#.|>g^G6XHAp&/?_ġYţ ǘlnj.)*#hĄ;%+wMV%]6@qiT{N/1gVŦ̞UQ;녗bcNm/#]/WTG ]Jtz_ ~O(0= 7bG`LL4 TEj=+VY<*|G{2fYywR7ؚG˖iPC3x37# ]>VĪ:xG5łMxŘpw[f_\j);8&n" @IQfPGMsG~sssi_YޔmiX3^_SboC7 e~a;RD^z"ݑ=sz^[GP1Vb#//T;MA@HSp (3w~5tg).hVݯڳ!}8G+ & z;_| G\U BW ]3/yyז49M&Hx3\aG7\4 IDAT;?P=?T񠘻 3IS\\,Y rla>e X:(0Ý(p1nkea Qwp|='c1teL2X‡^y}X 4˯_k.Y2ovv9 l&TQ ]`=ul 9J̙;Y;v)Ë-`D̝;i<}ytX#-2ELW18֜g"/v-q t9xg{zzp4<CK% 䥗^7)_v%\?~Kβ3 =99nl#"m$tUs8~L%g#`ͳS2 3fYJE/Ř1ڭYF ;dKkS {cm} 3#F_|ߗЇ*I"FQQQVV4 ~ eWHjDR4u@8k:ZQ~0Ոcw 83?ۺz71i!M7l/} |mwd 04g7pCUUQe0 掚>@rXqLʢH& W~~> 9' c&Xcwf}mdG*J_9Mׯ_O~W`qGTS<\j_CW7''a9{"?+IA@LfΜSDfMSB9|@A+͛ov8đE8^K/?0H| ѳGPP"YGF=hA/X.Wҍ3%aqVa67E>l^yla8)K/k=V?%Cf_~oDp-|Sw eS!zHUě`T[|L@ 9;CGH\-^wtEt(WQ!#K JT.YNEK*>SAxMdOF"ikkˑTY"ىG Q.1Z3„2QE`P*b'ѩ:;g+UTH.9aE*Tmh*1JQemsaEӧOP;K<6*VG3P`X$+cEJFB@{F12M7O:8Yxϵ5RJ Db |Dہi_k 0E4+hذ>|, #Gs9L'NIC&ٳgϙ3G2@PC,|j %&j!CP.1G|Pe˖ajy~gΘAX+U&\ρǷ4TD( c-a*ZW_8b 3![[ΏKQQ! ;*WDvVooOEy9zYR\JfP(.ZQ:(9Ǐp1&{OqW_}?y85ɗ!Ch8d?At1lp xqY+}ay 2&jIB\-DV&QtUeG(*cc.PR>GFL`jVXF9 Ch:YW0ݦ/Qz#>'1#Ą;D.cB7GF\s5 j!W\q}ihGI  PKsNW QɒA?;''77ץ浰t ;vG(|Zt0>zD` e7eX`G{{VNF.B̓K;:h!60kDNtP$ {h\{}>NjkM(6S ̹F@of7ܣinrϱk4c?%0Ģ\š-1hgTrE.iʰk VG:1 ƺZXۖp+ilnhlj?AWg۱G+:~rjGxdjgpէOAAfuw5`;\NrWVU1g1ѷpcǏege:qiӦ7{Y]]y Rk wv߷fF Ғ "`â\5;4FyqB={IQi$UG$j5qܷo :/[?+])@j#`e}ݸT@{U?t$DZ*Px$p7$KYQAPzm8^U qPZ\܎(. LvG>&Dvˣ>4{U@ڔKkupЧJ尹sgM+Gy!0hވZ,~ơ\wg V4qd 0Yol^x??YzrX9A@4nZ|tG"0=N8<,-˗߀5r@tTKj  +35qd##=z!8Jcf>RkA@ D@c#;Js|~dV##!q`iA@A 5lY#+8}۵kŲ?̾XVWdzu7A@A`8 i-đ=c ۷o?I qLڻ&  pI7kRlQ͎dΜ9\^^tUA@" qȥu>Mۯ^{Κ56JcZy  0(!_+3fđxrVKM!ׯ'See#<2}t☛+-a)R @z" 1=8jcoo- Ǚ3gBit8T%  fwL:hn{-,CW5Sdt#[J8l @! 1nxG?3Ϡ=c ,cuU{<8^%  >wL{cM`)>}cO?4J語7oU-1FlELA@H{;->SAhb8nhhA_O|⩧BYc8NB  Џp~$2/1 >쳧OfhΝ;a<ŋ-(c3๐*  q 1RZT7:7lؠ+r{?a6?Ld4qt:xJw1^A@H,gRk{Ɨ_~+Yew{K6ַuyA9X1Bvq T B)@vP(t#GPehe-^Z|9BIϵ)CR  Hc*ܥDب͛2 .2Z-O"  @" ixSVIvdK/Su0<Ӯ7nܸk׮UVqB(1  )O^g֭Vw'?!Ї>$-$  8և>v옮!!uW\A<,n $HA@ " qFvgi&\%S%r.lLHRei +  pG X|.֬WA91L^b :22=: y}kFMjoJ>L*pA IHi?#N&I3C2z~D/5 '<=]w_5g/=ߛt\7v{ OVOYT+ʙ*UCqLxz]P)s%Jlx18ܼR`6~n]*;/Ww ~w^dXH85{[q aw*htUn!+6U|QٔOf&ž7O8 Hώ΅>jaY5qvLRDywTb0]W/=wz!U?[.c>Dغ_=: -.lpGK>hܐ0f(<^ߝb(F>_ȥTeA@!6N& gƑ@KW~M)w`N;!b;Q|8bP>7Č)`TMu8MdF{$I3&li>q=A@85pJcb &1nTQA@! 1A@A@A@2p  $ 2,]zi 2& )sPA 5'-3&U|t Y^ƿ毸g:Zn'Fk\Q'F.ĉ@Nfڻϫ6?'">;Tbݏckӯ\t#dQT=fNatOt c8{h͜"KNl5=""z@jm9͚ [!\Oh5歼\^&E5/>g÷M~HA aɬ^}ı7ɫZ?t;#G@re˔xj]{u+??{j13|dm"G}֕^jn CY7֘~_egY]|r?,0O?nݓwG`oݏnڦ2n۴A%m:lTNPي2v^Evݖ>پ*/}'{bC/?mͻեyg_zZ- d&v2w6]S濺+&o[]kK-31Zmg8)%!ܾVwK7_~-e-Ys~?hg6a6OH]OkYjΆ7)?Max g.=%+v}k~X[/CeknIpNm_Fb$+QNnN,o%iıcSߺ?xA!wWhzs;N!j^5Ni%;:A ='09+Q 9e^Q5T pe*J2G`7?R~޹ט3o߹0oޥu5OVߗ~oyNwڻnܲ+=sC'' q2٦vyN#4ƃXZpDy6tՖ3#'tMgYFۼ.0㍟iHظ%ˍ@YJ頣M7&.Q2O!jwwrx^]CG3]z@'${.m;sDT&=zM#?0ј9h8>(feRa}E[2i R-)0׷ Ք.1k/4 9~b0&6Vtx-+cU3F)Iqю5ey=w-: @W$ L;\,:byxr@g g}֠NPAwWYW?Xwf×w?Gr|¨>Nʜ<x$3joϬT<qu￸Ӝ68g^=/v3=ޅik)$ ?ϑ.?&74uj=u7]hݱUY_lϯX-3w1֟H&Y;#EFH&7^w'g*ݲqVC<;N-+**S΢a_/~w<ڻ: ma|<[V lL1S9-+Ikwas-KBK7h( GSl޸a%JIr2̽_wn57`E-R@@Q9RGz#w{zu6FE_Y{4w{B'Pl IDATݳbOVuQr輟,5ꬬ_f}Kt}š5rkp#tVU?Чtlq\2&﹨o_xyA@H?&(ݳEmsAQ! 1nFl\7X q*Qђ{Њː@'bdսnV1i嗀 d<d;Ylz  ^JMA@A@lҶϚal Dk#9cD$C^ s3U+ߩz4iZ5@ (l۝N?‹ۋFLT^gO]G#?xB!YrK܀䎼s _nʱKl;naR(̞=[e\>ZBA =#/~`Ko499%Ux"b0!_z wZW9܏]yE3Tw8c}' W^S1Tx#J NI&'Eul&H}pH8tw>-N:IU{D=1];Avad9]EmG-**/[P9͊Dx|qqB67nj S?ϩ[=/h"\vr`ܕ97MABFrltAcC}3|k_khhH;Tyt a/eee_җ%fH/D#0AFB/۷zm!6 [VBhU0G4[ "[7:w}3w#DOVʙCy1}`0hNxQDgD/ q\YYY999(OxhxSM >fgg.y8$mA>Wt4vݜK;a]0,'h9_}֭=㣕wPHE :܎_u}CO01{.] %[wLE]o֎; >sIK-Zdl >!x0sB<`BL qW-ZL‚lk཰&C*Xxrsft>ӺV z\Ƒ(PdLLs >30]thw}EM/st=9>32;!nHWYa>9#%QO;%qh34盜@91#t_|^:~xtjkk?]奀FKNyXǓCh4N9,b@"K{r&hL/>Or-xinwX$o>ǒKd@82 w^O<0;3!(1W"鮻"LQ5kj燐ʮI$?чE"y睍7#)U dP"zdDoluiJ{p0( M7ywEw`n]̯FGxR\`׮]Ee˖=4:aMOݛ wL:Lt ZL;޽og:k,DGioݗ 7CAZ$Hd`eC"Yц qQ} 9tbW՛o2(%nb)18 RBnzя~cQ뮻=BpDH$ˆDjəM"O8?,m$3o6&ְҸ!xdA +sf}1UFGX I^ 1/Q[M9x(6oG>rA /y&dtµ}2DٴD2jƛLͦ 69{" noş'MwyY22 ),f; UZ?hON8f^~e#>?O;%[@9VlI*4 Dnڴe;-zjfp X^l}d$ I~%8fk<34iثT8B܂(s cA_ # ~Ks%.$b!xn7al&c۶m0$j $0>vA I] >%G3D7ܱo،'IGU@0`; jq"(+f H!%IU;x+אDDJDDJ0i8 `"2dhB׊TMo/A8Ӫ`Zw0nJJJ6S10&K̦$]6ԘJP#s# Ο8 Y5g0]>|cƯP  A#-^xPɺu`ZpwBA5rф?,d`h pSNmp}"+"2HI #hgH"`ܽ'bI@0"o^)(|rA5` KwC&-POGI @SD |C=D8ќ +dZ!e$ 3KĐa-`1$h Q~_~'@<(N u tU z*hA Nw޽{z-O~Bdk,Js~ *wiNk.kmm象z.\^k8-w6abF\I$q$ we$0~q…09DJ !@>ĵwڷ~owEx`ޑ~M>Pf}:Hf>B[3p'66GQ|?II,S;>?DCC :Q40sh!xI ^!7o ਧW^y6c#n_Aj:|`+ 0s ;lnpl1HvéF2@`Y# #H.^}텅xq„ 5{J ٰ#y'a+;%S;zOl./Ϻ"%d=|s'A~%D ٫$7H`S>s! URHhB`B>"#(#ЀzɌUr@5/Ew@g8voV"8"}W5wrs1$HR|SAPXk!"jrj6Lp0:B렎111lM)m!LAv3 ч :Jb{: 4RXAO $Sp ?iifH#H, $HU! !&觟~ Hqq?ϖ/_uĞy |)^B(8h؀0';}w p݈暄{ .;c@D!a="&$a"{u>u8HXC$dp8lmŊ<IpIy;C_v_0/Z d|F @ꬽ׀U!L$6pSFp )Cx>>6?K^5JUC6߂#‚œ TVVb sՏ>_R~!tLo[!b D؄#r8IǚA־}.] . ñZ$C#!e.B+~B6 M7+I$XF`>B$#e"%؃>|8KO7dDw'=áKƎ`LwCX,Db ]6"hXL@f H†L-BdOȱ^زeկ֮] g2SH#Ou8i2 8Flv? ނ"!l&Zl5773/ȭJ!c7ӜfC I 9h Rl_43w::BWP`IT920FG0,uۉU \! +Jjq͆lDw=.0J==u/@|`󃵍p|:7"q;dB"{w?ADo>òݙH& N> Χ\ϟʃ>:lfH~,u 7ހK=:u|{= p!q5ॗ_~y͚5D9P8Db!I$/Hei#x H">~j6ۋ/fٲeAAA}L8]R9l|X!̘1^#y<0#]AX{>nL$)OD7|&$H k6G0p͖| ()f }򗿠tѝ@ I+?;O{E!(dv,7|~CHuB<ܙHvc"GbaC[(DBM^$ AE6\q8xG>{BKR`c DzLm7:!-ás8U7ֆM5J $E$bH\q0𿆕׈7A5qYxb +I`"Q ?k׮73"h; IϪqsBi "\Isz6خ$W袢"r͛GyDZ7:Y*g ~!$ p 8{Μ9p2  %ɠ̈́  t_?BwĶbM@D]CVi?5G *wy^  g?@Ow (`dIHK Ilb= i5C,EiXA|{˔ C3 zh∍OdhmA:f+*kD 1%K#ى1XxHIHM$XLǎ5wuJ%=|hƫjt6f9..ntĹJCNWO+*ګSӑQA3i${g1--b15>LZ]6`5y =wܑduu5}ٷ~._ $iXY !] V+jTj*[,Z`KH+5P㨁z,4D0HV(ĉ:HIXiDb! Đ =B-qQِD¸}( eC9sL-u%4O!`b)b3w܋/BGMSnɋd Pggff)0w*&=8*i(g '-zy IDAT愶yPa:j$=,p%< }@Lf"v<,8!Fg|ܟ2e @s=*"w1"pRq4R%PL7bI

qŶ[bBRt"M׵؎]J]QŠdZ!B/3z%C*w=ZLo VԙɑYN Ѡ3!-V4X/i8}b٠EYZȃEi[閇$.ؙ6Զ[pbFR،Fb`ZխNsxE\" HHH" #GyEر-]L$$ }H%g䱿2[B9`汃l = I-m.|uJ&@zYJW4JP!4[ *3dLiRXb=q #՚l%6\lѰLL<\#DX` |j_͑#LUVjrwLO1!$#X)(@1.3[[pfG3F^bq`@$h(F# bZ `k''XkUͶ&>Xea wUp\hC-H" hmš*K '3 KAvģ"6-V~fbgN0uJ0 ÊB Ō 读1(C y$KM &nT$X (m0[**[kL6ͩe іhA` e<L'\rPH >A71BgԹ0G_8qSp+.YRlrȷIsWlK^dMmZtef~usUI1' !n Noe&Evٙ|1s'E@%2S7П~;D^7Llyө҆seM-vH"0EM_#v6M67YيC`&fL%;A:xھˣ̌HYrP|1R)ocN knl>Bv]8|sR?ƺ$j[vߍOk^"͸Cjp]6$`"%HMx\pu;H>ru/6t'ZKVcԌQC-G?c`hGM6&ʵ: F-pӓBS .:j;vU+gF0),1;駉=yNJ6y<~ dqSJdy6 T+zNʘ.R~)*9ID f #I$L$#bHkƁ"5a I1d];zTEs$Qz> C}\nb.':5ܚ*j-L`Ѣ;Ww x"9iNZVE!0$PqH ǃ!i^kjDw BFQ)WW!,n3jq2'sW|JxgI_>N8JMB$eliR_.k>-}c_}qH%L$8Hg2@0{:!kvLLHtϽKHASPz{8ˍzḙ11i-wX-h:kv,<BHn⩡vÞ ރQY" ȴڜ0˫iX;,rZv0F&YDgƀiJiAA4U`uL:)P[^>wevlzlrE)@gj+MܒJosQE,囜Q[K?F f0oHE"X4L*6 *-" aelA>;v H@ʂ*-!锑MxZU0GK7̊͌bգ'Gڄ=g ѢSi3w[y3p ~Woo:1)Ҽ .-@!)PSz$[*1E)ԛkZ0 UCՈJhen R.TYB@P 6D8+tl93xnRTp2_e679[VѶdh2g]+*\g  W!$BG$H"%H"Sdmf#/0eee=#pF,a/6 !),i)E*sI] F3CJɨY^i xD8>?إXZ`Z2ifWć铣>&:K귎cN $Q}==>j67H{mL5wVYrxzޤwDRP?Uxj}QeKU=!X@5p])Wlاc4pgQȸe p獯Zv?,%?8wq  qubSp`ܙHGDxH08?n5?)ِGuZI`hxh.oE1zY1ba䁡ڄ6”:b\-ͱ,ECs]ۍqS#E:"q |5d; mPy5$"c QTU>^/O {&6`A@˹ (BцIA1!J6|L wl"tA'8ƽñ$IŐ'ayJªh=;1w M)"_i棴"BGkx?|:x FGFMÑ2"K3aB8j㨁z H h>p|eP65;, }zC7)(/:dvԵ*[8 KJO 19,Ҍ@x$WH$RCT%VTTlr.})p\D\=LU"PUzcaFVXp|CFo7se w N ne@IsԘiQpt)43DI "%Hdpf_xL$R7obHĉH Dzb%.R̑KxP#|%yYzb)nۯ6t?"K*O3Ã7oE7[dQzG>wQ8WO iF(WNQmdHhp0xIɓ'A5qSjZ]ojȕga4Bw|h υzt?#izb(V]<@><(8xXy$Lٶb9AN7^XlR*.12 &s×3-1&u'H>"A]6)2}qSZ;_NyIVjPZhD1%h^L c5^Zu-FL Pq4<`D8qDže7G}^ +%$ѓpa*1Y#Y$$'nB0̘8U884OAb=E7gRتA긭򎾟zP^/Nv?/UwVG'M:%R7z1<>ՎS(xJ뻯ӫi\qLDGݙH!ڮ[0%*Ӈx׻lQO_4cޱL^В>c81 'O}]5 S1Pg' Ʊr+_?wZ=gCk 7)_y"{V'Wk7O8券n4xv~=9\UѸCwK' |$A^֜s(zx=fn֞ÛRz;I^^ ;4+B4CeZZ@r*R;Qd奟N \1cs=yggٳ/ e̖-^>i[lOba"Fmxhvs*[_~dQimSSSh3=teړoͩ</Zm:푵`Xr>?YXT[BLl^r_\RG{2y?VޒΦwך%kgx 3gdf0[߽(^w58 %0ws9]>xS?eY=TjL%?R%>2ibR$zi1i7߿(SEM ?#ά{ґάYzhO9HGK13vXtߣ. f5QVGxj͊ j賙S+47cGu&026濜-tҙ#/|2)eہ7E=,rܞxlu ÔWD|gzyoîC+ =^7@?؟_~]dHtnO!0=~(?_Vx0Hm|6^yy,c95dW>uÇa+~K+>wNsD=\]dAgsp'~걭=,GwLJ7^~3-<HZK|w-wAj5$ )dnؼcZO[>+( ;m]׮޼mǶm[e~*d:>cu/[ٌm}bMOZr\ Pm%;Tᲂ~vKd1' {@N5kFZ2j,gbVn\1yxzId޺3+7E{# .tצlsScE #U_ F}7/9gG7\>O"F4-L;/dflik~c]b~"] ;*Y8R<3=:O;sP)@j8V4-k\Rl͙Q+Q1za#e'['0GbW|Zu3&D6,'$OI m*YrOw1|:v?iJfD(J>%?5h8W|RuKyU-g,B+(WeN(=,` {EZ:{nnV+;p=wf3Le >`@?-uyt֘=7c0L3=5wV{mpRy  `w8FŚjk[C8+TIBy\-5F+ bDB՛w p϶ D7n]k+ a3Vڴ&eh+]yxFONI#jpb4a-o^)m$(H ʞMV*^qƃYL$yobqI3kCB{}p%{:#'#2V㳈;7ku;v;zrw5XJ!2UT$@ W|4EkWr/OAM?> ZI2 g hw諉|}~za1.'-<ϿKo2]dmpGm_zH{sC{^P֐;8~{jTeV~_rn>rJ#E^1 EU{_]f NG]QΣKs*kþ߬ĥ#܆3S22 Ka-knqv:!C192sR2R-$]?ZΓu3T9f"u¶O/)3'$P%>zofEyZ6a=$vƆ[; ROR!'t(NwC֕OmbӔGu+wTͣ܃1tW9?[t&)]E#t\9;Wsv.@?0}}9՟MS̈dLW )I\nöoZ~!}ޭS\% 㘵\q{uz mnRuz㊭{gud "ovMkNٝd8~nN طwmXdg6t}谖ngS2?u$3tQv*2RT"mzI{h*+ H?dQ-Yu&tWPqhI@oH]jQMPu{,ud,6ŬEM,{ #e@x+(|8bT]Yavw//6N:TQkp=_꺝11˷ݽ?u8lo#owCGVHRo(K]Ca1Ac1-HO)(85R@J F&hA`n,ٚ{gƁ@Ro/D] ˿rǼǖeFt\@溻 \-!dcCK[DX".Cp ք K>>@NjK.Ff%!x K㉜v .v1#{e)5RGh }LQ.kBJT?|| nY}>kD+ͩ1bF$t)4tx~ ьctǰnE<iB \×7LBnD?+͎ԈCߤ CtB̀LT#"w fO{8nj:wq3p("rN!P;? YsRTY1,gaVD_FFUT?PI"V{ZZ AƄEqQk60'i93:̺k?m 6>FO1gDBtG%Lմ1 ӏX44*8aQ MjHS4EaRb<|7h핓PlA*ѫZH N:Qb@Be0{YDQm*` ;hWB"B"Wѫc;W!* 6D+`kldG!}`1Lffx秥2hl3G9CMql|0ku CW5f"DRg(u=EM#\!!@f_Ӧ;?+!:uL:~`w@ fN" xb`-Db /;6!T*:&w. @윂eXBmxY(`ݦ%GX*z,.X~]${C4mZR)k*Jx1/VOV%4>!L$8HcWYJZe)Fk>q"Tay5ziNȽL3 EͤՎ PqDI HJ2*Z_&+G!X҈FE_>rY^d"ɫZ8vۊlsjsfh-p 2>FiQ;26%71 :!BK"L+ΦGMVG[1QN` l᪨̈& _8:l6k,)c`6/U+XVB*BJaɆ$9A e4ڏw{ƨ'.2Ň^ߛ_11F҈O޽; SPGdG](&#\r\jgMV☇.%Fj#4b7EZ'BBbf҉ۄ#\#A *dQ:8yn4XVVЉfF+ Kt}ok|sZ4eb1 fO+)6G.5u\~ap.=Ts7U~V:n C[m0PKGBxWEyǑN>/K CW%ʧD9\@GIC@V`dV'oA|Q@IaHó\9䕜:_-t;tLu7ZO8券n4چPG*T ?xVe=MS8rc;gaU#NWV@Gpg_mua,Y=*.,;TT[vg1)2ѓ#b,[z%}Jb*Nnhy ]@ KP!Ў\ )1_LSAN`#Ww8oMK$(!QuoUFm@#>bRTTg- Z'@D A-9A 2j 㷗/T4?8Rf_>2yN:n=c !rx\oխp>7uQdsR0 h)lLV[>>SP8:G$1#=C("pvn,{hSƽD3-|WTӤ9PT[=٠eE iHAH6cMUDb_3,sey[?yYڴU}?>&&4tFzf}JA\7TNz^6l6u#NWW_W>>CX_qo1k ./󏜪h/~ʔ[2߹(]hݞy9ηy)4>7_s0ڽv޶y1~ޚdpiXA\kKĊ01Am]eZe+qWbH$`nzBrW<أj ^iS0[]{V!dwPkSOs #@xVTρ̬AX*!5f/8UCϡa0e(ɕ?XOxsC+|Mkm˒=_\\Pu+V̊tڪt<,س)e-[n]v)P{\N1Lݳ%͔kpk<ŏXQ)..۳%cݾMpa+_aSn$s7.]U$#q ht3o76BEV"ֹFgZ Q~PRlq#Դ8|r{BͺkӛݔoH"{m4Bn)Ƈ>Gۛ'_k"\nBҞwDZᶅՊr'H6Di&@$O 9`SJ: WzEd??uJ|q\uCڜ*WKlcuyێm۶n۱yZĬl\cUUsl^iKG\7SDId㛿lgm޳o+/Nng7dg 5ÕZh;uߟgNW%\UŸ-w|T圲|M9WjdM4/ o1ܞh7GEEEGGGFF=5eGg !wб[eFyrw9+!W/#c^5&N/,ki?$ AarCKN;o쪣d aZG7dY'a"!a$qGOnu\Ò~J)m\6&}?goʃ/oL~Xu;#!K~ױ`&tߒSKڙd>S 4Ln\7ϖ{ԞqՅZ/JhgБ|Bܸ{Sv 2Ley%tÌ_^ebюÝe'i 0 8Hx\|);J^3* iS*E^DB I7(jP@$ q5x?Rη9}30v'äbsv3@ {zMRA1ck~z&w\c {nOR+VNr0 O0p])㓇A rG3W IF|60;zlA-@*&*.4:I_U=[ 6yծsw_!~wEo]wRO[L$Y8}"N\(!_]Ty *:I܈4ҝ'z Qyg]BG3&2kjW.teRm8Hk᥿36FCӒƇ3a4hM  lXX(1xp"F W;w@*1=փo!@#k]\_u’5]m3b`"&wӚ5ͦ3V~FiqDc 9c431 ‡"@LL pJ"k$8_;R@ZHasu?/)9o}̽TvnwDL,~rYZ}8502;R.1o%7ESg1ys}Svּ+'`ۛ˼%k7pW1d3& 8qH-H9/!3@r_H1sru |ϧ yGrG_`f73! Af&w9+?VO"nq,/wnfD%v5+;X,̝sS7l&?c{t;+;眨}scƺ;+\k1sw/;BA<+L򅔨2U@,<[t)\b 0Ssc{ O<1c`gre²慏7.|;p̓[P@K>INO\ U""w`!0$z;¶ŧO:T\ؖW7X[0R ܑݜW˷8Qb\Fh83$ߓ6P 1ZS_;!H^`\a'rGO^yfg6/Eڥ@DL;斾4sy.=g!GY3W=2 A -dIzM[j|P fETKfoi+pu?s"S a;5:,uap]O/\s7xd̴uP! b2-a"SW:;cAԮ=zDn Eʚ;Aq?mvy3+tRǝ 1H3b]~ Lz#t'k ~0YpnqY-:8UkW:ys8 FL;"='6-G; -B 8Atp#rG@XT;zlRzƈРms.[ݒMXcu,+a2esGJ gh3՞9r%7ĵ%Xj:xJfg$ĘHvVΟak6*E%ۿڥ3KY\{. K~E\W*_rܑ5v<@Na bufP f sIB n=\XgT22Ek}]z[&W{7ly3~=b'yIl7#]%P/0h&{5~>c cRXc`%;w@ ykulONqR”D@)B&х;zA>bwHFd&Zk ^hp朆ekΫ\V~&З9]qY,W%*PfvzRN߹HYqߎ"1#sR;jbcnK}T'ްAƼi6)D5 M{npK/g~v_IBYu&?o+b_F/6rm%1PT!tADڌ !I!y{c-odUw7S@Qk͍.TC艭](!ӫ&rOB_tVr\iGuFlW[@6@G,@@7f+YVQHMΦhfδ5+)z\a`!> ms<TQ# U& )m&ņdj^ʵ,BHbvq6'Ϥ IDAT"T!`V=5~܉`η'w?Vn1ǪAJfF%V'`"b):@k6QB@'@@5mfNaMv+/ Ru pF%'0lq LZeRkz!p62v7 B…DE4:|wZY* E Jqg%8 !`3 Fb6ieC @+D H#qOG(&n!<( w>0|CSaQޙ,)Lha@~?Us0-|#.h HH}ů o ;>||.|Ў$@HpmV^n\\P^4k2̯μy~SMYtdj|a= S Am Z;q6 GБjGfg @yGg-  @8HDl IԂ(@ #_uj tzxyP,jMUj^=+me.6r%Nۂ!kZ yX>P3GͫT$}&! \@78U'p l"/F9{Gcu-|YpWB>9o$;IbH$qdӪ;ZC/+ dH$&2wDap4fVdhph͜X [lN^:l s*-BM4"52BatkF+_oahuBu(p>D#E2"qF+tw`Pɐ2+%eIeog}z Pc!$78 * y 'ȃUÌcC_&Zdfp=VSx 8!6{Ҕj) ;b,ߍH=GF@UZ V5 0|Cz 'wv~c4;+6UpPxcZe&F gXjAMpp21zW2j D  F`dNcMkq2:,U$q $ِhJ؅`KH% !be4iԦ+6kTr pN"x=6Hp(Y {iX\ʚiB&AD-+lMJ5*sXB6 9Ա=:KQ/tC6ΫX&T-V8p5*X( 5' C[KL6(8l EP)]%"AG$3@ 6[]Wb6ؙiኩFY\fFZ]x}RGx 73BS vpo $+)`-AƂH@ Z@3XS\*J D y O @> 0 tː&V#,% Bzk6e"K5i܇_"BM-O&)`0a'{9Ab!9p$6! ʱ{dd,<ޑ! j cy  !+PA"fw"X}9l;Z@j"$Jf'[bQ]i[")I |qgϭ^C5TD%]c^=%!J"j H$r`RH\;wj""A$$5q} ;XWH&Yu}(adB(mTtV~RbA %{8 S,lWMWi|$0#s>;~}dJ'>;61BNl;Z XOQ^B&}B!<%j@!ũ)knˁ(JWuXRgµVAce&B8"v /K9EpfK#F@ܠZH4dH2lIFkH2WzP !fްH@'!E\kq* %*83:H[`'T WM ݖN й@f1A,Be!%0 x@F"?h_=$#qx$,rܺ(Fwm=$AĐw@$9v9Num2!*0Q v(F?Ё6'+ok2&dU:xAe %#BaW3*.Ibo%/3C&9 nDB*c8I@wts*ۙǮrGFvU@)DO0;C@,E,HAe$Wp8[umTMECDE7Lf\#JLе%Z()l(qxZR[HKsI!0 DS>Rz!qȝ[GP !D+W3#ֱgr X %*Y[**zMyU0DdM¹:>X'jڒ6ZF !$Hm6Ky)+&C~>Aw`+ IHB$:G'qE"?I%hAj")B+ho{۶8˲8m"b? H6R7~c_9qQ%E>!9(C3B^.y"R?8?lׯ?<|ηo?77C.̿L|}֟O8ZI *T́ݺphKxZΥ].VL=^ǀ[F4X_ć PjDĄD˟^\}/_M8MfqUlχ]kdbBs@6m*ϼqHRArFz!W[hǽ'C3TY*ӮFUk%=$I wﯿY)$bx$~|4}Wv~N_y򟧓''E=% QQ$!hn uWAY1[cBO@R[4H#Cv@Ȝ g˟8<[=;ΓQ{S>mx}ͽ'V2n|R5V45;P;~L}Q ĴFɍmڿ c&LkvS=kl9"or 'SuBrbrth7_wzw0|xXZ!SL>.;V 3O[sIrӫ4R>w.-/@zG$5ۍj2#rPC1& BA[8K WGurnEm~uq֪PRVaX{urr*7\>:Wy ! L;))s&&d0Z\ l&&] bǻ/]ߟ]!.fh!cL& 1 كqH{L*di5Ɠ5Vil~ˍP1afo@E?=_NvvbȰJ@XĄ< l<|Ã5b!n Y* Կ1cҗ'A||E jvx rqx35]K3N^ɸeJ9[nsjr@'*[dE@i6"v٘^4bPPA jT8 k@Ȟ   Y* lCjwXzwJgבCD"EYiqGe6NPaeqdۺw?0k p'!q@_UcEq4H;CH`m\?Ӯ˼* vy7逬D*vn5Qej;fxjM:_eU-y>{-6՝ug܆ !=Vjr ƹ.laBAHBmИNc'nF ?j 9Z{uX_?sZH#aq8dk?3[{1^EP;uQ?zɖqlOF@@d)I@ +0Ļ @@ڱGI @A@< @P;JG vt @ {#@P;: @zԎR @ @j^) @Ԏ @^c~ @jG @@ڱWJ? @s @W@+ @9@ +vҏ @@ @P;JG vt @ {#@P;: @zԎR @ @j^) @Ԏ @^c~ @jG @@ڱWJ? @s @W@+ @9@ +vҏ @@ @P;JG vt @ {#@P;: @zԎR @ @j^) @Ԏ @^c~ @jG @@ڱWJ? @s @W@+ @9@ +vҏ @@ @P;JG vt @ {#@7MUWIENDB`openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/sflow.rst000066400000000000000000000141701514270232600246200ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================================= Monitoring VM Traffic Using sFlow ================================= This document describes how to use Open vSwitch is to monitor traffic sent between two VMs on the same host using an sFlow collector. VLANs. .. image:: sflow.png :align: center Setup ----- This guide assumes the environment is configured as described below. Two Physical Networks ~~~~~~~~~~~~~~~~~~~~~ - Data Network Ethernet network for VM data traffic. For experimentation, this physical network is optional. You can instead connect all VMs to a bridge that is not connected to a physical interface. - Management Network This network must exist, as it is used to send sFlow data from the agent to the remote collector. Two Physical Hosts ~~~~~~~~~~~~~~~~~~ The environment assumes the use of two hosts: `host1` and `hostMon`. `host` is a hypervisor that run Open vSwitch and has two NICs: - eth0 is connected to the Data Network. No IP address can be assigned on eth0 because it is part of an OVS bridge. - eth1 is connected to the Management Network. eth1 has an IP address for management traffic, including sFlow. `hostMon` can be any computer that can run the sFlow collector. For this cookbook entry, we use `sFlowTrend `__, a free sFlow collector that is a simple cross-platform Java download. Other sFlow collectors should work equally well. `hostMon` has a single NIC, `eth0`, that is connected to the Management Network. `eth0` has an IP address that can reach `eth1` on `host1`. Two Virtual Machines ~~~~~~~~~~~~~~~~~~~~ This guide uses two virtual machines - `vm1` and `vm2`- running on `host1`. .. note:: VM interfaces may appear as Linux devices with names like ``vnet0``, ``vnet1``, etc. Configuration Steps ------------------- On `host1`, define the following configuration values in your shell environment:: COLLECTOR_IP=10.0.0.1 COLLECTOR_PORT=6343 AGENT_IP=eth1 HEADER_BYTES=128 SAMPLING_N=64 POLLING_SECS=10 Port 6343 (``COLLECTOR_PORT``) is the default port number for sFlowTrend. If you are using an sFlow collector other than sFlowTrend, set this value to the appropriate port for your particular collector. Set your own IP address for the collector in the place of 10.0.0.1 (``COLLECTOR_IP``). Setting the ``AGENT_IP`` value to eth1 indicates that the sFlow agent should send traffic from `eth1`'s IP address. The other values indicate settings regarding the frequency and type of packet sampling that sFlow should perform. Still on `host1`, run the following command to create an sFlow configuration and attach it to bridge br0:: $ ovs-vsctl -- --id=@sflow create sflow agent=${AGENT_IP} \ target="\"${COLLECTOR_IP}:${COLLECTOR_PORT}\"" header=${HEADER_BYTES} \ sampling=${SAMPLING_N} polling=${POLLING_SECS} \ -- set bridge br0 sflow=@sflow Make note of the UUID that is returned by this command; this value is necessary to remove the sFlow configuration. On `hostMon`, go to the `sFlowTrend `__ and click "Install" in the upper right-hand corner. If you have Java installed, this will download and start the sFlowTrend application. Once sFlowTrend is running, the light in the lower right-hand corner of the sFlowTrend application should blink green to indicate that the collector is receiving traffic. The sFlow configuration is now complete, and sFlowTrend on `hostMon` should be receiving sFlow data from OVS on `host1`. To configure sFlow on additional bridges, just replace ``br0`` in the above command with a different bridge name. To remove sFlow configuration from a bridge (in this case, ``br0``), run this command, where "sFlow UUID" is the UUID returned by the command used to set the sFlow configuration initially:: $ ovs-vsctl remove bridge br0 sflow To see all current sets of sFlow configuration parameters, run:: $ ovs-vsctl list sflow Troubleshooting --------------- If sFlow data isn't being collected and displayed by sFlowTrend, check the following items: - Make sure the VMs are sending/receiving network traffic over bridge br0, preferably to multiple other hosts and using a variety of protocols. - To confirm that the agent is sending traffic, check that running the following command shows that the agent on the physical server is sending traffic to the collector IP address (change the port below to match the port your collector is using):: $ tcpdump -ni eth1 udp port 6343 If no traffic is being sent, there is a problem with the configuration of OVS. If traffic is being sent but nothing is visible in the sFlowTrend user interface, this may indicate a configuration problem with the collector. Check to make sure the host running the collector (`hostMon`) does not have a firewall that would prevent UDP port 6343 from reaching the collector. Credit ------ This document is heavily based on content from Neil McKee at InMon: - `https://mail.openvswitch.org/pipermail/ovs-dev/2010-July/165245.html `__ - `https://blog.sflow.com/2010/01/open-vswitch.html `__ .. note:: The configuration syntax is out of date, but the high-level descriptions are correct. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/ssl.rst000066400000000000000000000327751514270232600243020ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================= Open vSwitch with SSL/TLS ========================= If you plan to configure Open vSwitch to connect across the network to an OpenFlow controller, then we recommend that you build Open vSwitch with OpenSSL. SSL/TLS support ensures integrity and confidentiality of the OpenFlow connections, increasing network security. This document describes how to configure an Open vSwitch to connect to an OpenFlow controller over SSL/TLS. Refer to :doc:`/intro/install/general`. for instructions on building Open vSwitch with SSL/TLS support. Open vSwitch uses TLS version 1.2 or later (TLSv1.2), as specified by RFC 5246. TLSv1.2 was released in August 2008, so all current software and hardware should implement it. This document assumes basic familiarity with public-key cryptography and public-key infrastructure. SSL/TLS Concepts for OpenFlow ----------------------------- This section is an introduction to the public-key infrastructure architectures that Open vSwitch supports for SSL/TLS authentication. To connect over SSL/TLS, every Open vSwitch must have a unique private/public key pair and a certificate that signs that public key. Typically, the Open vSwitch generates its own public/private key pair. There are two common ways to obtain a certificate for a switch: * Self-signed certificates: The Open vSwitch signs its certificate with its own private key. In this case, each switch must be individually approved by the OpenFlow controller(s), since there is no central authority. This is the only switch PKI model currently supported by NOX (http://noxrepo.org). * Switch certificate authority: A certificate authority (the "switch CA") signs each Open vSwitch's public key. The OpenFlow controllers then check that any connecting switches' certificates are signed by that certificate authority. This is the only switch PKI model supported by the simple OpenFlow controller included with Open vSwitch. Each Open vSwitch must also have a copy of the CA certificate for the certificate authority that signs OpenFlow controllers' keys (the "controller CA" certificate). Typically, the same controller CA certificate is installed on all of the switches within a given administrative unit. There are two common ways for a switch to obtain the controller CA certificate: * Manually copy the certificate to the switch through some secure means, e.g. using a USB flash drive, or over the network with "scp", or even FTP or HTTP followed by manual verification. * Open vSwitch "bootstrap" mode, in which Open vSwitch accepts and saves the controller CA certificate that it obtains from the OpenFlow controller on its first connection. Thereafter the switch will only connect to controllers signed by the same CA certificate. Establishing a Public Key Infrastructure ---------------------------------------- Open vSwitch can make use of your existing public key infrastructure. If you already have a PKI, you may skip forward to the next section. Otherwise, if you do not have a PKI, the ovs-pki script included with Open vSwitch can help. To create an initial PKI structure, invoke it as: :: $ ovs-pki init This will create and populate a new PKI directory. The default location for the PKI directory depends on how the Open vSwitch tree was configured (to see the configured default, look for the ``--dir`` option description in the output of ``ovs-pki --help``). The pki directory contains two important subdirectories. The `controllerca` subdirectory contains controller CA files, including the following: `cacert.pem` Root certificate for the controller certificate authority. Each Open vSwitch must have a copy of this file to allow it to authenticate valid controllers. `private/cakey.pem` Private signing key for the controller certificate authority. This file must be kept secret. There is no need for switches or controllers to have a copy of it. The `switchca` subdirectory contains switch CA files, analogous to those in the `controllerca` subdirectory: `cacert.pem` Root certificate for the switch certificate authority. The OpenFlow controller must have this file to enable it to authenticate valid switches. `private/cakey.pem` Private signing key for the switch certificate authority. This file must be kept secret. There is no need for switches or controllers to have a copy of it. After you create the initial structure, you can create keys and certificates for switches and controllers with ovs-pki. Refer to the ovs-pki(8) manage for complete details. A few examples of its use follow: Controller Key Generation ~~~~~~~~~~~~~~~~~~~~~~~~~ To create a controller private key and certificate in files named ctl-privkey.pem and ctl-cert.pem, run the following on the machine that contains the PKI structure: :: $ ovs-pki req+sign ctl controller ctl-privkey.pem and ctl-cert.pem would need to be copied to the controller for its use at runtime. If, for testing purposes, you were to use ovs-testcontroller, the simple OpenFlow controller included with Open vSwitch, then the --private-key and --certificate options, respectively, would point to these files. It is very important to make sure that no stray copies of ctl-privkey.pem are created, because they could be used to impersonate the controller. Switch Key Generation with Self-Signed Certificates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using self-signed certificates (see `SSL/TLS Concepts for OpenFlow`_), this is one way to create an acceptable certificate for your controller to approve. 1. Run the following command on the Open vSwitch itself:: $ ovs-pki self-sign sc .. note:: This command does not require a copy of any of the PKI files generated by ``ovs-pki init``, and you should not copy them to the switch because some of them have contents that must remain secret for security.) The ``ovs-pki self-sign`` command has the following output: sc-privkey.pem the switch private key file. For security, the contents of this file must remain secret. There is ordinarily no need to copy this file off the Open vSwitch. sc-cert.pem the switch certificate, signed by the switch's own private key. Its contents are not a secret. 2. Optionally, copy `controllerca/cacert.pem` from the machine that has the OpenFlow PKI structure and verify that it is correct. (Otherwise, you will have to use CA certificate bootstrapping when you configure Open vSwitch in the next step.) 3. Configure Open vSwitch to use the keys and certificates (see `Configuring SSL/TLS Support`_, below). Switch Key Generation with a Switch PKI (Easy Method) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using a switch PKI (see `SSL/TLS Concepts for OpenFlow`_, above), this method of switch key generation is a little easier than the alternate method described below, but it is also a little less secure because it requires copying a sensitive private key from file from the machine hosting the PKI to the switch. 1. Run the following on the machine that contains the PKI structure:: $ ovs-pki req+sign sc switch This command has the following output: sc-privkey.pem the switch private key file. For security, the contents of this file must remain secret. sc-cert.pem the switch certificate. Its contents are not a secret. 2. Copy sc-privkey.pem and sc-cert.pem, plus controllerca/cacert.pem, to the Open vSwitch. 3. Delete the copies of sc-privkey.pem and sc-cert.pem on the PKI machine and any other copies that may have been made in transit. It is very important to make sure that there are no stray copies of sc-privkey.pem, because they could be used to impersonate the switch. .. warning:: Don't delete controllerca/cacert.pem! It is not security-sensitive and you will need it to configure additional switches. 4. Configure Open vSwitch to use the keys and certificates (see `Configuring SSL/TLS Support`_, below). Switch Key Generation with a Switch PKI (More Secure) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using a switch PKI (see `SSL/TLS Concepts for OpenFlow`_, above), then, compared to the previous method, the method described here takes a little more work, but it does not involve copying the private key from one machine to another, so it may also be a little more secure. 1. Run the following command on the Open vSwitch itself:: $ ovs-pki req sc .. note:: This command does not require a copy of any of the PKI files generated by "ovs-pki init", and you should not copy them to the switch because some of them have contents that must remain secret for security. The "ovs-pki req" command has the following output: sc-privkey.pem the switch private key file. For security, the contents of this file must remain secret. There is ordinarily no need to copy this file off the Open vSwitch. sc-req.pem the switch "certificate request", which is essentially the switch's public key. Its contents are not a secret. a fingerprint this is output on stdout. 2. Write the fingerprint down on a slip of paper and copy `sc-req.pem` to the machine that contains the PKI structure. 3. On the machine that contains the PKI structure, run:: $ ovs-pki sign sc switch This command will output a fingerprint to stdout and request that you verify it. Check that it is the same as the fingerprint that you wrote down on the slip of paper before you answer "yes". ``ovs-pki sign`` creates a file named `sc-cert.pem`, which is the switch certificate. Its contents are not a secret. 4. Copy the generated `sc-cert.pem`, plus `controllerca/cacert.pem` from the PKI structure, to the Open vSwitch, and verify that they were copied correctly. You may delete `sc-cert.pem` from the machine that hosts the PKI structure now, although it is not important that you do so. .. warning:: Don't delete `controllerca/cacert.pem`! It is not security-sensitive and you will need it to configure additional switches. 5. Configure Open vSwitch to use the keys and certificates (see `Configuring SSL/TLS Support`_, below). Configuring SSL/TLS Support --------------------------- SSL/TLS configuration requires three additional configuration files. The first two of these are unique to each Open vSwitch. If you used the instructions above to build your PKI, then these files will be named `sc-privkey.pem` and `sc-cert.pem`, respectively: - A private key file, which contains the private half of an RSA or DSA key. This file can be generated on the Open vSwitch itself, for the greatest security, or it can be generated elsewhere and copied to the Open vSwitch. The contents of the private key file are secret and must not be exposed. - A certificate file, which certifies that the private key is that of a trustworthy Open vSwitch. This file has to be generated on a machine that has the private key for the switch certification authority, which should not be an Open vSwitch; ideally, it should be a machine that is not networked at all. The certificate file itself is not a secret. The third configuration file is typically the same across all the switches in a given administrative unit. If you used the instructions above to build your PKI, then this file will be named `cacert.pem`: - The root certificate for the controller certificate authority. The Open vSwitch verifies it that is authorized to connect to an OpenFlow controller by verifying a signature against this CA certificate. Once you have these files, configure ovs-vswitchd to use them using the ``ovs-vsctl set-ssl`` command, e.g.:: $ ovs-vsctl set-ssl /etc/openvswitch/sc-privkey.pem \ /etc/openvswitch/sc-cert.pem /etc/openvswitch/cacert.pem Substitute the correct file names, of course, if they differ from the ones used above. You should use absolute file names (ones that begin with ``/``), because ovs-vswitchd's current directory is unrelated to the one from which you run ovs-vsctl. If you are using self-signed certificates (see `SSL/TLS Concepts for OpenFlow`_) and you did not copy controllerca/cacert.pem from the PKI machine to the Open vSwitch, then add the ``--bootstrap`` option, e.g.:: $ ovs-vsctl -- --bootstrap set-ssl /etc/openvswitch/sc-privkey.pem \ /etc/openvswitch/sc-cert.pem /etc/openvswitch/cacert.pem After you have added all of these configuration keys, you may specify ``ssl:`` connection methods elsewhere in the configuration database. ``tcp:`` connection methods are still allowed even after SSL/TLS has been configured, so for security you should use only ``ssl:`` connections. Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/tc-offload.rst000066400000000000000000000107141514270232600255040ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================================== Flow Hardware offload with Linux TC flower ========================================== This document describes how to offload flows with TC flower. Flow Hardware Offload --------------------- The flow hardware offload is disabled by default and can be enabled by:: $ ovs-vsctl set Open_vSwitch . other_config:hw-offload=true TC flower has one additional configuration option caled ``tc-policy``. For more details see ``man ovs-vswitchd.conf.db``. TC Meter Offload ---------------- Offloading meters to TC does not require any additional configuration and is enabled automatically when possible. Offloading with meters does require the tc-police action to be available in the Linux kernel. For more details on the tc-police action, see ``man tc-police``. Configuration ~~~~~~~~~~~~~ There is no parameter change in ovs-ofctl command, to configure a meter and use it for a flow in the offload way. Usually the commands are like:: $ ovs-ofctl -O OpenFlow13 add-meter br0 "meter=1 pktps bands=type=drop rate=1" $ ovs-ofctl -O OpenFlow13 add-flow br0 "priority=10,in_port=ovs-p0,udp actions=meter:1,normal" For more details, see ``man ovs-ofctl``. .. note:: Each meter is mapped to one TC police action. To avoid conflicts, the police action indexes 0x10000000-0x1fffffff are reserved for this mapping. You can check the police actions using the command ``tc action ls action police`` on Linux systems. Known TC flow offload limitations --------------------------------- General ~~~~~~~ These sections describe limitations to the general TC flow offload implementation. Flow bytes count ++++++++++++++++ Flows that are offloaded with TC do not include the L2 bytes in the packet byte count. Take the datapath flow dump below as an example. The first one is from the none-offloaded case the second one is from a TC offloaded flow:: in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:10, bytes:470, used:0.001s, actions:outputmeter(0),3 in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:10, bytes:330, used:0.001s, actions:outputmeter(0),3 As you can see above the none-offload case reports 140 bytes more, which is 14 bytes per packet. This represents the L2 header, in this case, 2 * *Ethernet address* + *Ethertype*. TC Meter Offload ~~~~~~~~~~~~~~~~ These sections describe limitations related to the TC meter offload implementation. Missing byte count drop statistics ++++++++++++++++++++++++++++++++++ The kernel's TC infrastructure is only counting the number of dropped packet, not their byte size. This results in the meter statistics always showing 0 for byte_count. Here is an example:: $ ovs-ofctl -O OpenFlow13 meter-stats br0 OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:11 byte_in_count:377 duration:3.199s bands: 0: packet_count:9 byte_count:0 First flow packet not processed by meter ++++++++++++++++++++++++++++++++++++++++ Packets that are received by ovs-vswitchd through an upcall before the actual meter flow is installed, are not passing TC police action and therefore are not considered for policing. Conntrack Application Layer Gateways (ALG) ++++++++++++++++++++++++++++++++++++++++++ TC does not support conntrack helpers, i.e., ALGs. TC will not offload flows if the ALG keyword is present within the ct() action. However, this will not allow ALGs to work within the datapath, as the return traffic without the ALG keyword might run through a TC rule, which internally will not call the conntrack helper required. So if ALG support is required, tc offload must be disabled. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/tunneling.png000066400000000000000000002150051514270232600254450ustar00rootroot00000000000000PNG  IHDR  0iCCPICCProfilexwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ iTXtXML:com.adobe.xmp 5 2 1 :w pHYsgRQIDATx[TU_.d2 ,Chda].CCIA)$KЅ,nq!* =EEfVZgqiì3{{Gk,G[fY[t8)9ik2rЀtb%3u7[h>B],Yoo_1V7gd#tɖ-suy~ϯ,R }.Vw.r_?tb̭k,G\+;r}/~Q`Xo4/"3,wZwX/Z[ndmcyv|}{S٣' :0H-Ř{YY/S{X]+w[-YNQ|pGv FRX˶lV 0|,ˍAm~SEԾڜXc+o DAs;>lWU}E΋ٶ2r'{nxJa{չ ŝ=o(?b@M)_e y)'+zכ-ck]UW3Z=c(`cnM __p+uZվxŅ5'=fJ@c90e$'mCRU-|oy=c(`cUs UUu|zײ%UܔdЯ>'ݳpCq~^ ߑOQ{9ߡ+߿UVT ^ |U}-u~(]^.}{Ll_Zk;;_w{"x-AGFpb1ndh44 j2L~fAggؽ΅{|Lٝy۞>[\〞 ugQkl䜼|1[ _]}AJpO(Q/k-Z0}h?zn;m oS ؟\3KS^KmMd: 5"!)K jMCGjzA+:t}N   -z A A   -:9,:AZ0trX0t``i!a!҂CÂCՖ.?wߐo$w2%`tُU 1gC_1kHՐ\JUzij f?TP4~ڬxZRWҳEv ^f3ijХ3>wd?>Y=PU3tYS:n"0xtWLqJNɣ>Hva:\w+.M͗ 2^W&^GgN"ύ{[X ޜx= n$7f:Iv."cY9'zz҈;|9_0/FgYּ˗> n_ެu ut1lݓf?x1tm,Wo F_:m;>s-{y#/.7 }QUeЗ?}z:2baKb#5r ,k:/?^ϯrτ7!okuN0y?ow>[_=ݱ2 =SCyʇ D:$R#/Չ=M`ݤ䗒_?hmǮ޸o2t[^t?XI!C?b={ KR IZQK7?Jvܥ}5Gv[ }78?.?ǂyi?o|\?z4=몍C CqsC18ϛ2F#z|+ʑpulwŠos cѼ5-St*N+b浺6q/?7As3Q eIOP0I5V"C5nUCOt =&~ ͚.[)zCr>֦WǼCX1tg&g+zma=[wciu?I[4BzM7F t㗴2#g1%\:WS`ڼmCĩ&.c.ב2Е+7q[y-JmLG*[ceٳ9kZ/6Ri#rO/9@_!rO/9@wMqi%^r0rMq\S\zKҐr|00~=-@,Zظn7-IzQ|/3[Pa(6S4oNI.z*O$kՔF7A-ϸr~c Sy*'v`9g̡\C=-@mA" 5ۆE^]-Ɯ2].Ԯ='3UWߛQo75 IR>)Lam̯c_@W2u^Mђs YΧLEͳݏhlM`_}Uos[=b*'u-,p޸b2#%ˡ0\PR su݂;@O Cۦr%d16&yP\YF%$ˋWmBw>O|*Tw :m6U\+S/Ss@/_BȥKF.pȥKУ^gUI}ot쇦P#ty3 @~,zc<zm<\n Nx8BkMvDl."B݌]]f=[<=-"􅑋=߶xg6 }~EڏM$ FF &&t>#1q i*sScFُՖvF=gu~F9,c LGCLt3BVubY,-Hu܂@He܂8M9a^Pb,2&<-Pz__c\KEvΆw.)z(VWU<{eþkLr6)) n ^#o첂02IPR]k-B7zbzeo(Z<rt;su/ה _7ȔXE,KcW'xOO=P7FԌE|o}ݜ|w)6nkB_Ów024E|5m{Xd؝X&؁U2y8c>8u_ֳ\B^?4){BEOggb"l;JCcɱ 3;e_>y38ΔUWVl?uQ+L1t>&Rσ#N]q=qrZ[yЏD_CŽE9O%((3wc=l0m -b@7$WIn24\9>kՋ;ģ?&?.St'zw8K#v%3"kGu9mw#s;R0 o6w_6^o8,~ah:"a#؏VvA=}5daaw%=vEb.>hY;Enٞh?96MUm >!^\N薔{PC_n[I%pWSsslJ}8)۪| #x!=Xg iN8 P蟭.Cl8\p54Rl]Z)2Gv9tu3o_0K~Ae렸6tS2ZF}b@{}~DNk1E3o=sH_8"t߹As0g*Pʐ7lHp pu^k/б^Z^;NNV&CeKWEȡ"` `#AAZ.(!ZO:Gc^t; F#F7e@Q2pHN #WpSIz\bcerf|@70MK[Vހ0 З} {ϵQJߨXZ=7E|;[ ௦tb,?TѼd2udk+#c5 Yc_!$dU7fv`>;|ui+Zn;.=L R2Z|BtmԊ_. $ >0H !*DZ8qǯ_'^ZR%]=[y j ] F ۘTT@yM\,&7 c{"J55%P ۺ:Tnj]n08$>M,JEkxcͩP׿(-:4#.: }`t#_)8F>5tzK'PND {#e]NeԌrRlVCbnSZJ:qgH@KY7y$FZ`I߁::A j5,0϶񸕟Y="E_[{JɳbO|2˭i*yhl;{j^Et f=bG.f)wY˼NWUqg|QeS_ZD&)]tiCyKdY)ٻEk6+á {f /)3t-EIGYV ]8l}CJ1[3,kks@mPH4ePWrm止t֚,@pIbL# E $ Ցn=WY7Tb7Qg?00 u!el eD} П a;Z;80X;a:b=O).dz9{]>yL,o(@PO7&g-dysKԈ v! 3)w{0Ǩ%*i:xffEuCF3bv>N`]* #MAߥ#)6 cє̠!Fٚ^~ukUEw:{9YC&@G&7\`&VYf<@΍p䉃_8Ny .: wa шX1|G @ovq}iS8hta6^jiX]};((e)ٶ%Jl|nיq*Z񐱿SĈpW]䌢v&ř6 h)Ȥ@ nǠ? y -̧aM;fw\fa;`q-ٕ?m`@C6NP9@OQ=l[ = >![ۆ\> / NXK}Htϛ!)~c4ger ;ÙAD-kXIxm ?(Mq!iQ>ȍFx7{>sm@g(>ȠmK+z R>epS|;>)~ \#ylM3yGG5G鏳52-=zdkz]x{r?h#gK w~OWл) z5][ k]O\SkEEXJkB"$.w>$Q4+ ba؈ + + ""2ӰJ e;9ܙfλ|'3ߛ7ssY_2Gܶ w$q+ <]?6A -p?R~*K"tΐ:Kڥإ!sn:ڟ @2ћwq |0\ܠq)nBJRCR:e28G8XLཧF`J̭+ q'SNd =$C;CؙI;~>&&z(30܇~]:=[Ims16 #BiJC  )93pjV yat `*E!aݹp-7S\ '`M!ЦP|nqpܢOt+/<ӕMqD]]}2BOЫ!"NKX¯h::?zBmwLi< cLjßC!ؒ cԿQ,B g'Ïu't{!R wg3f ?k4 ]}ʒ cQ e?l@m} .㱣R1x[I_[exbsۃ#=RUf谭::>qds/Sb`}e<-rzIyߧ}Ȋ#YL&-לЃaGc*9F a /;c!z!i:r0BE')gPQBA%߯H:w2{e71 ]}ʒ2 7/mz(~Vۏ-{@/!q} 7ÖB-: Ё'!\K 75t8hw}Ysw(E58< G0?Eީ݌jxgc ɕb*:c]>`)'ïo[י;NKpxq(x]$B>9|{ mB{KmoԞWr֛йF/6LۢX|{]kBV*&k{wjB7]CϾTKBr&L4zBoQCB.nZB_o\Rg_zcM苵!77c79BSc7&`cc٥hg-5"}B7xv }Ӷ {n=ۻTo'/rwҖsjClWo7?,>] p5wæjCv\S{ܯ\:~ݲ[oͯ1}za]8MSVkW2ϿzBD }ZodTA+[v?ƹ?ZwS;apOY[Y?/kHY jqCF 'k=WٌqQtUϏ\V NT7)ZWv8vQe%_Qyw޶q,)Rzk'Wk\X1aebH\7_S*3gTqީ, z5{<g߬,gEP_ߪt`e3}_qޔfJcߴ^,>okޮɀ6j !eԷvi*u,z.a7۪%ٲ"wْ ƽ`x`B&'ciPBxt> 6 N(13ʒͮvwu;gΜ9gܙn -Ve9|*VoCunvG5vpHa$_U}K,wF1`vtXzOX#$?7z{XA*X[#V0cl?.]% M\B>lpr;gw|&]L:L\-+p؀I? t3Qr#A14k39`%ꑁ3LSot=s>KED $\{,9umt VOW[.ڦ~s26'HFR8se@&Vp::hԋc#bmHs9\,[XT9eep@N{j X2׉U]|J!q9qfl1fsrr?tYT(0/qXOOٹmCgY%:lИĚ}up@^b@]5X:4AW,_0;8oX@w<U>"9sŵdـpSTP3@mFLS}U\NNp@^b]X|};*(Pd>Aq;VviO{t6IUFZ.Dʍh@;;K 쉇=xI}8.+P@ֻ^z7T~ ;?zwf/J[zFlbp(zK>s+g3w:JD{u@+S{W(@:kJ8wiǩkQt}f@DYuԕg^M)"؛|79/m;}D\ h,mtR;Z-2!v4Ou,O^F:o[K%;*cgaKg]9#^NEGww©zkRw%dt mhbb)--L㵞={ѫW/ݻw3zъ'..Ρ#|rssa}90rO]GP\߈#wSBBJFF{H^^HvvF62 1޽;_;јh0n%x)))9qӘƨ;MNoy DH4Rذ9.>H@ lذg̜93U8cƌ'Oq 'SL):ujYZ9666BiӦ TL>=#--iz5@` T̞2O!:={l<# loĉIV4`2~O!p~رcK gO<-|HNH< 82d jV,B xpp#@=q%y_  ;WP;q3Oq$߇#p!Q&׺uEy7lߏQg߭7o'_شiv9qooM_l&rG |=xA;I4Xu!dqH[l9.6Ge]>kp-$͆/gEF^X( u\|KEjO{SM,c|xW7h2&()z/瑶|>">2y|"CQ q̓my[2Ӛ<<04g2E@ʳ ߛ2/2{~>'x'ڒ.:Z|o= }n9/ApϟڊCZ+|_A7ų-z|t!5):qeCr@}v?DP`IY|:^P6o">*- 0:2.q.t0x ϟ3Ƿ jŶ!Cϣ]j WRZև(/^^Tx?I'݄iG~kߢSϡ Gߣp 6l?v;)~G^e/d5H8:ģ~p ǃ"\ߋt{Q ~%o0j\;px~ z:ѣGYBІ4nnMT\xjP 6 xPt3:|WW"sxj|/@̸<zZn@cp Yq&p::f9jiD_nљ;2n*l&P`.n9_uw:ڊ|tn/Be} #} xG])-= q=ݍ8w@ip/`Xq7/GnF`87*ú2ӸY/իכCjp5C6#У,30K` b"yxY'si΁KviT8@P͖ g1i<q: ߁ƏKL\zx6Р7l6U%kNsIHHH/<{2a*^l 1~YK.b AD<\_Brr򢊊 ʶyEd xN‘iµJf&"piωzc=\c%h3 H c C#Sf .QxfDDzvQ$֤jCa9J|MQ7FKNYYl||MZ2=-7+PiJ*SʮsDX(_w%_ )_*So[>U|JgndQ}XCV` (_!_ujuα]'|G>1GWiȦWdOoO+kJ'IJ2W'ﻐ'0;tNéժT`JSVR5? 3*Y0*OݿȋƔt  A˗CP:I[+-c&\*+6ӨOc#+x Д .U e4.~OE*i||J'lXݔ~5F%{!uw+QeP U$غw-2dtA J^} V'nOLJL4U&o%gW/?&14.`F*InM](> lnڍoz#?/K:Kx-I3{H49ijQ305͡P~NKˤN#^ɠds:4mboǛ|~q^yK5cT~&7ےzc9.7R6~|eҺzh//;7=8G$4tYyvcF'{oFіƊky3@mXtWT8"I1xXde7C|нQ(rh-%;ghX:ZNv~^`qoayـCu:?@Z?ǣxڂ _{<(?N>+Oox2q<&_[<ڞeKgz{grcsWaCSK\zb~mѱz{]w4=N[b[  o,5Hw9e:Y?{mt2u@uto,!^ ݎ 79zW ]9#юħq3aW '#) |΀Nw`E#f@glѯBD*pXzޡ5L%6Zô'IGI/ 5 &v+Fb. VNH;q^_=!V'+(}HfsYWph`G*oWtVk<z=8I%gs ;ߡF5n =F~c#G~lV.Qu@5I(VYxl p\6'Pd'=QNKnB=8 +VY߮V_HzWם\X8:XGD9kfiD,7qNZ7@#-FL?&֓{]ѮFgSvnd]>inN}C:SPF򫗬=b ?t^pܾRbM` F Qt`Ĺ ىt։m)(Ѓ0jqXͯɅIwكRD:U>$*\V~Nj><[OlSjZ﷈51)T;Q72[bMӯ;2Ax~XUzPW@씑-3ps*\m::#ǜ h7EUD1.:mHFw'P8 #:>\+'q4o3pqu!u\/- gu>W@'u+  k'dٸwqä́š?C^o$+ǵ< OzP3!#Ĵ"ÔYYP=6h,?"TӊԿZg^C2[}?RUu)~l=N=*G&gK򈂚qE|j<(?@fw;,6"_~|~U |=ɬs-~lOvY9{ov=xO]i+msک4a}!G |ѰCs{K< ^z7 icS8@mXsOvЌh_?8)>'nod _;qsFͻh@^_+.|0#){gЇ.8 ]ٽj\eA6G^-UU6ɅƼn S|mrWfq[>gU䕸guM?GVSi8@8{ː<֥ڥn:T_{^1CH w#߄HgI[\s~-i@Ӏ]TW =k1E뺱AbXcﱷZb[{Qc챬kܽ{;>033˝yYӛ~eM46yiHԋu;jҸyMf񸌿u]y P#Ȗ7>"PN,eQvDnUD?ytEǬП2r-k՘N&-{PE$|-Ҙ&-=Qr agmęЫ5+m6t>>cn+oO4L1qa-h;׀w=[SjzEꪅye ?c'2%Л\zֽR/qK+0j5Er #iB;&qzuU81[ U\L{ЉEB#\[Y"t`'tg-BW UikE"tgggNNNپ_3]/_>E4-oeyU?Ui$tNe^53۷]my...omaAp~"EL̻wN/^L7nH'LYݧYJ!ONNs̡AAAڧE4CzBmzA-[Fׯ_OǎnLduf!{>zGғ&M˧MFϞ=׹g-3|{:7_u]Fj߾^z|'|B {?>|/o߾4**{.]rgϞMm ۤח)S" GϹ~BϐI*QB?ϟ?OGerʕt˖-^||HӧO>2辒s4 ׮]:ux<\7lAB|ͣ;w'˗pfЕ5C:={pFSn{UTǏDh߁ 'iz|;w73R!Xhs4 px@0~"iӦMycǎhl߾#zj":,B1͐,bÆ \?A:K;oŏ?HO>mÇyo ޽{ܞ@ݺuoF^K BJ;vA򂽅`|ЃrC'7}gtttæX5[rfroܸ1'qe:t(`62d3 j8%8%%l G }̘1E%P̆kpVa6BS".\ā#&&!%M1`QDzJ%J_Ta*Ͱ݄>Ey "Q~ {:u}8G%K}(=?3:95^bޡ~,"t DC8N?k,1{^鋽,B7c OQD ۿ?O#:Rj &@.bxB堈/@Q q/Ű`[@+@.uƤDxb(PGx J.XXo^#FLxaRxs7!/يgJ1C/\e4s e `?)9OUi%t B3ӡ{ Axf, "ClKzxޣ$dB7nb܊wL 6rH>}dx#ƽ[lɗ U{`Ad[Sr3~7/Rn{ixuJ=Y,"yջ<1q2J^i*͐~`6پ`2{)- p^d:5t 7"t`GДLx'"tU! ] dzLS?%]oBA1* a$zLMYk]oO BOZph^N@Bo^!1jSJ?zDcViОƍZy1GzA ͆.`_z1kE=٧©{`j m>bt"tAާQ WHR-=xqpB4{|*Z_AP-]5\7{dLC ,uuk:јEUj1z0&܈,R"狱ǍZq!^&Y{7,EL R/aؤ@\regXr6ߌ;'>ywKLX1H^~E?cNN$ՓL-BW *QBm8HV1=h:dލh4`IK/&=O6.7:ޟ<ʖHop e>ǎVBF~G͓ik2ڃ%=hDNN8W:~ eGaVkxT Ž{RO֟T?[גSsa£RPvRNy\5q 'oFxU5HdURKgӒ~E=-BW |zxWӛC?ǘ-ȐȐ= +Y6:dǑ^5~u1'0=ehxfcӿ.[ZS{X&0i7JEXN!tb. et A#6^a`Hahڮ͇]df<31Texv~San4ٮfA%Ĺvv[HKfz6sv16`PC=^Fq Wjك L? cT%,6v.dʒ4yjD I 4&T`psvq-@ЉʥZ'`3L`/9=HEc v^7#i.oa GIS k(J|2Y1؎=@`:a73SxB? Sp\S$s{L$=aAzFhq؎pP 2LrQV b6}*(J|pm|,B~@]}D[3TB?sN-o>$Mg!"!dەHNnnP&3"֮zW0!RΝH=' D&"0|z$)]"1!,B7^$k-ARõE FiCJCDv$Ę֎o7FAQAwQU<ԨQDW"F41=kϬc5Y̚3ךs9u=VQE^Uŭ{O߷ϳFJ.ĽXW$pT4pUL$DC0{ kp 1"4RxA8:z{|=BxpS8Ďu4!#H>sfF[.^i`7I/~yWees繄G롰A'a3Νw^'Q-Z"io #u''i=BQ'1i ;neC#xC?W"s;ƒ#)t{ ІBuB<# X\2!;纸ԛd a`>1md:Hs=8sh؃S<$eoRA.vAG6PtBLaP8Ch0'΃1p=M?x (:H=S qqᎮ  ߞ@SIۢz`J,Ix  o',?d8G[P7  :cCĹ $):K)HpK ă [܉N'j VoW< ЇAK ܮCG"91669ЉcH hh#xl§(ByhMЃW0Xŝ18Մx1\o<  _x6"N~&#H Z +A0N#8q#DtBnNE6=꽄_C v`O\:n)Jϼ uƋ+QGCr KOqlc,: ȷ~Ens{kǹDN~(v6t4!=!| [Rpt`.\tB H:@8^Hv,"+ypx- OE:Fڣ ?E`\$mI&BƑN!R@_oo=|W1n'u4hX{RqJM \OH!u܊#^qq C:)ΖAJ|X.o =%6!o5T'K8{ 0o?gxH?c5. }Bee+vF ?S<^0&(KaaޚIFtms|gq*!Ŕ͋*9te6e|E[Ys11yg K'ߗ_+ay+*rw+~?2l\􆃖mnl/yݤϲcOF/1xcr *iYXINQy? l#9iޖCk N.}bgLfmj*vvWge~UƹSGO^uB{r jՎӧq^d NB$ď&W=8Ǣ{Pbʖʛ:E5_R qM2:y*Z:TC;|P2׷C9 7w߀jafVVh(, Pz_ek춍Zx]06=Cϛ֥UGN\[Sʦ&!OŴ *$< Gցd9$D3}j*-gYaMr$Vg*TtBYS'3W`XvI3pJRigg8p+k>ЮudVJ[zrX c͆,~5Ԥ@]K}*ЖS4צg|]nwuJ寫N HMZp|x'\7f͒uJ#pү>kVƎ=G \X0Oխ7h@b!Ї]IOd"D-a:{Ū _I%_Ҳ8;bKhB< z`_w&ɕaZ|>.W\5X4}5lY՚ mW2ҹ}ԉwM7.XB^z=r+R/`e=CbEڐupF&KmQee 1u]:{(oZY0< -^dRZVazFq꽕3a~5{ےw$Ǵf+KC-9S#~m%޶,8jgۅٔ$ݞ/**.^Jpu};Hk&\2z3Ė4M G;uakP<ۤ'gM]tA:%&тm0k܆4XIal1#WS_Mv0r^Y]\~dgU[vaI't$l[ɭ3e}]v\A9}&_*,~FMeWJl#uJvE; b حKx#NϊU~o8;yL`WYr622gJpVϢ>ո+T~".dx [nö=A:y;"zrj7Y"4XpF̉]MzS6cPV,}9װ{]$YŞZG,HZwݠ3%p+ J)4P9hGjն&__NTѹK }x ~\G4 iة[?(ɉю)U C$kWԱ88Z 9^}g9 :rd=\7x`yA<I1pфL} |Ξʜ:`0ZNtSa}VMl| AXZӗnƭx %w-6]y.kH[=imrGi6ݑN4y>jRlK/yV.ӎe!\Ihꤟu{nt3ޣ<{@C>S3 3-[p# {*l|T=$y7Bl\o'GeX!94Aa$. iɢipl t0unTSGCGER{7ɇn^0̫ N;)<|I8:s# (=`T~e%Jv*Ї3lʽ'9ܮlD+a&8Mi`H_Oă5ԧZ/aV;`{)Sx FZr#zHq݌a&LwSgi@^ooB`g^4ksjAP:;"qq‘#R2 ]+vD{}ZS. )N`s$g(ipV k 4PiPAWW_&Nm=@szcҁ)wD{8xOAtޒGm4ڶ90zI-H-79y_2AǕxخ}"X./MR<1go+yS5w֋l kC.A "|=گf)"[7.Mp4Ɂz=JS!YOf⦻Y}1p S+2. #ݯ5>gay!?)G<@һX[_ 2RAŌ>I/<eCv ˯+5`Z2LwDb8; hF"_tBJ0 ;V'Y2N9iQla|KG»v5 Zp/j*/UPY-4H۷fJtFikE ]uĎōGF8t: Pr,A8t |:[<O蹄;N[>zf8tۿZ.qh8+Ն\)%o,1=}C9u-{#ɮyCWη,W\h׿0ύi!vvqvWkYԷS'е|\֛m.og?U];|^e$t-Ύ:W; \; IiT=t<6A#)t6;D>t}EZāU=t.D BWE2ôbr^#s^%;RN{T -3 *}O>wLϠ gt$׏.]p߈]Yzp&tB5z綇.)){C7Gz>ܿ_n]ƒ^~s(m8 G$Z>mkfzM] pǙ]S&[88ImƉ7wSĩTvbalčq }ߺzB-@@!qCO !87u;$90SzOozzfzꞖ=7Bw#cKaJ zPzFME{_$omCǏ8W8AOصms8F'enfVUx`pPn`xLɏ [9(3QmnKx`zt?,^pӖ3JH @zy*K:^O0,}U:9.g3Y >~̲alC }^w ̒@* ` EF+=$͘H9}Lyf g'C;}Ţcrx *iIx [- P;B-F%0EX\קlw;p 28<{r]ɐp~Wd+u&=ȡQ.->t)$ַBw1nNE6X +0GD<=V/`0"Ne3<KaM$lߔVc*IHn4=b<%%9\"n]Z/W/$^ Wn `IݹP (( 'HCI V>/\R҂^=Co-t7hצPsFI-ڞ&[3 5w.3*S&JñK[xtל= . Ám[chs7W2[6,U*7-j떝٨%vMfM'32H}v$RF0bgBaFfر%/\8w-e#oNj'tx=|M.ٮ'Cvc%.w_++*HƼ0kuP='sf8[v-i>f",ղ_=Ap 7߽pcz6J ?w*ܿ_kö *{[9N-#<8RJX9 {JLɠ~˺xl)%˼^\FNJn@EB΃GWY+7 I^u<@OZ##U!yqo; `{™{ +VLE_I߭9_]{}d$`mSbx!31_j~Ԯfؑɹع䝓W~)1AQ;OYikx~\TN*(9ߺs }^ڶpb142R/("D<ai?Me-f$.igK1KJr 70>۲ !_J¹cK*ud@z42h|6z9xb\R.\ ̵=p~ \9un@jF LsH`ۙk5']y 7 71wgZC&6<{]'VH#[VW~Ǧ ZqdC5 E[:r[ނ! 1lN!BG;C8{"m #P_WExuGK'ؾa31&H»@J^T䤴/Vj^{5r#!KAM a#AXFbN+9-FeQ8bRL"&H觮"W" iޮQjXF砡6>iqr(WeV<"}W3B@B"xlh1E{Cq) .bBwc0>:3X5 [:mA#]IѾ"Ι7=kHQ_:NwƐo*\D=z* <&ط;rEViuB[s =#JC~ -xݹҞw.wk-'!3νUb%r.;Y@Z1+%jq5&a$7`~ԊV:xݛ*r7l&{Ѧ[Bg[:#سcɺH " ѪG<@ښII}+o8ODy̯A% Nio\a$':kBvIQu h?)g^$t^B5+м1͂NL,FMP^G-ݚNjopy_u;!lXyNa;3:fR'CMeJӦXz׉ H-4PPH]Pnlx>T];,MQK&|]Rp/JlQֳ$ckuӾhdz6sS :sa,RwntWD p8 &speX?-Οܬw89;{@׮1JpC L3+fƔ1c cWkxČRZrq[ra̟ރ^x2g;MBaښg ]=UM-&?3.lwjƵ ]=Y߻Jr(f ҾDM[]{1F1$y E~| Zr8E%lFOm$V^"Z**5eBe'jX^6B`O1Kɪ H7V,xCE/Ga+t+7нh} MWmt0##$t(QݙPDڼق;d(/:' CD]%GL76=~Pƒ1trRORBw 3{TVgt+e C6QMBK^f(*O+;rp+1d% &:)#`uD.&l^8K4HW#@YЗ^5tU%r<y髻)@`vjZ/bL;2eBׅzi&Wie)/x~y*Cai^̜9+]RlE˖*.:?Np#;Hex<ϙkD99vx[sf?TW]})nz 13?\wپq" 6&R 3&u5xSiku搻]'.@\@ H蚥ɲgౙ%~p}(rU-G++\i?[ ]OtyHڨ1=*ʋHД )gsɂy7nUhsþ]P#%ܪLdO{-tQ+W. &wADܲ,rJعp\LL^/8j٧Zk@q4iMp=G>j黎g:ZNJ4#+r,M 6нhoUaMn8楣OcXrrݹyMuexBe_dd~ 3uVKUZrGx9t~#+K3c4T ǖK%#2_ n Pv,B !?N gRQ֙j{O^DPo=ܼq˴ʬU@owcKK4Gcca2 1vMPnmgнy/Rذ~0s0ebH,4s1IsR<K;`l̈́^Yъ4T\(c9bBBá'/%ʼnPHb2yBg=;ݣ.d罛Iq$t/mk禌C6P AsL~ >ϟ9H;9*+CbNxp $X. Œn0uj}R\0a-|M8vq+,8SYrKjIADU#8͵h==-p[y0)3=;, 맽fL3l/˚S3z E6_4p1M[S_03%hu ] $8/f~1I0ڮɿE[X*HY2Z@Om(VȜ״ۓybÄOa'(G] Fͤ8ݵva^bhetoK;֏VyvgƖ HF<v >~^N qʣC-DZF;<2g<߃DxP RBOpA*ġ#;ܨ/!m]!va?MS , ګq+JB&O_=&]/+ pk\ėyyӾe.+`Rs8'R 9ޢ9>U| ,>I~3 cB!6~lZcQ2M茧k:Z~Ax0>SEږx dV⃇Uo"_ƍGFSb!d.br)>oS+zMm|Ռ])u,;wָySa|)lVO>+ ƍczP|W0ˉk¹#F]ixMc|a,qZvk_Op LgeMBwצ7.wo `U8 [ac.w4z1$l/Lc2#l=!i@|a]7(fƁ@޺ Z||O/`Zs}IDV bv/O+^ob7`:7]۞!x yyzjyp'L PtOa6(${Ȉ0ezJCDm0%8Z;usb[dr&ZѢlv-E>\3<y5ޢ {n6/H+ǐLkOmy8'x0Yt<`FWX֗ڨA\ZԩŃ4."7LB3||7X8SÃdSuK/_*Bޚ6;{`C?U!%ձr=f2<3$I't"GtÇ s&wc>H'OL5L@K^g!Wj$zۋ ^-l3y];G?Ҽ'Oe$UG&Lqpf{AY,Vc OD}|߫jl$N4 |f1<_2ȨDr]\IkLvbG5433c uy= 6ێm.]d djn/5aLh&У} >œ/`a|ON ~dc>m/:X7:0E6 ΁88{Pkcc$ ]x ~mT=mϊ;|c2 CO/`;>{ă/ɵ=qԧ0)ڣF2;>S:S{ 'RK?Zhuۖ3<`B#)3п L`^o D.9>GDn]ic$t/|v݀7l#y ;F|44Vù m^;ׅVϴtԀqj 0 j0a&hA1L|ݑ ~FhE;uOX]([6˗2ox kJUXswC<Go~emϊ}d(K w C#H죛B "na)AGM`VCljǃ= ubo!N-l# |]{/6._1 ݫ6xe-j" *.Y8\ wTBWmIB,$Z횦HZ0&͠0ᛚ0^=aDs|&}NB-LÂZLVpVA%Y-\Zx.]a5Z}ݐ,.ruXWU>2b fx~$tm V[ωS y<߭Ʊ#+2L"ၝy<@/Z$/Yшsx͈7 xy1GL!+:\q6^%$e0ScE)1Z~h/ [Ю~38K}MH\>?vik2ܖ>*f 2っx:M."OvtoN_3Ram pǘMI5~JlS`v\X"<$6ͷ~G6yo}p3 ֮BUBMKTQ;33 .Z-eĢ,+L1jFA/DѢ!Z&Qh1I}@vΙg4ל9L=ۼxGO._#~[63 FӖ{oVsWꗈSxS]c{>\AUk #cϗ=g)A.q0tttttttttttttttttttttttt ]?šp9B:grzT׏wUr?8}5j ^t}xnuy֘yhfceJv_ܫkhrBᕘǚZ՝W;^cFW+ ,\SbMט箏ZA_'ot&-Ϗ,}1ys6q6<=>)iMv Mq[H^y>rz Ln7kmվ'}w&ٳ+6vDj)cId\5hu=RN=81sVto\.qXăoF.Cحw\A//^sX߭䇟a60 7z9t&9L#$yhuk]x4 tiJHMEC $޻*%ޫ"UJ? T:Ix9o;7{{{7;{9sf?O%tGڔHRǀ.)3ⷸZ@vgj1ﵜvFB%m?F'l[M$#QzpU'y.ڽfatM5j*m*elޙga^٤-Eh aitBwq 0qMID , _C:OIQ8.Uj1wJUR\"xxul93~dL /3׿7U*6J$W^DN@p5e,gaO?d m?&&jF$tC4d|~&-D'tW65zݧ\BBDz9HH`&ћe~НlBT'7jxŪDf~K'T78Gx8NԯBP'tNN6StBwm MT'N: sYZ<ۡmuBϖ-|C m-]UÒ~:j:K2+ oGN,./Y93f4Yϕ+q9C v%B !C 8t18FGNnk't\τΝ.]n92e2,XЦ.[Bz%tlr3f ?~wdɏ^֠z:&tJ*lٲ&~wvZJUX1c+W={Ѐ^$%Bij}v?Z<>K,UVѫW.]8|?4O%Rlћ7oҩSZ%0zqnիMH+sxxɶГ'OZ<^dH/N zE? ]ۄn)ڃ~~k֬zM۷aLݻG+^H/No۶m:Gd77nLkN_Çw`uBw[ {T˗ӍK+2e9~ҥK[ntڴi, ^˄n,~'}'NǎOx% ]Ä.7qѕ+W޽{!5|z)7ie8isÇi\\qM+UODr"QA~&L|^ 7̙CgΜi24ѷo_.ך5kr鄮 B$ 5kO7mڔo˟??OB_}l7nЄڵkWb ڹsg5  \Szv]'t ] viGTڧ~:u&&&ҿ/t|;uDȱ7x`+Zvm$<9<֭[v]c;R dx--[s(FwJu###'OtBWGxB=͛~~716ydےxϱM6<؄_p=|Z%YpZj[B_EFѣ<0~$ۇI:|0ߎ1| gx0cMӄ.a/z0hڼy90n!bcp XF=t۶mˍ{РA#=z+@6``?lrΦMàޫW/:37Ld@tBWMxB@xA:qGΜ9:͛g܏?-j#\=kG` ] |'E`&Mݻwi~:2tw ݻ=| ΝˉQF: B?{,7Fߠ|0n"z؏H~֭& DS=GJ%^q?z葊-x#%%d1Co|?j {;*˗/σ$VU4AʕO>vD1|~1@9qD6?l4a]~̂A LZ$tw+EXG@~R rG鄞.]͑Bj2 tyQרPwGbgϞahBsqFn0@CE{9BWd^t&rŋT! A't5M:;Ę%7b9:[r3O$eBG47%tB]kθ|t5Nb1s(]*CcN$ՙ۷۠lh0d}||FF:uwh"^l}a8>F Eb… 9Uר/-[ Ř?Zb#4Xt+Гq_x/yy0D0!;d4 y Y(,zMHӋ~ o8Id6PDuk m ]^0*|f8݋ dRؿ aď쓘c/D4NIo@2l%:Ph,crEYo@G YglA't:`9#&:%pD|P?L#ߟ#G}|ғRe|!O->9 ?w`\sTb,yZYbY.?ޑokm_,Twaɮ-{϶U>5LĆX.a{ dz$Bw\~wvNnk tb?1wJ dj`:qlDyGi>Q.hf>u= Vi;'i8$>-޳|0n*/#I=ϧ.e˙H o~"BeإbxáGT@΢L.w(%wJVE%#"xESyM dci^OnMJ$@%6vWWiٹS~!L7a @o C1-zH= )^E)M*\2MT܆wAf1 o1#GI'yd@{u_i٩?Y75 %ћYC4$ R4 c "©b9$"E DzQ_'Ro N:{>TԠ+Hʇ} GTgSH>d8"PE$A==Ԇ-"eQ;GQқvz(-`C$Gq ER2DjRoA esoAD  1v)m_o 42taÅ\eKAO@€m&[O"SD o(ѕ(KE#1Άzi̗8 8{#HsU'F^ ]Ec9yW[qG>dzogS@= F.]!{=)x3Fܢ^4m,R:3E2%ƽ.tH&25缀9@!jp"4(.F&ѹOzȝ 8w] WDL{ec-Z]h ᎇ׵ET=#6ٸΨ5%}w9u1vdu7euR#ŌxGd˛l9h^d-q<4nAl믑0rʈ]r])v {c3HL%0FBjR>cʔGRUA*2R"kf ]?ӭ3g g2#nTUiwPnN|Y_Jeb ĹC ǰ&KDs.LtQҲҳE`i-B[G(hBib$c_!jG$*v3Rh|٨=#j !֭jc{/`m^;G)^d1/Gf\˹~+q AG*2rʼnlUYNrQYNjEd$1 j:;osQ6LDZKbʛ:8"52՛+X&IDwFwHʾ:2r7*M/TMƂN \Le6'~#ҰRkAI78FFHG7G L<=)Arϔo4N$yƐ53D55|Qd#MQ@]@;y)',~rzJpSWv֟h.aejc3\-|Bt5wzg(E[wGQ5;OԹifc$#~n] nݬL %Lm~3hg(M.MJ}XUI!Ӷ9`V̂̍9c@0!(Q̊ KL<;៝靿{gޝCsRMJ۷S~W_U_b@b|m޲;oK?Kk1ǂ캄Wg+-J٦(j7v,,xl  wD#R~\-Ȗk/9S_-ْ>V,}g 5[0- M_'ؒrěl-rD~/Nn5Vpc[ځ7EDXsògk3rǪrXS>NozQً`%ڧܗlp& 1 K,vzca&`Msna$x2AvcyL) e+!)Wk%n:A m(pb'* 7=tiR&{&ĸ`_6B M)lɖ^GbsR:N] Kvu; |G1AOܚ:+4mghN)AmBCCӂ&lO&O!ԦGf׫ {8`w>;Ϧ|CS'-i\hnmR ڰ'4mohx5\&سՌVbK >!nxE0-; EJ' nJ"Q LD0U߹pPo^#w[D WAB%>Ip7>4uyx^֥cݼh/Cy:(5ޮ(v]| j즄mi;%2)ɓVvqiْ5*>uEQ[:rA*;%W6>"[&,]To.*]0jzԝ>m,zlE2 S)܌Yk76/;˂A\˟,WFÚ5&;U+U0 ;҃/=jl?dꊖBNm6/UaļbrplQFP>sڎ\7A/˚/H/ݓl=!Iu$.0'~@!IU_ǕkŖߞcU{r_ukƑEg_ 4߰N6cݻ檎-lClkzE-z ؽ5G8Ah~L%guCbݘ\dR5oL|ْ_/) 8Yhɧi3iӱ]go6eϖjdH'[ZRBK*J%ZHt:|TS7HDQ$IYHh"Yڤ|2{v /î}V"WhIs-yRВj,^\R}Wi ͟bJ&gq[ұe/eťYXdPGE) pAġԱ(t76΂5npS,w<GuהGj !zmޞL aҽ-:0rʓ='a6=Ӈ cW=(SdMx3ş?ULܓEg%pAYCTw5^ל/V}y[)0ÜVU")B-(LŽL}5}VHOWcVY"wJ/EZRR!J U Q>NJHs57 jՂW#ODLJd-e M$KE \-9%ܾLOf։֌: sx]bqwETEJNU7k)Thq Պr2%](~zSRէuUH%m,4h}_jb-9}K$w(аY -y/ H<h0a7,&e0"48l" XH75Lojfm2ygYQ7קh{ SFSYpiE-k1.cHy%"~%sDɚ3esZm})1e@RƣodXx@+ uA6^KkJH$3u_<+\w֯I_mJx,oBP zZ\_WĐ"r/lH5dH@"U $Ѭ ih9OErHuYt070%g ~jr ZeED04.e{\K,~iA܄ ap1º|x:"xp-Xۼ9X0)x RkȳHoaar(] ',bo}N'CP9|Jک<)3ݛTGz$q.~^ \N{KU5R:ƴZ55@F%5k-ecnjC5C5<3"'"\yUe?bHc[p`2` Vhɝ"-Iӑ"ٜREN\&kȳ u͸p뚀ЍY ^e;>8yEIU呃OYV/E81v/CT:,JV nXBkԕ+ڡ@  e0l#)PK[Sw KgŅ?|UuRn 1X,PX Z[W7` eR|"(<1Ccj#$Tܧn(qZrN cpBW)ں{̋)-{IOHaKN,¹quOlt?$Omu%|Ҳ}IIAq@m<ԽɗWJ~ܐhn)'b枷!«!BڟYMhITܪ!2t_Ɩb 33ѩ^x oPK4䝣>f+0f[xrg0 )Hy#R߈Ax$]u8RcOb6&U{MXr -{ƹ884$Bw{%^5qk0Qa),tkԄ_8HFV})UټS%O>t}C.cK8;eW/M9RSZ%[4 \R~BdeBڎnno+?ꈴ;Dud_EFy86 osya= Y(Ŝk> cl$sHyFP wp»W)4eO_$}ƴ*C(*-DCf<S͈ ̃Cc: ?.y``!x*aQa)?< ) s ;{> s r[75ްF0[SFAU~N7a$8&=3Sؒc0PŎs}D[ sҺV~& "YWQ3e["(>ᱭ/pԨή{%05MVBC]dokj@. sBZ}}?Gz_noK*sG (f7]RNeg9 eTjRTޟ"jUl*VÀ)YP\s#tVR&jBWb%9@tqٛ!Y7Mܚ8{s7DY:ºu눫+3f !'N$Inn.1Z97&,Lג9M=qq15X؜{:|WCc-) \d}m$aJ ҡ1.ߺΰ(R4dNG"""ѣGUz E-[FJKK}X_7ӖؘL~s$3 ˲XŎ*-{:cCڮfK&1}5>{T$s1"IKK#O&d#'O$gΜAxLk<18 "ke~cڳY9G|*_N~ c%,)r ==|#+ϗߤ~Rhg-JmnΌ#Q7ttlwZWw,Y†R_UUE.\HϷmۖ<~RP4OXB7i?c_?~ߟZ8]^^N>}JpѣG$//Kaa!-.‡|P܂}'dʼ546h{lڴFQH߾}y`w~(v" "&ŋ͛Tat2e -nݢ}86{l*cxFݣGuB8p*(mσ6 zcC 2d=֮];}vÐ95%]>.)>Yˆ7T{ ڰ( rjnjW#tSN \r <))DEEQ @h>|^/As9r}{n1@7XBqN>ݻwuk㩅Eh Ca'N;wP2{-ݣi1~6u/;/v.c0vn }I]-fD\˯k[SF!!!̤)1oJ0. ?9INN}D ZxL =ݳg+r; Ba`ߙ\o߾Mq02`ߡ`H!p q>^("nPQ7+ ²ի=Y狌ߡD@sԐ@K`ݜT+S#/<#òhQsGnP7E{вàRưiuZ X<#30 .&媵+Pw܌ vk:9B o4a}`2 az8͛{Imhl }6tP(3X ZA0L0E عs'u.CXX5 'wF^~;fEQAf#W[:v!]ՈãIaHN߬&s4]wZ6r)pX''[z<J]t vAyD5v^@W~H ׃1$t\m!T D@pvq`<7wИ:o^Tiɛ2=7]x`Ah\RO݇x nsw+ktQKUgg^/)&CUt1 e't6. sf-~:<;XYf3Bر#%tcYB1@9c2'ӟİ0sr$q #t 10aǎK#ԡ4ratmgc1EMӆ+֒(4tpzF";d9Bgtή~=L^Jo8[;Y䌝k-:2I&m۶#tc߼;٘<2'ڀkҜu^\ t@|jV爣c\ 1VʹyQcD2A4ԏq8:a#!l gzi4l 46m ʇ (V0dZs t~IcSMKDWmX]ybז,O[X]&Zl]\ !`J7s1pi`p*^a)(۷DSR1 {l3 `H .uxPG=L^?D Ƶ3NII8;c043/,m  k@yC[Y]P0/j*{,Ԅ/Ւ=GdUӝ٘RkqظkDVنך$Qwk7+/ݪq7fT`uvڣ96A8,v CC6/Жqr59V/ځOv #G 3K֭7[Ի^=yŚڍ=Tg<ɼ=zuU[*̮0VхS#"H8{ [np°f-^Q E[[cnd(~ ^Xp(L&A^yߓv [KnN^X*u%Ş ٗ)~7 *ٹxt-%[]ocHM9_/ߨ_mYGeYe]Y `q .c1/ ԿqF / hJwq:ε]]v!,M=iעR G _FKtgtS4RxX+0~]{.:z9!͡[x>;GYzyߋ@/~oe9 m#6u6m;,r;޿]j*q&AZAOMhةV?ZqIAY gt8_®_2iiyoصnyH<[ = :b_y k[dRi7蔬l'zfYv( hA蜛$0(Wx7ym~wЅ)7/Mq@mEkX+)&q*Z+k&!-ŶЈ y_{5-&&L3qd0S),qYn`4Vcf'Iͥ^ָSǮtèN63[C'`˱i³}m{^< 2'˓Z D[ ǫqqqZiB,nHU8r<4p'/;6'Jw۸qd# 4(1v,P\<kEpi#RRRS2ivhĂ#j:+ǥ{.5JDzu/N"vEb'S([Y-f]^˧YjG22Vfg+0e3E/i M7xbMF.HkZ(X`ru Ғ1rÁS Tv8K,APA#ss^{GS.ڵRvx[Xᗄ!sܡ{/C.5swz~ӗ=pm z5#U70i4n5zEyV}s{}v|yqo^+Ăr6n:ndviIiM_۠s,/wQA(M,k=M5*714-sbg|Au#(@59K!@GVtH+XD T4MsjՆh17r\FB9*U8/-tj=S{{.?пm;W ;uA&)Au]m7WhX8n7\ނSc\ע#^-4 |ߗб ַA1Q}:uݻk'Oj05:isUb5Y}v:V9 dઔ(.s86.w{c b-""B"exq瑄7l5|c9?hʞy[89HSSq+DDwgXoMjwt9|9.7H2ghcꊁХEzb) me, ohƈx,xm!vR`HBWt ]ee'kaÆZ}u\{zҟ=1ۭ.uRҭ?VoDCހN)ncK7AEKzha}&$n mP1Htڴij5G(X،K|y#t%  9˦O)Ud$ne. 1= W=?qw_v;4ŭq(LsQfR`KY4#d֖SNCc\vU}= ]F5: FГ\iA̘1cԨ(MSg92s,E`^q,薅K ]XNR>Ftɇ76Amذ7..Io>ßev-48,{VԹ= ]`1(t1oO&_ƥ=X ɢ%Y 5V:h 29]jc|p"GBeEo2N{{Zv2j\Ԋh˭>7%qs} 35TNYu6oG6Efĸ[=SBo_:QDKRg=z WWpC(lw: asa p.0-]xUV-B7b%m_[A\s+C(ވic]5=0ZLfkUr1: -{۷/Y y0DN[qyTӡC_~ hc# 4! 7a[~KVpK}6A`T$#JDS KCr|@'AS7ӿ-b>%HRYOeǟv-N'JSF0r(oW<>KJ2#xriX8 \Vg  o }N;Hw3y.%x~D=V6!k62cc[=`ς)=aʚ=Kt= Z 5V`M#uamЀ ٹNqƬJ o~&Pg䪧_Ro ~SY\N<$M65󾣀_^K2tpy~(ن, q9#^ "Oycڔ":B\ҳ~┭)O4-ލȠk+n$O\q Z~w jwwsǮn!}(0E||S4E#Xو;وoĥ;/{.v~)KV$w)3Jh;5үU/|ӉxƛN:ԡ2~մߴ[ iŜ7mZgO6APDBی;evx׏d?ec6_h̖|щlSOٵ$Q'(,ސgߋx-ox{uСdWf!?V{QNKoo:? ЍqrT0K> y c\zVΌ&6?Pc7]mڝO7$S;HwfA˔Mg;W\j\`.9o]!݇۵]q^vE.RNZMM~УS;ma1rIҘoMYC;d)=b1p]q4Ub+Rq;J93T-aTmtHD8 !ͮfqEqo1oX#{/z|+ow[m ;ˡ>$,L6i$co%oŸ?W4O:Pg{7ƬZ3UxVUt bbhk{}7ۙO3S$G78xR/d%ΗEYܰk1z~u'C8aaxʡTti}0NwYFeֳX&%9_OvWi?m;&.|Zo;)z'a=PȈ@ygF@M"&{N~#]PWލ76q#T!5e- C~ nٙIEwxxx<5Q sێOwiq[o~Ѓ/}KM͐afL;jq$@9S]ySw|D…3vea0<*3R67%}d y pϏWuPP#KB08!9! ln+36Џ7KܘѴWĎ i/Kwf!zMl6d궓2w+x7Sgk|7O|]]yڮ}) M'l*,n 4Vx'o94& @KFPH8 P|z ؁>giڽ&'Sd= 1t2Pqs2$p3mơ8wbmV;ݾOo6Qq[O|q[vY{PW:!5[|F`\ƉT6uo{ūI>!A=dha׺'k$ӸGܼ^KR88@r~k65G+xr8f3[8rXKr $߳RjGvҪZt1?͆-^r욤 cWnn6|! ꙗϭݪĐfcT|:Fɯ [Gh`*.y̮T0 ;Yv![1B5on5Ri 8(=-EM|\1-b)n'r^Fv_{q=.$`]˵~_;)H9skNV?ʯoy $܀+k5 G QObߌ.yw iCB1e`94V6~R b\ ^"kY е?t٬ZҜg%}Y#nl2>L@6 #L}vuv6hy ɐ~J~ty2.یZ'd%#exoo_Oդ.u\5%MRjcd l_HywMg<{EQv;HyE±3e,W7dȵq  RÀ;P قi!dN(f~d[t-ǒleRy.9+z/ ^'tհ۸Ya13?7djێ_mu5-jԨ)^|mcV%7t}V4ڼ:N(T e5+6@CyJɏ.7rZQۨK2@Dh` @;9%}@HVG2oyKֵäG:Nݞ^]Ũ[]&;]DO !3,hy?Z*[rA esIX0ϹOv w;YU+ L?L;7 d|΀f<%omp!A\ .`ZKSkpoqg`N9qY@zKg2a2mޘe7NWON[K ׄs>8\U `ּ~܏we#sY&1s9hoggƛ0+v3>W=y._e2kTP:iEw`IENDB`openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/tunneling.rst000066400000000000000000000127741514270232600255010ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============================ Connecting VMs Using Tunnels ============================ This document describes how to use Open vSwitch to allow VMs on two different hosts to communicate over port-based GRE tunnels. .. note:: This guide covers the steps required to configure GRE tunneling. The same approach can be used for any of the other tunneling protocols supported by Open vSwitch. .. image:: tunneling.png :align: center Setup ----- This guide assumes the environment is configured as described below. Two Physical Networks ~~~~~~~~~~~~~~~~~~~~~ - Transport Network Ethernet network for tunnel traffic between hosts running OVS. Depending on the tunneling protocol being used (this cookbook uses GRE), some configuration of the physical switches may be required (for example, it may be necessary to adjust the MTU). Configuration of the physical switching hardware is outside the scope of this cookbook entry. - Management Network Strictly speaking this network is not required, but it is a simple way to give the physical host an IP address for remote access since an IP address cannot be assigned directly to a physical interface that is part of an OVS bridge. Two Physical Hosts ~~~~~~~~~~~~~~~~~~ The environment assumes the use of two hosts, named `host1` and `host2`. Both hosts are hypervisors running Open vSwitch. Each host has two NICs, `eth0` and `eth1`, which are configured as follows: - `eth0` is connected to the Transport Network. `eth0` has an IP address that is used to communicate with Host2 over the Transport Network. - `eth1` is connected to the Management Network. `eth1` has an IP address that is used to reach the physical host for management. Four Virtual Machines ~~~~~~~~~~~~~~~~~~~~~ Each host will run two virtual machines (VMs). `vm1` and `vm2` are running on `host1`, while `vm3` and `vm4` are running on `host2`. Each VM has a single interface that appears as a Linux device (e.g., ``tap0``) on the physical host. .. note:: VM interfaces may appear as Linux devices with names like ``vnet0``, ``vnet1``, etc. Configuration Steps ------------------- Before you begin, you'll want to ensure that you know the IP addresses assigned to `eth0` on both `host1` and `host2`, as they will be needed during the configuration. Perform the following configuration on `host1`. #. Create an OVS bridge:: $ ovs-vsctl add-br br0 .. note:: You will *not* add `eth0` to the OVS bridge. #. Boot `vm1` and `vm2` on `host1`. If the VMs are not automatically attached to OVS, add them to the OVS bridge you just created (the commands below assume ``tap0`` is for `vm1` and ``tap1`` is for `vm2`):: $ ovs-vsctl add-port br0 tap0 $ ovs-vsctl add-port br0 tap1 #. Add a port for the GRE tunnel:: $ ovs-vsctl add-port br0 gre0 \ -- set interface gre0 type=gre options:remote_ip= Create a mirrored configuration on `host2` using the same basic steps: #. Create an OVS bridge, but do not add any physical interfaces to the bridge:: $ ovs-vsctl add-br br0 #. Launch `vm3` and `vm4` on `host2`, adding them to the OVS bridge if needed (again, ``tap0`` is assumed to be for `vm3` and ``tap1`` is assumed to be for `vm4`):: $ ovs-vsctl add-port br0 tap0 $ ovs-vsctl add-port br0 tap1 #. Create the GRE tunnel on `host2`, this time using the IP address for ``eth0`` on `host1` when specifying the ``remote_ip`` option:: $ ovs-vsctl add-port br0 gre0 \ -- set interface gre0 type=gre options:remote_ip= Testing ------- Pings between any of the VMs should work, regardless of whether the VMs are running on the same host or different hosts. Using ``ip route show`` (or equivalent command), the routing table of the operating system running inside the VM should show no knowledge of the IP subnets used by the hosts, only the IP subnet(s) configured within the VM's operating system. To help illustrate this point, it may be preferable to use very different IP subnet assignments within the guest VMs than what is used on the hosts. Troubleshooting --------------- If connectivity between VMs on different hosts isn't working, check the following items: - Make sure that `host1` and `host2` have full network connectivity over ``eth0`` (the NIC attached to the Transport Network). This may necessitate the use of additional IP routes or IP routing rules. - Make sure that ``gre0`` on `host1` points to ``eth0`` on `host2`, and that ``gre0`` on `host2` points to ``eth0`` on `host1`. - Ensure that all the VMs are assigned IP addresses on the same subnet; there is no IP routing functionality in this configuration. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/userspace-tunneling.rst000066400000000000000000000203611514270232600274600ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================================== Connecting VMs Using Tunnels (Userspace) ======================================== This document describes how to use Open vSwitch to allow VMs on two different hosts to communicate over VXLAN tunnels. Unlike :doc:`tunneling`, this configuration works entirely in userspace. .. note:: This guide covers the steps required to configure VXLAN tunneling. The same approach can be used for any of the other tunneling protocols supported by Open vSwitch. .. TODO(stephenfin): Convert this to a (prettier) PNG with same styling as the rest of the document :: +--------------+ | vm0 | 192.168.1.1/24 +--------------+ (vm_port0) | | | +--------------+ | br-int | 192.168.1.2/24 +--------------+ +--------------+ | vxlan0 | | vxlan0 | +--------------+ +--------------+ | | | | | | 172.168.1.1/24 | +--------------+ | | br-phy | 172.168.1.2/24 +--------------+ +---------------+ | dpdk0/eth1 |----------------------------------| eth1 | +--------------+ +---------------+ Host A with OVS. Remote host. Setup ----- This guide assumes the environment is configured as described below. Two Physical Hosts ~~~~~~~~~~~~~~~~~~ The environment assumes the use of two hosts, named `host1` and `host2`. We only detail the configuration of `host1` but a similar configuration can be used for `host2`. Both hosts should be configured with Open vSwitch (with or without DPDK), QEMU/KVM and suitable VM images. Open vSwitch should be running before proceeding. Configuration Steps ------------------- Perform the following configuration on `host1`: #. Create a ``br-int`` bridge:: $ ovs-vsctl --may-exist add-br br-int \ -- set Bridge br-int datapath_type=netdev \ -- br-set-external-id br-int bridge-id br-int \ -- set bridge br-int fail-mode=standalone #. Add a port to this bridge. If using tap ports, first boot a VM and then add the port to the bridge:: $ ovs-vsctl add-port br-int tap0 If using DPDK vhost-user ports, add the port and then boot the VM accordingly, using ``vm_port0`` as the interface name:: $ ovs-vsctl add-port br-int vm_port0 \ -- set Interface vm_port0 type=dpdkvhostuserclient \ options:vhost-server-path=/tmp/vm_port0 #. Configure the IP address of the VM interface *in the VM itself*:: $ ip addr add 192.168.1.1/24 dev eth0 $ ip link set eth0 up #. On `host1`, add a port for the VXLAN tunnel:: $ ovs-vsctl add-port br-int vxlan0 \ -- set interface vxlan0 type=vxlan options:remote_ip=172.168.1.2 .. note:: ``172.168.1.2`` is the remote tunnel end point address. On the remote host this will be ``172.168.1.1`` #. Create a ``br-phy`` bridge:: $ ovs-vsctl --may-exist add-br br-phy \ -- set Bridge br-phy datapath_type=netdev \ -- br-set-external-id br-phy bridge-id br-phy \ -- set bridge br-phy fail-mode=standalone \ other_config:hwaddr= .. note:: This additional bridge is required when running Open vSwitch in userspace rather than kernel-based Open vSwitch. The purpose of this bridge is to allow use of the kernel network stack for routing and ARP resolution. The datapath needs to look-up the routing table and ARP table to prepare the tunnel header and transmit data to the output port. .. note:: ``eth1`` is used rather than ``eth0``. This is to ensure network connectivity is retained. #. Attach ``eth1``/``dpdk0`` to the ``br-phy`` bridge. If the physical port ``eth1`` is operating as a kernel network interface, run:: $ ovs-vsctl --timeout 10 add-port br-phy eth1 $ ip addr add 172.168.1.1/24 dev br-phy $ ip link set br-phy up $ ip addr flush dev eth1 2>/dev/null $ ip link set eth1 up $ iptables -F If instead the interface is a DPDK interface and bound to the ``igb_uio`` or ``vfio`` driver, run:: $ ovs-vsctl --timeout 10 add-port br-phy dpdk0 \ -- set Interface dpdk0 type=dpdk options:dpdk-devargs=0000:06:00.0 $ ip addr add 172.168.1.1/24 dev br-phy $ ip link set br-phy up $ iptables -F The commands are different as DPDK interfaces are not managed by the kernel, thus, the port details are not visible to any ``ip`` commands. .. important:: Attempting to use the kernel network commands for a DPDK interface will result in a loss of connectivity through ``eth1``. Refer to :doc:`/faq/configuration` for more details. Once complete, check the cached routes using ovs-appctl command:: $ ovs-appctl ovs/route/show If the tunnel route is missing, adding it now:: $ ovs-appctl ovs/route/add 172.168.1.1/24 br-phy Repeat these steps if necessary for `host2`, but using the below commands for the VM interface IP address:: $ ip addr add 192.168.1.2/24 dev eth0 $ ip link set eth0 up And the below command for the the `host2` VXLAN tunnel:: $ ovs-vsctl add-port br-int vxlan0 \ -- set interface vxlan0 type=vxlan options:remote_ip=172.168.1.1 Testing ------- With this setup, ping to VXLAN target device (``192.168.1.2``) should work. Traffic will be VXLAN encapsulated and sent over the ``eth1``/``dpdk0`` interface. Tunneling-related Commands -------------------------- Tunnel routing table ~~~~~~~~~~~~~~~~~~~~ To add route:: $ ovs-appctl ovs/route/add / [table=ID] To see all routes configured:: $ ovs-appctl ovs/route/show [table=ID|all] To add/delete router rule:: $ ovs-appctl ovs/route/rule/add [-6] [not] from=all|IP/PLEN [prio=NUM] table=local|main|default|ID $ ovs-appctl ovs/route/rule/del [-6] [not] from=all|IP/PLEN [prio=NUM] table=local|main|default|ID To see all router rules configured:: $ ovs-appctl ovs/route/rule/show [-6] To delete route:: $ ovs-appctl ovs/route/del / [table=ID] To look up and display the route for a destination:: $ ovs-appctl ovs/route/lookup [src=IP] ARP ~~~ To see arp cache content:: $ ovs-appctl tnl/arp/show To flush arp cache:: $ ovs-appctl tnl/arp/flush To set a specific arp entry:: $ ovs-appctl tnl/arp/set Ports ~~~~~ To check tunnel ports listening in ovs-vswitchd:: $ ovs-appctl tnl/ports/show To set range for VxLan UDP source port:: $ ovs-appctl tnl/egress_port_range To show current range:: $ ovs-appctl tnl/egress_port_range Datapath ~~~~~~~~ To check datapath ports:: $ ovs-appctl dpif/show To check datapath flows:: $ ovs-appctl dpif/dump-flows openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/howto/vlan.png000066400000000000000000002432161514270232600244070ustar00rootroot00000000000000PNG  IHDRorLiCCPICC ProfilexYy8߷{&spdvyscL)JATH*dhPJE$L}5|s~Zkλ:QÃaFBB"l(.;+2\ ϥub3d=#B|X+<" *B`}3G "6/A J()1^~XPPz^Hm.zT?:T߂D&6 مG#WcBzBhoXό7yr!_>Cd P;%C=-`-#[#c!(m37<=)_iWOi lg1"n3D0QDo>0`d[f 2ݞ9P \0DR?T"ABp2" `9燌) g߳Q9 K"sl󶭋tHל%FVvV_hy"ZB ;*h]6Z#0hkFߘx5G=r/ \_ Ca~Q]dHRLC%)r7m{mbOE$DE 8-$qEF⎾y^1dd@" gyԁ0f ď? ) iPJ@9.F @S`| ;؀ !2BB$@Z!dB.B( : P5t u@gkh B0 &07, *.l{a?x9p\Mp~ Ÿe@ѡXQ|() JerE"PI,T> UjEFQs54MFSRH^}$t6} ݄B?GЛ" #QØb1~XL&SƼLbcXVVkubc v38vñ$p8+pU:^y#WPT|;444BjV޴񴹴x^Sz|7~NΆ.. }15AOC&ׄ%"(L!9j=qL/MoJM~~AA!!C?#-0>#1&2L#YBH٤ Iɐɛ)8E 듽i r7y,l|2&,,q,E,YFYQ¬;wqdG+ll:l>lYl l/)A'؛ه9699899889p\\\ʹyùra 3K BѥS (]>.>h |O6ES*y  R:#+","$),<#&b* R+N(-OLVLE,HSqX\Q_H_P8'L#**Y&9$EҕfNnq9!+)(,[!VIL.UU| Aŝ;}vJK1SS񧒲RRҬr J}UA655%(F/RA53">T Z-RQm>mvoJi]1@:y=Yz+j(c,'L#FF~FF ƊM0&&'LLMLM̔ͺ v,-",ZwvR2Բ XZg}kcmSd3e+g{׎lnWc^>CC#j'N2ΉΏ\8\\Z\q˻ w=GqOƞ"{>pp vNupAQ=M==x}93{wO߬\~@abI`IJUPUVSpCMGPРЮ0g޷aQ ElbF.}ѢчbbbVccőBO'%\܏ﵿ߁c$Ϥ΃N&'_J^:hyMZu77fg7nv޸%}6;;w&]not|{ދ.'{z޽yڃU6?RzԧwO4+ KOM@!:22zBݬ*952_R^H }."/;&oШȥE^M_ּ }68*&gL-jw[umBluO9Ĝ\\#v9{%jgWyB,5$9CBCg^k̏:k/c?PbGR3))!i҇820?r=hvc999sN;yӉQg <:K:Ow~dɅke*Yp/K{i=}LxɻuN$={aRmP`8jηʆO$;fAq2:ztLl]sCK烾(/`뾦/y|]ڇ?~~.J5dbp4,4Ӵ=JlB ѝބADG$0ؕ99/p pPrB҈H!R8s\IJ9VSNNHsL[L'DQo@0ͨɬ9ւRJZFVN^AȑӉ8qq}uυn=u'nPKq=.ngwᇃ<{gyЇWLnnxadѵиЄdTYGO'^ϋI[jtkY{êڭ C/ }ϡ4蛘vBDk/u | $,+YYŐqG [2i˜\#Y>K0BKlb~ْW+dS|4BPU3Q8YU]s^7W/I?PHؘxƤY+%u6IjvW;:;;.㷗wqwSȣ#/?PD . q % 7هw/"%R+GŘx$RX $$DĊPbC_2oܿ@ AWo:8%5 >-}]]]ߵ3@r !7FΏ>ثw!!"4< &o",8, :~a)xï(~)*U@}B"=t(E,L V`7qʸeG_Jd Jg8|*6x^̐э9ɜԃ2=d Kf*"K+;;VJW88]\]I}~+JX!3aAHdTS $} edv(J(+ȩ hjjhы570Z76u42ii`uzVr:8oRwram}zto ]?! z_,o܋VIߧ\I<䙩s5]v\$gr韣*+뮨,t&ε^L#u&| F.ѩEɸc~g/8^+56zm2sG%ʲJٍ[?G@!'2F ;JRg@RCK#$ A{8(jD|&Ê3| (6*uՃZB H~>d6H.~ +Öcq{pŸa 7M5 m m F$? zWBI~yx2|Y>7Vp`óc?ȡɱɕmdzM)d.4*XMARDXHtLlCeE%Ies7h\Zs$5]< #yPZqحىvsqnuޝ>O _L f 3HHxqpCڙ=Y9Y,O_K[.}!"oܥ[5G, W0ilYl}VyPOiL}ܣ7~vyKWoᇛ{~0Y:<8dJXxSqр'A*MFi4^2E*BH ZY` >(JYP8:]p#=3eaOc_xq:REɣU=Bҕ@Rߘac`& 2yȭa,,.Aϱ9S[B);_.P/&@xHdV Pt:,}CfJKIηJ*wHZGt< LLͽ,,C&mmGœ7]AE{z;bn"ws;^5Y_I?-C]=1rtҳNjRŔ_)^VM,pscV6ۣw;iv?T|qY3#_x I ۏľx|p䳩ƙď/J_Vn.F}:Tj^} ~5uՍs?LRDDCʏ#[[KH$?Olmmmm,G ?Wl c{q6OO~{/ pHYs   IDATx |UտH 8cFbU vZQjJ[lW}m+,Bj-AA&eI  w%;{{Ͻ{>^{}Yg)6ED@D@D@R@fvM=0;" " " )N@NXP1 " " "=;" " " )N@NXP1 " " "=;" " " )N@NXP1 " " "=;" " " )N@NXP1 " " "=;" " " )N@NXP1 " " "=,!H![E@C z獝:1&/+835Fy2I샺ɭe!k D—lSc׏q92T\hDD @W.n4ý?W^Iq&RI#C+Ku35UTT"`py͈Ju(bk@R81qpZ@*ׄ 'W`g驥7J=J@34'7U{;^3F]|3nPw_yNFvH6%ι9+?9`ѵSʋ/r l]:; *|t۽H-p sFQWPkHn!qβF[gt)w"E{wœ;bo=,.۲ao /{&z[x}9Ѓwzuk?ZZNzE\)%D@GHɍ O/~9 4@Pv.Af3aEZ@@O48*v.ȭҎg'Z8ly930~&^wyϮ(q=6-7C~Z(T?S_lj6@83K41|^x)trΙ:}~[@oos>yTv48D*$<'gM[}?fB7=cSAUmȜ*yԝΨPxنڪUisKKv,:6@))~9֫ ˊ׆DHm: 4>(j7t*QƋLDU{!k5?Æ=edI\мt\PVN~>*t햟߽{f y0;zs֕whu][_eh'ҫ,%E@Ρ%i̝Y]0㾆i{0}Uz^gpa]/K~p|SƎ=90D --#$ p(4cd_М6wS 4yzRiO@g9&|qg5m 璊}ꊃp[}imw(㇭\sΫ<ܔ"NXKMoO⠗\q+:Lw8JfwZB) TtSpyUeT9xNJ*/ʳoﲕU?64l@APzo.X~o3yoŘ@'ȴF>#ZSmExAMG}AP8Qq5z\wE6G>)QpG@hgrMz[Džw8ҵ)R\tQ0ʌ&ٝh"ɼP O"4}4g,z׸`Zu0mц'N|SۋMp ^m?KR-$'՚@ ?'G۟YDG=[@GqohG<+5 F2x+Pt֓@5K(RkΒ2ezCu]@Ac yki9[֋wxHu5&rR]QkgiYUѿ{EDC/H$]J$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " " wYM$DW" " " q p'Մ@" (I$}-" " "YqhMR~[." m A<"r 4TJUI@X̙o?N~@puQ<ȸqp[湂[k51RTT|gƍWy4hO|ҥ@rTTT$J0"ఒwȞ={zhjBDty@q1˅;1 E 'fdgg3{ݩ.//Nx{s $ȁ"TVV:⪸ .E }&L]^^Opl&tjN{NQBڅ~q饗ZmqN pd, |,I="pUUUy5aϫXOWDKCi8|@׼U% D;sX^c'bQZLX[΋|oN2c vA&^\Nc I@8??[n:u9" -!cb"ӖN2#tU$(E:J+p'77Urs4C?IɾI$-(F޽{X nVwQ1fp\avr ͌m6 m HpRA@ZP$p_@SAP :$Ta;fqWD@D@Ҏ3:," " F@NY\#p'L@PnWE@D@D (I;" " "n_H; w;fqWD@D@Ҏ3:," " F ̊%jkkiǧCr Fal/qHp={S7tӞ={R}o;nܸ`[3k׮M!LUUeKJJ!ցG\3]Bt뵋Лi3x"U/̇mۖ 9 'iXiz1 o A-;S̺5]M*0s2HOCp,PHHH.,IuIa fV2P6=H='ANiIؿ! l6urR$0yI=C<{C =,ᎷJ',@2I4큪?Lt.4/g-,&xlmPW҉U˫V@[x! `xZTb'?pUiCOjQ6]6@|R))YʇXhʹ@<7i32 $;e/i+" " "j wZL" " " E@NrKڊV#@rP\" " " &p$ " " "\$@ (i52 $;e/i+" " "j wZL" " " E@NrKڊV#@rP\" " " &p$ " " "\$@ (i52 $;e/i+" " "j wZL" " " E@NrKڊV#@rP\" " " &p$ " " "\$@ (i52 $;e/i+" " "j wZL" " " E@NrKڊV#@rP\" " " &p$ " " "\$@ dZ"5Ӻvص宗֗Ţv%Cu-^ck|1W[} Td.|5!u`_Zb9i:nٌ#rj{|ݡCv߾}K>t6#z}~ͩ]=݋ ʹaqt 2||/ˇxT:]vu|/Fˎ8әS۽CP.7\p:zre9(qܠ w:C"-q3ɟ8{Yw-(8묑]>enDIq'~] 9cz2w7?=q3?{ܘyȼ/Ӻ*]^I]b$3clC̾]#N\G~"F'uXt3~blEc5e^ qt_3uk7uuj xcrdN@kh=.^q}?﮸y+>\μ]FҤ~@k]%p˷/6ѹy܏8]X3W΁gyLfG"=*wWpa5Z6ϤMD [p AX6͍@-:\}g^5+1̒# f~ox[ǮƑ}ka׼w:q̳+Y{4R_ݯI.}ig]4t; v?ldžkbJ CGhDێcddՊ;6u#i Y^r@N-;"B}s{D;o~eZc?-2ya)6f{dY~ \ZJ!rFߍ UA7SX*qf??}n┓;/fUorKZ'fu[>&q6\OO뵎mat8n̳ GmNC9 ,^ʷ/K`flvdւ q3D_94r^oްص 4B N㶬zj&'ֽgd!?8#t4'u]WG$ptݯy RkΦ^;zspyhoi b7]1K3*/I .`7m\j3<gφ;fu/ثˀ^]ʫ7{@A=V`="-ټ@B}O<6ah˚-jMT"wjV -!xaU}wf|M6sHU4Vs{dhWC|Ð|6-U#GDT]޲/r%6-ʡCd*9%M_ɪk w߷aP{eEnLu3\vp O9UhӱomҳJ6/֩lQ+vׯU++n)^GC^Q79}6>煴Q;m:o鶯 =슮݁5F \ }dcs7)G򩑌L_RﶴNic#/<t_1v?Dq~ :ıc>'_n~:|iXz7#l|ΩGs3Y_ _ަإ0˫}Jc{$@KK&*S]Ӱ9_As[oGCԔ77wFJ?3ǭ$߸-y.Uٱq/W![,$5s87T~!PsPy1ݸ:1%ZOq$?XUdE IDATիw5砂"kox5;+3]H4;zDPh } (H4;zDPh } (H4;zDPh } (H4;zDPh } (H4;zDPh } (H4;zDPh } (H4;zDPh } (H4D+kk?sݧMz!C5e4n`9y X!:hv'vV" " " F Ý;v֭X}Xjװ]2:u.UܹsՈ=0R w222233ՋKScvO>wq%ݔ G+t34if1k(?,m0xbu84ݯ_n-&XC45DҡCcǞr)eee05554sϽ /|+DƮMlQQ _CD^^^=9,b )Ǐ?zhm|8qe]6ńظ SȔ'aٓjT4L;`7ҥ ~P_o߾}Y_VG {l;  m55Vf kcزe35K/t Ax "c fbq,dqp 6mzWK/Dqu 'O.W)MV$]v޿ފC,G1*'j/;grNy:NBNe!*++68'q0M`"P"ظŔq _WnI``ҺH<=yQu`=yY4LpaG8vQ ,Y2sL?e9vNp6؍N!x9sx W_}gjzJXZaիEx:E1%T-.:n@0H !#34 w|G qN^-w}ypOS]OyE+߆?yѴi^y&QX<[gg?҇qμy8OHggCȁD$ZMp#qbW!fR?3pB8ꨣ>csʕ+WXOr-xL'567>ӧ2dիI  .&ZxϞ=#oek)Z~dii)8(@-,,4 \!ӟt۶mq0HkOpW{kf=X\җD7Fz}L1sl:P/uӜwRқ7oI))́pƍ߻wo5'Hs &bx[ēLA9`oF;Һ CJ֓O>Iw`^6sv%y䑬Θ1Q'J LDS1WFH${O?M1#GZ@@ 6Cu8Ηx3'i-Ըm 8Cȁ XpK)67\GqOZ{eyc*%˹VGY,^N9Kɫnf~/"QbY!`d7HnW0́k67Ɂ> Ԑַ*j ছn7I4p OVnlo3f#Sq9nH[bxq`ܸr ԧ:,{ա׸mw9Cȁ;8Wp/v'aX3䌹h4}_ڤq3hp|%؋¶qˀ|ѹiqK1s > !xVpGڸ$^ Ŵ(.d$A&(*]fntpIp^S&C0 \v/ݻCpoX(ou UI K*ˎvN{bW/"\͵&9g۪D{p5+jv͑! qKnKN#E[X i!k8VU~De%Yw8WcKޞ\s3V`18m+` ,"C82J$[82)pSU37zjr H w;7$JxȖp P\ ,8KVQˁ$A zuL5D@R 9:W w;r&ʽrEEB Wn UEth.f5ϧz/f[3\U'K΁{)9}oF)iJQ)2mU )rLnlq= 1[θM5JhF)iJQ)2C*nZ"kBdT ZUKx+t͹w pčDyF۲vlF YM֫!kF5Fm.J:dsR^F丅Oq=UebNm(=i_~ Ul?8άnS&/I>2E/ h_8^r ]!0PԨerdtHArب,i3fK(ڵo[":tkΕׯc?&4i:͌@:RXXhVoltq- (A4ܶm<6QZZQh+h)g+f2.=cV h4-ʘi.iRNO]I(am[zN +W>sq "ˆ̙.;3.Vފ!{1 hž}X]dIrxtުHA?Ys{hh1Ќ~hѼ⤖2Qz7;C(=m'$]pMϰxrb+aqz: EFmNFAwhh:4Z>3!֒UYD͑Yod L2QlK,>h㖪XlM%'DOC*!3B I,("Ma״X6XrP15A^Ϋ'm਩[\TNO*'B7(ֹj!Q9^=裏&:hŐb2Τ9/W)I'gi[<!e"Xs[IPHդȤ9V1A3XL~iPI& bΏXo yӓhoE/rB!O|zVm_ZZz+kVcZDXA3me4t:5R#A#t/ uQ07%:,90a&xk,A"Ȫ bG-FsX!K(JO'|"{J\4J4oT2i\u:$Q"@cjoVC&`Jāˑ`LX:,V@y/ƣ*&E%vab@``FbO[ej B_=㏋nhUiW;02ы9@9 CeAmL"!R,x7JU2The,hżM(EzlsHYhŨA~IC1o[f+ղЖJR'lW4mŏ) ZsHіkiB> T%[nEC(AS)4D%'Xq 96ᗼ![BjT0[* 06̓[1Ra.PؚszR'ZfM JQȤ獵mڶR搥k6yZuq%E"@ㅷ"S1l0 =;[rŠ,?ׄ7A8dQm"dž;:yN2P|J4רTY]cqۆ;9$yZ$M&mQ3:aeȧ~MЫ l)O؄tRP/ PpV'Z Nipz-6);#&ӓ Z(oUQ )\,BAt&wx4DζZl AuF*!lᰰɼU S#/7[S x! 1vvy-&j@F ,"齨)`[NҔ7)АMvVȷ X#ϹP"VOc 6Q  zAljeLO60E xQNOj3Y*$Q V2TH&NOWD0JO:`-V6M,n2G䘱'RCyJK>[|x Xs&e4; PuIHC漂VD*JO = |Pvq Pc6p6y= ,+ ,&~&(,IcZ(`lI mVV3ҦV9^'[[*Y%kz"EP9S>7AE 3*os3}LO_96y8XЇM$o[YGTӠaM Ԡ_,ìU4kYVpf09=GIDڔRz;h5\ߝp ;:H(b# /Ueqo+fYbT{'9 X$,*$Q',j(@MVJha+dPb,mF#tM )Za+֜ZmVl#HBa'ekXȧQ F5G>UxNWOT]WO걾g Xw\UC8eΙMdKyߊKu$ǰՆ2Q͑I<|QR֜ w+l;2SإPA wd h~Ic /IUQq6d+c"v&흔ckB4 _9H9AILDL6[AޕK'{"/ 1Ad-L2l[1W,V6k5d ʘ o٨Q)Q :ɤ9[%Xu &n)D Z]7Q/IՌ&%*&(oˬۢ͡'KSzR3D5g9Ha!Q'e%Br0h9R!4 cK<ت$ ,IPmV1{ 0v``@xDŽ++hCex&ů-O0yh Z1~iUjhjFO*ūj P҉ 5cXWLD)'ml¯%4Qa{gY=ر f`6gb cKSXiMH ⭘(J^o%v4ݴVQ &ʌ!9v9pip}!tf(vĎW-X)tnxT2-)%b֦r6ֲ1M52]8d()V,4t SXmpnjvzg* +96\N(AVY(U[D.]mt/*<%ӼU6oE&6WiI(6 Z[mh6HyՆsX ~KYiVm Ag֨EjXl~<bՖX͡p-<ٖԟ\e#,Id`;c('gk f-/[֦p$EHj+$uO<'*][ς?om5]H k^ϟ{$xtjWH 68x )@9\.X@$H8ڄ?c )w3\~GP˸>c MWmyCرcy#*t糉4Fx7ӟhmN>nܸ1?@|1іn+UlsUwj.aR( IDATa.;diqK {X C-0G駟f3pvDg&'[у⧞zʍL߇:u*@|BW\ᎏ 1sSc4wF,!L?GIvfH|T+ͪ) ފ u?]˰3G1f݀0rH&*s=8++cɊr > w|d~44Cc&gG~ߑ/W]uP-N p 'PkƗƭZt)i.8Y+ dv7*l5"_qر憙!p uZ 5r ܲ w| 2N-XMWqhʳPV U M7dY5k=xf, Xt~7>} Wvq2ʁEĕ,o۴䮡pk!Kv;bĈ .# \ 3 :hg4gb \> /+IS;ZAރqk"m| ćy܀/P!`Qmz>-o+}jªtS tM9?鑖?5KYQ`:fwt͉)Axií Ʋe˸ =_xsO]j@teǁTrK|WPy@H쬟;&ā$dGk/˦Gv랷>Å/ ד1gVCNSa>Q %;7FڱW3oͻ=h?^Nϻ8.W^˞-R8}ٵknxANp]۪.]<32NE&5|&mؾ IFښt\l%@Ŕ%;ā 9dS&G#! x؁ Jq պՃ.V~[qmZ&.& @u?j_[Dt{teN bSN 8 S˥R$og^$P-FpNp3|I"f2p;-w8:!1@Mq4XPA #v l ֊v[X~ 1T`J-p?;\C|;ifnuWD@D@ґt," " iE@NZ[t$p'>@ZH{Ҋu;[8ay%/nc%;,&YST,Q‰ )r0)I5;y??gh57@GLPbc#CjBډH;>-0a?'Jw6pU*.@₹F@:-GN{/=>\K;g̘QT} 7W4wAq2 M)-;iP3.);A7sH^=~jW|xc$u+~B`ix6BGL =Y wY'y pB<*«ZD сLoƂFN~paxɁVr - Wa5kdddS>:y_.l= vƨűɖmsIIA[DLX\޺ྺ#go.)~ԓٺ$_Do]WރΗwv9k놧'ŶDj:P)KW޽Rϕh~vnURR8ι~rs 6mE? t+mt ;_>j=hsr yݿfے1þׅwP<FJaKpKhUٝqߦԎwYѮwz{޼ ߜ9럡Єpq~C=vEqYtB<#Ez!Swްb?J<5f9 `U g}Mh2Skݼ{ތ8'u!+N;@*Ê>W99aBoMȁАr -dax#[ѡ;v~}}OK}~3>|n U3' )5iq~K?egw\q"P2։鷍̂KZ3{cݱ.{3 4-٪8LR: 㧆GxͩK>0H m'BP)PLN;qp&ʦ?p/k&4kom 4,^mYӟFk2{Ef1:{j,}S;Fpew2/(گkWn#H$h)]:(]tל? @ZgF9Jҍ'AS{M̜v@&_<#ԡPCCCUEG];k.ٲ:5LEON=h@ ԁ49ծw4@9%ft<ı ZS.1>_L-_ Y|e*JCvj)grV_p䠞"%K>{7F-Pm-0z׻[<+m" 68DDΆBy+r mH%nUncuܮgNuBs=bgk)w#]anO6ܕp0<~_W`ѤSvx4;;0WѥhxkfD=Gt9XE5ldIZ탢xzn ;ҤoqtPE]}䳩^~uVܽSćo5Ow]߯ɉC|x3-غ'<_RUʪ~vYC/ڹuWVMD8tYў_9}cvxZ68 ^eչ7o&ʁr •5+ *CŷмœXblMLk떯@]Tl|K돤 "/㩋uW-|c(rVKBSϾe޺pNUviube&-h)6;u:~ Ri-jIҪŧN1uI\vWVrz‘aKEczdrJ@JW ;mʄI[=^ڨ+rɁUҖT~+.nBӽYQMguW*z{o(=5K@JQ:*܉U.N A$"'M=U@ЦF" " " !ٝP?BknJjjt?=z%O#u(]ggf|Y jkܥG۱:L3HSQ9x(N1+#c@6u?(2l)VZ;̳W,T]U]w,ټRSSiC]߽'|OUXP5DСCNNzJG!=:9U* v[Yʬ۷GPCx̌PFݺu>"?|h*@r.S܁Xg{ `Sixo))޿:8:%W)Ý.fʮuoq^Cwo'y)vԧ>UP&nwBөSV,ApXt:v'cn({'@_r*++_}~ӦM=ze f:[Ѕ"Y9*& #s>w(!pNNv.r xѾ2vB~;W^yy:l9aˎ۹sg8p nފ]w1''>s=wر=(6JXyKsˣVfMܶm^xA8Zڵ+vee,v̉X2]" qcӭ*xtBƩ@l_zʾJFWuVȟ'&ʁ47~w8MpǼkmphPLÞ={N9 w Y~D5'׼k4Q u :K1ܤ**͎?_*N^ڭ[|h!ܡqkͼi-5ǁ_̃,$n-ۛwPN݁pO@?C If( wp>۾ g1MEE-r=e̙0G_87nhjcIނbumΝW^ykro>GY4 GFr mC.R>9\ˎ;⊅ :Uq {uGqtwala|`Z1!IcrµիWO0og~gZ\".a>K.7j#vOUosǏ?c bڌ@'rҰſ{VbJ[qCh}~.D,۷3DR$:1 Ð߸ٌB< 裏{(**0`e]vsNqKYøhw X>OE]t'N6;xԱb f v-Q[Qjw̾@xp hя~|ÇHȁ8+$E"~'I#>Jr|eWA-[2CG׿&!R?0@vig~];8& +WSiFO{P97swXr΢E\pǚjOVZ;>=D8,olF.|;A=?5k ^{%jf;@Eo;AZil9)w}˗/ѣ'N2oիW/[5 RK@WX(OrJ'?y饗@vjϞ=@|5+jX,1b' N38*͈x[ zZ4R#斝M?awrN:$\yү$9K/O gGwq΁|9Wms5!Ӊfؗ8)-6qJqu׭Z7޸ꪫyJR:q04h*{I^^F@ RiOKf3n:/qwrÒ%Kx&f{Z͗p Ϝgt!:* />}x4sxI#wp)/˼r|'yg&P*4r >Y7p]h 3aKˁdUCԌ0*;GMC׿~Gx8Dio(xO#07uHm3~$19?guf֩{+j:½r ~:5ِf!=f}{fW썇77>:+0@9]+$ı)4VA45VHc`ziwL+x@qdUMk w|\x+`+9Ny晼Z P4\tMÆ 2o6@ ȁ́ufW0!Mˁ/8צp'3eg Yx861Þvk_CG.^?'bd+}D9]vZ2= /0<y[#~ pEw4PIG,r m39}5Bq=,1Q?n[CJ;"dE^6y}sn!{&"MV~BSOPz"nV᭘g(=ES7DҦr m09ޞ}h:hrGAߵywX=S fi cqޣ<_W*tIWFNpMrX|57pGia8Ϛ 0gΜ74+1宸3 9. }BՋ@ DALr Qv'xMߕ(sp'ꖝ(A&#;6mh:j*zp J>eΆ |& n#d2L r"  is k3<`D'K͠KM wn88ۣgc.2 IDAT\0U1[۲e V{Ņ1=ODDA ȁ4 /k3@uRQtɛA۽#ĉ|_裏-[q<ꫯaС_=~ 8$0T ȁ26ݐQ_q+&p'9밸4R,^E{y4WExx' ms`N1--H@ _ź{VfoJ)$aGәqO<,]Z|7"\ܳgOeϥÇe,B=84cZƑXgǎ|mb2yk3r qqmJN\qfc\Kx>R-taeg>V.nV\g}6wN%́-׭[yw=~po\?hD<?s_~\~&'|臈[ LutX=O?R?xBJR$@p ^H o9\I#p]o)W9A-aEt^@]ZjՍ7Hgsr^߾}ݓ皅xTIF@'l7rqOˁ$hnY,:œ;E#a |T]N^]KY[޹ewW\qg0޲Cfr5 2#Iթu |,۷ W]uըQڑv9]ju۶T?~H-vU+_wK7I\.oz~!KUe9BY;%?oEz91[y[vN^l:,U{ꐾg MwE`JȎoQnJ9aR]?>\\\})aN炤s 2u;mpSwϑiLpWUR7>|;lQ-rဇWeTWdߗY>贂̝e<8z׮lݺ?-o֭\Zu4x@^^_yg0BN/ޙt u>YKf*kE.l:q}&ԹZ ~0,9Szf6<>@AzuC0tJNAGxs&_I2£(UayΆ[k .9c`^ǔoaPY;,rֻ}ۡ8(nG2B5ښyB{|Ynڱc7_ܱc+l\ZO?>by >\{Oz&HSաs^eN*nfKd[ |ɷ~ۅ'٣s8d i rv+6wԬCJ][eX S_+F8.{>;%kct[$tdhPU~p~]s@CiqrS~3~cX8s!N GםέhTVVY`}]n S&F#75OCYZU}'ߵ;|t99ٹ:fgp4X2 |sڟl]p4ߗjvY便X̷;d$NRҁԄWLgy giF;j&H̅*O} (jK ]zH;y;u%gvGז/޹s'vlV?x{N{KDkovދ]r**weW;t ] L fhM/^~ׂQ%PeO w@vʤ]R>yw`}t\RN؁d#Lp Ge\JJ>^|S?U1fg};i/p=C ^N=uʥ/}vr޸yTN}pN{/u\D ?9/nvꔍS@Cf.)9Mՠui˪h5eɒY2HR7&wEM&óL؁՟e{w-\ˁ4R<o5gc2nBNџ]8k/6w_ep&۽gD6WU@gxdtS'AVmXYK>rXf盀9澿mXH$)HsW7 EOqǝ3 y ĝ-EJϰwGr 2oUty=B3/kIS]{W&?~Sf^LxzVۏ>Wfwpyb{,H:ӇqCҿ^UQYuU')R0zdn+ڸk>)%m8wUS[wM4y 8s%캏,Kh(v_Enx[CHB0 =I=]mvg#R{p5m~nlXc4֤.C7~\szÝ܍{>2622ؚuY˲iX;RVE5._[Ǩs>O^fA(+9{mNhy$pMCnXe~FP;̪j]tcl. i%1brx#;:Z1ތ.VYtVXԤ*"@+~%@bi[ؐNht@g41hYх&@l~+fA{&=#Gw-} ,@J胗Q#Bbᚠ %@Z+إzH+kfk1&sfO8Q;wٰeTTjC\)8(N3to|1&tiA hD>tx`Q"Ur5`VVK."y Yr8cNԓJ%$:yWD]R! a$ӮYŴa˖@oD:Wr&P5Dbn"f2dܘgN9Bٜ4 J8Xh2^ %tL<&yivJОeyABӳ'/뎃;vf|cXNWO-C.mMgn-gIbqxApiqVe@N9iMOGZAn&<ԉCӓ<젮 Č:X ";/ ,Hw]bk"GX|~k=1o+K}LkDc;úQءc8M`vBxvV3dC,iXM˥`0=uΑ Uٗ2k2J"h@*"S$g0 %(8%M8t,7 Q aA}YQzjͶn\"x)8D3#5HdM_ $Wx~[aDSn r @ƌO"@b`bNW@OErAzdGۉoeqcdyYiNWP)nV>@A5qܠ!-Cc9 ,uk⎶A&BH'56ҩPG NJg dzLruQ$*s<;Mw2ml#9=1KRRE Y]U!x@BMpk&>B<# ^8Ch61!@< E 9Kj;DG)iaH lc &h \3 BhևqV8br;= ^7bɑpC>_3NgŤ4)b {3r<;դO&@\u%XRi/hyO&n V5ya b͝N}VHZNS(‰VM:zPDa['[ݖȫUƾg8FԇN9${,QEI'd蛮=v rz5fA%K!\fqd85%pCNKh\atW/@  *PWѝ)sqikD hOca0ˆB:pbb޲}OCÑB\u1f<' 34=|@nkL:]c>_ xE:m)^&\GǗfKߣ†@mcA}MiQ*,Yu$Ԛ 9SP:?mj e% JtGOa,9{v7_BZltZm][voyoz;$:S<&,'ie7eXS9C݇M麷ǩ# ۓ9d5Rnо)=<9־; ={&$ wSP^'kUa+eˑzWn|bִ_6x _?m{Փ O<|~SK7(+.*7kUꗞ _ #Zq!QQ>F~`JۤXJ0fCBmHհ/_mkV<3ØڷiucK]Eges *Et*- ]~2+\s Y9fv` `WCmeA' mu;\hʝ^}nLspC[opm@<1㫎;5>5RjZUc;+^_ccؼ]3dd2ّw|Om/,v5 =W^qt].ZwՔh#ua`J=|Q>`f1kH@oH67e1ClZW,"@H-Sɛ *!@B.4; [PtGK=GVf 1Ik\ :F I[1^!4ڢpgհA+b1WtG+⒢Bj3sz}Xxc`>+EZ5Y=׶dy JHI7')K;ò=e:]y^['l %k3xFwXZ+o1)YL]SU-ϓ3e<015AFʘ*m}o),YBŵj,h ++e G:nXy# *޳7x{,(w3}ˤV#O٦WwxM+r* 0J wspC25vr>& 5au.h"A!C \j>ijr͵"&bѮ@vͳЩ; ue9YND|:UQ;Z쬟rgsȦ |Wk"3yDեrtJ8f ޻~XgTA{3O/t#Cڬ[v0j|WSm5YDv1Ԕnå)&U>"/%pd N1T;03o5UL9u] +ZCiJv@ۜ*@Ck$hPeo(#嘛E6!3:oŽ3{Y=O?cSV15?yqڞgfzci?SOq+k6{7'k~iq%h']y($)_0|fÓ~#dwvPlr9s̱pyl>cFʬRTST"fղP /OH8?;r|7=9cSo+eZMdbGɑvz<B㱢n,HT&52jMtԣt`6gL6ݲ3syG8KO7VNwvXsmtYhvq IDATǥ/YN~0@x\޺ >)];?ެD̹0gDh045x6l`9X2%eL3eZmg'q|qj+wȬ0Bo34m 33Ik[daK^#N35'9FH`gIҔDƇ ڝ&E~,Jc-E/9TWNm-+޸M3|?0}udGZW`aq%C'=,m|JĽw]Ԋ9vͶ}̉/-\֧W%+ѩ߹4j5q(wZw41128cT*oP@Xǂ/mmN_]+iYk u=/;Td{ HrHX5[a%_UUUPP0tPIخ]233 {vAv ---}y徊:ori}* 7&1-||k44z˛MZs lY`Mm]eeͱj=;1Ӧ-.WWԑ#a[i>Zhl5ǗS?NIvjkZϚ֪D %bGLwbJ58MꗖvkGnu!E|UVV:t褓N7oիWHII |>ȹ}շښ²cwjs+_[뫨HUM9'#@+z[K|-%@؂oCIdoR܉=)8a.'55BG7btfZdV /C"Zx-_udWϻ~Z4࠷.,*D@#7 -$R$@*_wĕѣ_JrD(hF AW?CZMb!+zݡC||f 44ڡ4kYڭFɞ\i)ޔT‹U0P jЈfdkemMζDeUUUeGQnsbMmtiD5o o@;x46@ف}eT8l%MHxR w%$%Nw+k6w@A#lm,0*jj=VsL^$kF5P$!!1d¥DHϙuW@:<.gcQISeD& '\H m 4.E7Ie Y$[.WK+S0AeLkvE2XMMZ ÓL"4ĞZ rٖW ViIVk{V@]fiv0}P@Q.\}bLgF* VVT>jf:pp[v,/׎ ¨4%D#g.)^m(4l:aFr_F,~<%d ܇cT =  D\[ _~AY~Q9ׂBZU@J{u ,h[\&FLDn$p"]w>/#Cl';Cg%>i_4ou lV I=cZSL$HVq ]^w6 j{4: RIĀQS#H(/uϿqhIaiA2Ғ (HBLbJ4C*ZtKaLwƩѝǐ {^٨ إ14[AF6`W0 M|Ux [>S#WZby,ZL5(5LWh4!UUE'8fT&fKڀUa@APuSӜ{+F oH" mM=yy'Um98jDߤdM# MP(e;yl6"L aeC)xpXڙS>^$Y6^ `. ]u O/]vևQmJ*bF=`Jߌ6PR"<~މeC)Rt^7))ibvey '9ۋ⧌;S Ό;;dEϷfnΩ~o,ԇji'yrcLZjH~^o %99ԜʊZǥLv,KtCQ|iN6Ze5U ~{o>?f ][ LrVΜm?S]-BtiRSS3%0e_?H^|;Kkf1 [925h@, uf"9 9Jl=iKMMRW =Y݅M@KMuuE.ǧE_<׶=iJK4ţbPwa᱿т2REMB̓UF <}K&N4e5Biۜ>l':2D@ʱP2}CWL `:Z1ުͥ=wVY#8zE4q}j_z熥g,;cn6iM5kOsټ!;CSw x]pxS*Ӓcp펐&_An u9qҁ2UeT9~S FS/@C{}#5@!>矮ZT .;{[И6n^䔀U{AԹm%:|^Fē"RKe޽sIKK#|κW[H2 b 2yg_~uBgɻ'O<6ӑjx`-n6Av~ˮpV`6t2hJ=CRYYYUU%5Z&?=pgܹӦM,94#ed`;M{ *DJ\NgȀ [Jy!7нQp2v`nKよ%bkJm|$ugqe]&j=;rr?m،D&oRңC(zTKCp -^}CqJw1J;Ҹ*62 t'xBDUW]5gILvܸq999=z)D`6y8h &G@L:l;StG t4 "WvW":8 Wڷo_Hjlt8G1uBqhD4H ѣGo5kpOGt ׯÇ3Rxʯ}+UppcdQGEU'=ԡ\+ Yx-R[NtDY3̙3N#V2[ 1L2H $(HOP ip72.ڻW|FFLhÐ$z :Qң mTE3o[4ڧ43r6躖/_>o<4G|HkSĀC<N>~i feeڂ#G>ChbBܐhz2, 2MlO7;RF1<\ ;2 u'|rc͝ځ@PxxSPa'oy eDwXB+ĘB\ @5HsD0qJw"9,b )+nV3AT)~rY0G*I @X@Pg?{'% AodP xa-x(",qt֪U.rmC0V~^a#zφx2 8IeU!h@Rرc]w[om dsܢaJ5|li $'(ЭpVR\|%%1@x~1,k(]uND!۷噬*' O2E ;pY4$FDHE"K\{Eig?O #2(* @}kEL}Ç#7 :8&:2zHB8fU|E@֦M.ru]uzW\'"WX&^h9#\F~#FrXi u,Wx*A6;X lݺu;0:2>Szx* U2`+ ~={T\%= !&,Tݓ:;v`sCYg_Rl NE!`q8?IREXXĔϗJ^!N|8bQݻ:fd?Emu.:ʭBDb_s5|o ?H D\ \+ #NˠR +//9,Nկ~̒uXbR)PTD " cΝ,Ώt"(|SN9o:2^G ˖J N\gXk.=iҤGyCr1DS)6|*NO?SoРAH a[-:깅PG F"\pvh„ ,5|B `ʛ_yc5ɓ? :2LU Ewb9rkYEO>{ a#SJAP!&".<_}UUO<`9BCuDQ="j2ˊ4bg /pƍc u&/";.E/‹PDs* 69BVD".6o ," z!&͆J]ŪRkF@3UVVş9c=VkcUNEqΒ%Kj!bV~X~G,;K L-Q M W^^@u>p@:H.l -*4@4" K.DδiӞz6a)N4J1 ~:W\qŲeC4W:P* . w}7&}r[oe dXGtliPt'eDf-^é~ D#KqbRD#"+8nx7bS@pC N"Vin;M"INN\P:v׉UIWdsXw ` .;쬣arRPt'eשkydasXX(^)tHW(:k׮Eo>rѻwo ٥d]pU@@*N<S[[o~s…C+ύ9,ǣ氢DUBA~Yf 3fO?=j(d;&sUGʄ{jPt'Љ!7̸y}^`"1bK@"`F.g܇̟?o߾ 9Bq.1݉E! jժUu]6}G^׉UiUeB}DPq?Qۼy~ubڎ+tEBvBq¤ X5o=z7l0~xɓY`$Xʶ/D(X`ZLt' uPAuy~" & p2uiFuS!޿ҥK㩧 Ybz#Gp-}!C\G-ŠʠfF@Ѱ_tԻw޻w/ 8q":g4iDS\eځdAs=\9BBC"("0 (펥ǜ8;˗/,9,?,8Cf_!'|믿N~9|0 :y'?Ad!(D⊓Tt':*1n%cև~qƙ3g^DGT*Bvܹff/x`3ַ:)arHK@fi|5 IDATt'㶕+W7~=NpUW]Lv G!' pa[Z&9X}uVlPtnju"0BT2bcPM1R6떥JB <YŘ:ydܜ2e  DGACwX˖-332"lƌ'tO83R@# #\V3E?ߟ}&JDaQx@@ѝPAAB&e :Ceag+8EG/ɰC"͂޷'>*MLc!"^}U3@v`&wiÈ5Y(͎VюoNa¿۲wP-FNt4;xǗ!5Y櫫Xe3gzR#I.YsƕLH$xmĠ[N85 ]rZ{θ>x:eY^HQE P'Oy%xDuU !Қj*~ɐNN+π>zxX|nמSʐv5StFRWN㶢c5ݏV,l ^:-;!(ڒǻ~,_꒼jhC ;lHg~Y_؅_>ڶy[Fo̯u+mE҂j}/}{G qzp`W4lWWlG6v&+4ɠrٝ.Pv'3zƥFe"":P&`Ksw|vgr/|9?%"rд/Aߢ-71k~/Zd?PeOd\@meBpFϸHV16AADPQ-"XFQjo|Kۃ>JfO' Q }"mݍ(zLE*DaɈX BjND inT`A DmB#(":Tm1&<ւ/9B1`NWa$ڝe-EPUBw"U\'2ځ`qQ (7 сH'M<5pfݱOj?{Y-0#fV""\7dP&x]m h=;Y(MKңm47Yqa z`7 WS2މOϬi7VkeΗVT-uLwW!il M; fӎjͽsŁ\WbarjXXfM%#w>8۝8/Ͼq4-$Hu .  DDd -[]Sj%݉X9͛7^;$=ҢǠ̺ -z&[U̺iڇ-zW̽LZy>˚|m|S/f揰{~gVO>fb6\,kߴ,ٳ' $/u$$y54XN9l;'ضlg0 Od]eeed][*n'FiCk)[n5OLLxM6,۶3_jD+ND' @ C ~D;,MRI&NOYnǎgD=rlV 턹Wڝ&;~_}5K^^caW~o|:wYdo.$U-|sNn{IȌqOuՎ|bH3DY,]gkoy|| K?VwGTt|8-i~Cs`_!)l!wwkIw䐥P 5e7K~'q7IRܰ|vVhۍr;h #DD!n30faw Vfuʍp͠uǷ;u3 ͯ`=^߄l*\Or0Oә?H[)v;LNhqR{wg-/-xSkwQɼ@G2 %dF+JDDƒi~PǭI>YٝOmm P^`l[44x{ΛG#8<}frM-}rsOvϻi*Ta9s<{Me&IȽsG;~\b"X⋍6{Duws_Y]b#`ډ(%"u_C@MfY@Tr B@!5QB@!P(CYs-8&b'Gɕ[3OyaU<)d6*j{Q-)cT?X0h ;aÚpˆ y8ίvIDHСC%Hu'iBUWWw)U֯ˈnQlܩ2jrs$h;>f|>X62eTf86ތG}o5S[ U02j#1Tk%Qv`ӛܫ/}]7ٴ/^߰ξ CH쯫ΟI=J7“؝j TQfPBѐ`]UE-E{6OJfw9z9Jm".߉o&edr`t6Ю=Vrd %7J],%ݹ鞯H'%B~54HR?-Z|w }:~ @HFhH".t_t-';p'@b!FdHF.SNM6_;֎8OM6zhZj!xRFO3n$Z%Z4uF9\H?Q!uo޼>Zr%#&L8q"9i!8A,"" A? @w5':b?)Θ1I4C xo)V)x c r'!1{ףv-dT]dC~3 8tC~_̙3G}3H%T\b\ TIT?ЖGIFYYk2 ;x S8㌓O>Qq(MRm4"@=*!|չwMy]}xOpDt 's頺!DMQQ%,Aw@ZBu^Rv@j%rm?Q^^^LG[ />P3i?\4wsNM*&"pCzM`+pw_t|sPC ׾6waÆ$H &s $`*dRpRLYsړ_]Z>W*ꫯ~ 4{3gסȄ3f8Zi*vض!)P)'|wzf|tP N{;$@ʵZO+d4 +Ma7׿EnF9jijr!@Xt/3x֭[鐠>MlcO=ԫ QH&E#B&&/D \s/+(FP4QX(rhLp姦'8b4GBDb;v Xy7vj *Rb!TP1# =o`h[d $PluG_@&.s"aՋ/FCD9v,}X Ȃd$v]DRKf_W*9˘\x@nDW?_ฏ3XN,f,zד`<OKKAiU9kѝI$N^D7KI ^VaU`؉HCj{PPPrCp=OaĤGFtH:jP4g'Ť '=(T ?oCA;a0CQyhx`9r C]R@/R !$ {iao~Re1Utǀz\єk\I>z#VCԦYFڥ@tpG>S.1xϗ_~O# .k *Jļ#kw&PȲ83ϼ N?t2$rsS5GUJ qF(sYT;dQpR^Im|Dѝ)wK;[l7o-vT/I)G&ࠈ91e CaTHnzU2 RgR=HCz>|8,+_ ,9bcVP[8Pt=+x(\ evG#I> dff"ʈ4(chW2hT(D6V<~饗8Ucv( q9xN&=B}rGx Sbh=Yub׌ՍvC [/lAfι a H 9YN=ёI+Qa@A)\{R, Dg5TVB)Y4IP1 Ew^;fsE#UQ4]'P^$zUusQ3egnBBӃ}C~vM8ŪF>EL1Gyܔ1 2f:t{:G`q+DWp#[]8ছnbϟ#e ])wϰNX W1#e w'VL^#HY a;V+hi,x:,>' |#l/`9Mq~=,|ls2Y+P*cF&~)2 p<Ո% 3ay k2i%e|HU+grc`1ҳbd _1(: s*ܔF `*3ˁr r'O4UYCQAQP+(}5Zi*cXz66SNQ;YcA~!ENTp>#X/K@h04a<UJ >=iGoC)[J(cXhtutlrt(LX'I8-Fcڱpуu@hcK=`EIMN, tXkWH1`"qAv ݁#OY܇q:&`{"KCR:Dl۶#XW=?,rE T9Z oU O?47uHJ(2}nL9z( <ow&fk{)8Z׌8Y>w\{ D肑x;r}]s˜tdLGa9Tu3Q6F *OGQNʴ 8>5:f665"0dmxK R"}FO SYEA 2 ̍r,%6涮*xkQRu",6l7x֬YPy"blpـHyuVyG~_CTF8EY:ߕo#NsLb퓎 : ن痿%+r |A'ÑɪFI.ڵ-1`g~s~Ň\[ aDsPSr:0H<&3/V<2ߤ+N5%"K@&Ld(xHYO^Pt 4pc},LM30ꤓ`~pu£]9yPrwXa svѯ{PԳN #1̦r1"bT- <NcX{,'TH?m2ζ ,cTTEw:H: :-YF=aMo$Ն, Pxm4Yug° zC%veLfXRΉ0vg]̔)S :bcQ#N:4IX34;ݏyHAGͲ OiҘt t ݚ Y ̃+'H#k*<Ŝ}TsHP0&KX"91"pE{"&I+ Y&> $+0ƼVaIg}6݆j/J} =+wp"RsNs.‹ 8uN~1TN9PYUW^= 9O<6d=~Qoe29 PѝakT0&`A%}f*e"B)VI C"j ̑mz꩘vibrsǛ8rDw2Ergdv,8h(!YQW/Qd+W۷4a7.tR +=:>CYnRGR,Nh6 : qXHȪ+;QwEj S~K85)JqT&v'LZ.XڭheeN>bBe2|pt-Wlі If({0g.;W`N91A d4sLa 9 B蔪D H5f!X@>bO&FBw!*P!NphTh:,~a6=T=ܘ,9(ȉJס YƜ^L>1Rƍ.TQe(ة*H"=>V$1vƲe߾}z-vL%- ~8Wt'Z7Lz dfZ_~L:X7*eFGt-c`A3gιwxSr}Glۃ]3^漲 <;dV+@JlYres.ǰΡHb9chxU@ztKy:1{07B3j(Yr&G55@D#/*KۡQR&^ѝ(-$[6`; hUV1Q7I2(e_?`Ӷd =.GR+NMt{6Rhx!@5RV][U ?]Ht{oq ۛZbb[`6A8c%vL1s?<srL7Ɠcb$# o$cl Ю]ޒZRk[j\U׭[ꩪ[Ul@4]GKdyLAlO< ;Ls.n$Ma@p 022}MAKf?q Μ9jm8q{{1!F  z2_[\>74z݄՟GA{Iw}W@Ʃ&h$x/kx%Zu` h2,d6X͆g! $ۉ2>_g}[M O~>`. c4LYT6v\wU54]BrtV^e1NONsneH1g[&Eߔ#P']ofˍW]ל޶NGb<,ʨSqL'@]~'Ɏ(c͔o43 7\!L8A8x[ ZiiilټƻlҊ)>LyW2=PAar=.[u\,|l!LŭfkFWS#xE9ք΀'w$wNAI>%ڔ5-ٞ fU>=IΊr$ :f[ KJZd,y[|"kVS#*3hK1.r1ĝq?"^;HfÅ%Ú\_tyD(;|Ax nxhYL^XfvM_\j,wT.)JN&ȱ #Z܃Ç1Ʌ+Zf {pEY l6Za 3?.+t(1@ ZH` Y.(pZI'G .ڤu` mpA!Թle^NlI`f-l:qv ϥF,rĘt3nA!@٣r w2N5B6 25rؤu쫩sX"ZXcF{@&= i;jb Zj9..rchM תfWMiGo'tctJS.fL ;6JqVnè~8k9B_89Z[`vFr:Ur: IDAT^=69~5{\5PhB=V5|O+ Bك\̣\#[= LE +i6L751/n|E:$7tJ'U?/u,Ƨ5—ҩ@5@KgٺfLEĝ94+Ĥ>.wx|eU4…` 32F[va3%Ęḇ&b̓\<}Tݜ̋5NrƂkNՋZe7af?K}`V<{TxO`kyy9ܵkQ[ CaE Py b@=Avf=z`C->{`Yz:=,,XL+}N7D3|al`uQ&-$%!ڬG*{V~Z'퉑 ^8c84*UƤdd욃sNNd,.)X6ZBnH WUQv.PaF͐# rϱc_;sA3je/ ͛bӧtf,[B`\HYC)7vr nC)G]qudX=kHNB*`г&!/2;j1§]XʶuX"4`&%WK6}n~VBh`:3cz\TFmj}ZRtj1"ч5*|w]+읃5\]p!Af+ؒrf m[i 7ɩV6B{ 0?܃8b@sIBWtjSĮ.Tus2L,գjVHMq`5+6GA1(ĝ^C{2w=E}0p$=ؿխo}v{[$}Hi]etwFਟ {Z ظ Us-26 ڌnNM ^O#@VLFǫZ~%^Kae"E*\|QwY7% MШ>'3O> ~^^SWP`>$ L!qh\ 3jf\=ǏЃ\S{4>>+l2KC5I'x#sry3j<fluywS4T  togϮ YI1O|mʥ_@+ rvv6o CAt94Xr(=l @\0A"g\ Mw.+=+q,v$to"9&Hl3V3mFr-,8gĝTs[;2B42):@Իo|od SXCswb ?٤U6lV=PiFLÌ!A<=pX}إ3l;pPc&oii,"m$_ Fu^h>,_>m A{'p|߮V{$3$̚>{$;SWOzK ;H9BJuk=LAM{ɳ7˘7mĆwvy˙9%ZU4G wRNo^xJ/|QK+}[GJVtyd -6?slKSҶh#ѽ]Q\rsm [Y;~*;2&e}Q'K?}dnr,m !uN Q `W8e~.ys w]na~(`L ʺ5(r|{Wn sT19"WVT}hS*XȒ%K-[Qj㋻۽aF g-؈tN1zE]6vBG4F&=ֱ7r7ŇL帝W<~p=Lʺ=͋o^T|ēِuVR424WZ;g'ǥl{9G? <;EkL }#˫Z[[2^[a%vN]냖ϖ7ⱸX3]IdV8ˀI9+?mܐQk~A~YiWh̓K2{ -"[9S@2peqOr1౵K?~~˘~C87?R..=\)){jvrr |dIqك'[9..cn-i̬Yw`ǮN}> z('XnT)ݝt骵n0t Ԇ.nŒ@ s{ 6aE ~e;!nEIh*7hq l$:V]/>>K0he=+P muMA#?/'[}'SCll4pf qW)uB-GeTMNYZ=hAR^P {vCS8dΗ7߿iuDz~]c, [<!g|#'!8T8{ϗ({`:CacjCZm%У,{]3 [?i2hבxL#W)86nWf0@jpnWm_U\lh1QXg3=IwNg ӊҚOU?%EVy+_QgFxnn~\\k0/=Ls{l"C~UŹ=kHq*l=ӵmkZ/\ki\!Dݖ;X-x|h<Ϲ{E>WM[-_m*Z{r$zNzE\i1We,Jx F=)M͹97z{]5ă6B/4KZ3k[QLLwI R#7{?:_YyCCoj,n pb_wAKA+`XxeI_,ߵ@wVvq]Jg5{zǦ5yct#^'ܕD2l;~+~ݪnEAA |w鈕{ZV`Wn`"Ǧm۶vAp٬m#)k9'T bu6rj8aHo/ tN_iѪ[uwbkn)2)N྽eݨDrϯ*:"߸v,hUrWQr箈\.{=5.6S6P\KUkP]Rfnu*w'=t>#a!T7Dux~^<A$WwfW [ϕU0ߙ5jd:Hn J[|Ctyf5ْ&`ŀ x!M<e +c#{Ao1~T-8}2s8wXL]0`Uozݳ? EXBk=8 xd-ZPSYSYy;ႥՑd/aYⅲ?QdYW0 ʒpSe8 **Do%5w?SE!k+ީ=B@<~e/J7,jJ30-F~ UEBw?;kzMCf}Jq5xp@N!1ǧeO܋/_//}kbt n񪁢Ծ_׍ăPֈ3=zE)ߌxC3QgKG.! pBȊèu{^;/; B'[=}<&6^huIc?$|Cn__tV1P8v=8t:9r#zNQ,J5=hJ(J^~ʩ܊u :3,>% a({OK_5HG 93ZzVî/ySweo>Xrm:9 #3 ԥ5_r݆xlYdN^P,,>]Eseoٽ9hzr }G9$n}9FMɊ5rI P5XBa/SZ^43Y7tYzĚKϺUK?޳D+eK!|,:2uVݳg6T3Y{=kҽW]NnA}sȌzfdQp%{,i^;kH6uX4*~ ;V{̕o40N?qd2[/s=[^G- 0 fî<&b /[ohNeV4ro1A1Y}|hSjm *eo* HM_:p:gf(uV?u8˪s%PpE/ڸGX1m  ҇STx"`r8.Wk~9P;uHo+/9J:j*Pn( Ψn132P9Klu 8YFnv󰾯 |yϦrB!<|t\8:(Uo;LСzh읃cjm&u?h8UZOշU 'XfP>+܇LOFFvF %Z_䚑j7&ZI69qng Ga)L*R/$԰SE "մ FƍܤC?#Ff* v̡%iwbS4)XkRIS*(vB U3y'#FewbX8F*Ӎ16 R;M_g+b1\g^ZRoC&z4l^- !`FeZ'(|c#Vm IDAT! BЎI,UVhl>FI3{ϵ3V<ͤKߜߚC2> }X%9!JS!j{ G &**;A#SH`r+0+;HCN a8s٣oV"mof鯕BOrXO SqzhyaC9B?azMv;33t+(\hX#9=x8 'n$97uI6E߹wKpp9[煙¼d>...&&b`AI|rNJ5S܁j_-y(z2I)G&h8|f̷z.\RM7Y_SoLAlCI-/6LÁ欼ntOx[W^˟bBUqQϱǃJ!G[w?oE5lJ?/I_߹Y'g B[럗9S\)Rk ]8W^L5vdM^_s^bf BUJG~yzdL"aiO}\?HEk*R-;\tt4d TϓTn!:h(+[Veq=zƢtʃݮvH的fXCxWswf1 6aisYx9^]q=S x|Qfs\e8|Sꗍ<=yO>/_MFlr:d|õ"Tw$ybcfnV+QۀY!@aBFQm%[;qfB+:\Qa(h2SĆSi}׷VO>}Wm-Sp>|uՂ劀 Ce͉Jćf8uҧW0eƒOvqhHSxCP~yu_;Y䝍O8`w牷^..o~ELO&Tě/RITKƉ&}jaicM0cF`?*=k3aMc]?AS`SJv[3d Q\q* U7n:SbX1"^/W_*} ;$e0KLLamt)~Tcd91 Gy/ٽgCD D# 4~( -}\-5aVɭbԽ45X?'p'y걙7 +\1qU,?޺^+(|Xl87?>Dyt+]{^>1ݗv]4[mG<+{8{מ .OeW`ކ|XzDm+f2OZiz#/s m(V/vvJyjQj cdv|됆OGKm2&Y셾Z5&S6ׁFWf:BB#@KDŽ"Tf:E+]Ż8nOU1! o/é5ru 6rlIx[(&iќap`I)ʇjWc#aխk ,=v C>菖4 cee;lxuo;wv,۽l {׷RV\Nd߳x܁Ű฼ ݗµk"'U**JKvo_f$p,)JJvvEZ*9/_Y_={Rd:6撮vun]e?ޢK4x:D"r0{cJPpp'tqy>xÅO-J Z D(\HuXn0L*--bYN6l8ᢢ 4Bd+ BHrm;1*NB|M|P}b5<򻧻d[vwQ܆YgXDe;5ZNNvt%f~]: ~RK׈M$Զ% ͷ'ʆ: )))IIIl 36UT;5ͮ0obcOB8! A _Zd[~j~{ n: b\]NJ.9sNO,ױ4Ź49?35Vl@ cF'p*7;27lMqmI^qx\,TJ3=1x}o/W(nU ў#o~t:V/9 e&5n/- [ԣ{jx'{|U6UYplR)-\NKB ]Իuwm*geJ*cWbR+V(QSOgԟuG | >WmFE"N'p)q4:! NNЙL`4M!^SZC#hwz3Ne}J%6"ەTO7TYgSQ]ON~ u(#6/ۍGR];|FΎ.HpFaIڝ@p`)qs-:D)b,fj5ſ{'jH-Y69hbCs,W¤[WKbW;՟,Ӌ-xOYm*WN˘ WV}'vk߹[7)N?WQ"tY+no]ƇRHh^p̈3|||7 0z!±ڂ nlb_{c4m0i\fM<oZKG1>lY8&w't*[Cn*fɦ]U>\llJxENW_"jnB];ѥc0[͔6]+Zvl j4e8_1Hr`("bJMC NhhpoHiჺTÇoNݶnCnA&(*F^\ll2p6ƺ9xnQpF>b} m4,g1f9q /?ֹZnq{K =0O?=Pĕs\>Xd7]xRc#ĝ`JļW~1n_˴ua=Ekr]][=<"8Dj^4BiP0@m߾+{ ^?FmyҍPٸ7 Hl# Lim9T+*1* Bs}aFy*68qyVgI a: <[vl}lhɏ6 N'&ӵdͿK#}xBzT3 Oɫ&Õ1IaE^'=I"}AjKr0-m<%0:#R*=m5/t_cS9cw`=?znbW[z3 +znª`/IUQ&ߝ BPvkl?5?~|gCVhUbu3H{\7a5!{%d?-YtbeH?43-=w%x@AYi4cV7SYESJ\3N^GBW8;m8&ǜe0+^}e]Mw\.dIv{ s`7욃v[40 퍍4чm\+KQ^Pq vTYq⚈uvA朥/#LLBIK%VDJ>*(F'(%0Z ܄hT"$Tc7EL@$EߕԹ Uvůr䙘 4%VjKg.^̍7|pe fΆSRq&fY\|K ubOg):ԻֱTkj=d]۲fӁ HDϞQӲe?`|͕lzVؑ6J.&|)h26S -jP j uJ!.Lݗ:{쪈wZ 1/YU%yf/0?L}18e~dVhdhs,auhFcGJBKԇUqi\N;_b?d/m|JeajӏbiC.d"ur^QM|T57m.PU6S*q8n]3 4vmHȭgNϴcl,|BYCQ]OOh[etq}o {V?iO~{ Olw a5rU@oJr3[ZDbM !@?q2 ^BcMBN-LҢ_ T&!\/FYӏ<J(Y^,FZDxO77TliB6ߤ1 Lx #&q];5 ;Obx{ppMlHPꑱp=`F dz#.۸wƂ^_NOws+{r.ێǜufwا32*ڴ{ˆp֨'Xz%%Zp>穽[XR>yM .Kue)C܁jKkB@B`{d%JmN7_hqڷ/BUlɯh8]RxN[-|͕`(M B6obd.yjjJ߿daZzW$0<~'Duvv\.<B*\hn%K0vghfcysk%Y$.ڠ8EgMu8 w s^>ff54 㡱-XxBE+p !9k1>)` WݗkmXPS VGh#77֝f!+ `0m PF x4B0N`p&ܒէsz%JMzM8TD9zEl1mY\YPv, #!^ԫvCsuNBJ*#(3DZY6gGf!@-o\Uj{nlgU0s$ DD\p } =:Ge_ {ZM=joFǯ=2ӘQW3z47N2PH mugU>N>IW@t:NNRϙ\s ;#L3hztI:(ƛѝQ>f6`zj7$ 2 @[LarN#8& s  |A5[m2V@UEzhbp֩5mbUwETgcAdxn9\FN ZFGgrO !H*(ч,F{2 ;B+mbäflQ{rmT!kf5s˕9 $넱Jĝ!8&0Õ9nDf!EGnor݂-Eq@LXdQYv]Ep)(JJ4eGjrs:e쒌n2r5`wT[q)y853Yg/G"4 dWt뻙@4*`Q"TݖSΨ(t}zhgaG`7IfOx+8Ac~64wJMr[W<"MO0Ң&@2*'E^6|a2Jv838}8 9&u1dGGr:fpܭ)0Qvn~~Ğk}[S0rrQX>M0~Qg7qvfQـi )32's:A`WumlBjLcy_I`48M1Lq1J5"! XB|ksZᲘ[S3x"9Wh)cdb3ҊW8eEK2zX> *2AՓ_ïdL<g1z_Yy):8dp<0 a%KΈR"X8`#9;p 8;;>k;N0tc'<1a< &·˅qvc+s>wKtn %xFyJe4sI'8XEΏAUp,& }S0Fcq X{; V?FA{k莪sf8Y L~{"SxQקO@Oya?/F 4cNwip:kaN!`଼B0饁qpF nxeĝQȐJ% x Nӹ\vtx:t!;ˎ[ p>(@ 8(*ccW|^@mwYX/ eg21?Gƀ,Q1lT~V:@l>Kcx0!:-u^kGkbWf,/J8,c#]O{^kx-ghA!+/! F@ ڳqAĝ®: 8EQ DŽp_`56kduaUncAl[sW:j +[2׳3]_0JlQRg͈XM2N9aA@kt uȣ(؀9āCKvFhru^آɆv?`xEuX|=1 sZtr>/69RvFYyc11_僮/:J7"=sNO-Hg&#gz VJ'xg<1Qʡ`;!P( "kHIidH8] #;v]/>,"@S [,1xl䪓:‹fNxk jW 9QMV1_^uq0ObX㥾NBwMvfE(OL ܕMTo0rXxXRǘޓ(:HAI!,8#*5w N`IqFjk#8 'AtP[8 @8>W2B@?'Y5>~žҨAKB8&%~B]8Fx%;$[I5FMQƙ*S2p@dXEeA⿌-ػ Tx7DGk$nXYGzuF?/lgg5BuvwP#\I<0RB@Nлɠ/T]"UF! q?&҅8\E6iħyp 1Y|ve2bծ)8&0R ktRc1lW.!@ .Ø! <£]Y33!B_ A "+t=U7V{&*$LԚ|k$rad+]UZ $d $&,yw tX cYvz!@A@gW1*C4Ժ3슎DhW-2R` ҕwnAPN"=1B\ڕ=NWB(Ύ\2e𳎯y4vWC$.Fv 8{:6640 Where ```` is your controller's IP address that is accessible via the Host Machine's eth0 interface. Simulating an NVC ----------------- A VTEP implementation expects to be driven by a Network Virtualization Controller (NVC), such as NSX. If one does not exist, it's possible to use vtep-ctl to simulate one: 1. Create a logical switch: :: $ vtep-ctl add-ls ls0 2. Bind the logical switch to a port: :: $ vtep-ctl bind-ls br0 p0 0 ls0 $ vtep-ctl set Logical_Switch ls0 tunnel_key=33 3. Direct unknown destinations out a tunnel. For handling L2 broadcast, multicast and unknown unicast traffic, packets can be sent to all members of a logical switch referenced by a physical switch. The "unknown-dst" address below is used to represent these packets. There are different modes to replicate the packets. The default mode of replication is to send the traffic to a service node, which can be a hypervisor, server or appliance, and let the service node handle replication to other transport nodes (hypervisors or other VTEP physical switches). This mode is called *service node* replication. An alternate mode of replication, called *source node* replication, involves the source node sending to all other transport nodes. Hypervisors are always responsible for doing their own replication for locally attached VMs in both modes. Service node mode is the default. Service node replication mode is considered a basic requirement because it only requires sending the packet to a single transport node. The following configuration is for service node replication mode as only a single transport node destination is specified for the unknown-dst address: :: $ vtep-ctl add-mcast-remote ls0 unknown-dst 10.2.2.2 4. Optionally, change the replication mode from a default of ``service_node`` to ``source_node``, which can be done at the logical switch level: :: $ vtep-ctl set-replication-mode ls0 source_node 5. Direct unicast destinations out a different tunnel: :: $ vtep-ctl add-ucast-remote ls0 00:11:22:33:44:55 10.2.2.3 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/index.rst000066400000000000000000000042171514270232600234360ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======= Project ======= Community --------- - :doc:`internals/mailing-lists` - :doc:`internals/bugs` - :doc:`internals/patchwork` - :doc:`internals/release-process` - :doc:`internals/security` - :doc:`internals/authors` Contributing ------------ - :doc:`internals/contributing/submitting-patches` - :doc:`internals/contributing/backporting-patches` - :doc:`internals/contributing/inclusive-language` - :doc:`internals/contributing/coding-style` - :doc:`internals/contributing/coding-style-windows` Maintaining ----------- - :doc:`internals/charter` - :doc:`internals/maintainers` - :doc:`internals/committer-responsibilities` - :doc:`internals/committer-grant-revocation` - :doc:`internals/committer-emeritus-status` Documentation ------------- - :doc:`intro/index` - :doc:`tutorials/index` - :doc:`howto/index` - :doc:`topics/index` - :doc:`ref/index` - :doc:`internals/index` - :doc:`intro/install/documentation` - :doc:`faq/index` - Looking for specific information? - :doc:`Full Table of Contents ` - :ref:`Index ` Getting Help ------------- - Reach out to us :doc:`here `. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/000077500000000000000000000000001514270232600235705ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/authors.rst000066400000000000000000000016121514270232600260070ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. .. include:: ../../AUTHORS.rst openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/bugs.rst000066400000000000000000000046751514270232600252760ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============== Reporting Bugs ============== We are eager to hear from users about problems that they have encountered with Open vSwitch. This file documents how best to report bugs so as to ensure that they can be fixed as quickly as possible. Please report bugs by sending email to bugs@openvswitch.org. For reporting security vulnerabilities, please read :doc:`security`. The most important parts of your bug report are the following: - What you did that make the problem appear. - What you expected to happen. - What actually happened. Please also include the following information: - The Open vSwitch version number (as output by ``ovs-vswitchd --version``). - The Git commit number (as output by ``git rev-parse HEAD``), if you built from a Git snapshot. - Any local patches or changes you have applied (if any). The following are also handy sometimes: - The kernel version on which Open vSwitch is running (from ``/proc/version``) and the distribution and version number of your OS (e.g. "Centos 5.0"). - The contents of the vswitchd configuration database (usually ``/etc/openvswitch/conf.db``). - The output of ``ovs-dpctl show``. - If you have Open vSwitch configured to connect to an OpenFlow controller, the output of ``ovs-ofctl show `` for each ```` configured in the vswitchd configuration database. - A fix or workaround, if you have one. - Any other information that you think might be relevant. .. important:: bugs@openvswitch.org is a public mailing list, to which anyone can subscribe, so do not include confidential information in your bug report. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/charter.rst000066400000000000000000000206351514270232600257600ustar00rootroot00000000000000The Linux Foundation Open vSwitch Project Charter ================================================= Effective August 9, 2016 1. Mission of Open vSwitch Project ("OVS"). The mission of OVS is to: a. create an open source, production quality virtual networking platform, including a software switch, control plane, and related components, that supports standard management interfaces and opens the forwarding functions to programmatic extension and control; and b. host the infrastructure for an OVS community, establishing a neutral home for community assets, infrastructure, meetings, events and collaborative discussions. 2. Technical Steering Committee ("TSC") a. A TSC shall be composed of the Committers for OVS. The list of Committers on the TSC are available at :doc:`/internals/maintainers`. b. TSC projects generally will involve Committers and Contributors: i. Contributors: anyone in the technical community that contributes code, documentation or other technical artifacts to the OVS codebase. ii. Committers: Contributors who have the ability to commit directly to a project's main branch or repository on an OVS project. c. Participation in as a Contributor and/or Committer is open to anyone under the terms of this Charter. The TSC may: i. establish work flows and procedures for the submission, approval and closure or archiving of projects, ii. establish criteria and processes for the promotion of Contributors to Committer status, available at :doc:`/internals/committer-grant-revocation`. and iii. amend, adjust and refine the roles of Contributors and Committers listed in Section 2.b., create new roles and publicly document responsibilities and expectations for such roles, as it sees fit, available at :doc:`/internals/committer-responsibilities`. d. Responsibilities: The TSC is responsible for overseeing OVS activities and making decisions that impact the mission of OVS, including: i. coordinating the technical direction of OVS; ii. approving project proposals (including, but not limited to, incubation, deprecation and changes to a project's charter or scope); iii. creating sub-committees or working groups to focus on cross-project technical issues and requirements; iv. communicating with external and industry organizations concerning OVS technical matters; v. appointing representatives to work with other open source or standards communities; vi. establishing community norms, workflows or policies including processes for contributing (available at :doc:`/internals/contributing/index`), issuing releases, and security issue reporting policies; vii. discussing, seeking consensus, and where necessary, voting on technical matters relating to the code base that affect multiple projects; and viii. coordinate any marketing, events or communications with The Linux Foundation. 3. TSC Voting a. While it is the goal of OVS to operate as a consensus based community, if any TSC decision requires a vote to move forward, the Committers shall vote on a one vote per Committer basis. b. TSC votes should be conducted by email. In the case of a TSC meeting where a valid vote is taken, the details of the vote and any discussion should be subsequently documented for the community (e.g. to the appropriate email mailing list). c. Quorum for TSC meetings shall require two-thirds of the TSC representatives. The TSC may continue to meet if quorum is not met, but shall be prevented from making any decisions requiring a vote at the meeting. d. Except as provided in Section 8.d. and 9.a., decisions by electronic vote (e.g. email) shall require a majority of all voting TSC representatives. Decisions by electronic vote shall be made timely, and unless specified otherwise, within three (3) business days. Except as provided in Section 8.d. and 9.a., decisions by vote at a meeting shall require a majority vote, provided quorum is met. e. In the event of a tied vote with respect to an action that cannot be resolved by the TSC, any TSC representative shall be entitled to refer the matter to the Linux Foundation for assistance in reaching a decision. 4. Antitrust Guidelines a. All participants in OVS shall abide by The Linux Foundation Antitrust Policy available at http://www.linuxfoundation.org/antitrust-policy. b. All members shall encourage open participation from any organization able to meet the participation requirements, regardless of competitive interests. Put another way, the community shall not seek to exclude any participant based on any criteria, requirements or reasons other than those that are reasonable and applied on a non-discriminatory basis to all participants. 5. Code of Conduct a. The TSC may adopt a specific OVS Project code of conduct, with approval from the LF. 6. Budget and Funding a. The TSC shall coordinate any budget or funding needs with The Linux Foundation. Companies participating may be solicited to sponsor OVS activities and infrastructure needs on a voluntary basis. b. The Linux Foundation shall have custody of and final authority over the usage of any fees, funds and other cash receipts. c. A General & Administrative (G&A) fee will be applied by the Linux Foundation to funds raised to cover Finance, Accounting, and operations. The G&A fee shall equal 9% of OVS's first $1,000,000 of gross receipts and 6% of OVS's gross receipts over $1,000,000. d. Under no circumstances shall The Linux Foundation be expected or required to undertake any action on behalf of OVS that is inconsistent with the tax exempt purpose of The Linux Foundation. 7. General Rules and Operations. The OVS project shall be conducted so as to: a. engage in the work of the project in a professional manner consistent with maintaining a cohesive community, while also maintaining the goodwill and esteem of The Linux Foundation in the open source software community; b. respect the rights of all trademark owners, including any branding and usage guidelines; c. engage The Linux Foundation for all OVS press and analyst relations activities; d. upon request, provide information regarding Project participation, including information regarding attendance at Project-sponsored events, to The Linux Foundation; and e. coordinate with The Linux Foundation in relation to any websites created directly for OVS. 8. Intellectual Property Policy a. Members agree that all new inbound code contributions to OVS shall be made under the Apache License, Version 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0). All contributions shall be accompanied by a Developer Certificate of Origin sign-off (http://developercertificate.org) that is submitted through a TSC and LF-approved contribution process. b. All outbound code will be made available under the Apache License, Version 2.0. c. All documentation will be contributed to and made available by OVS under the Apache License, Version 2.0. d. For any new project source code, if an alternative inbound or outbound license is required for compliance with the license for a leveraged open source project (e.g. GPLv2 for Linux kernel) or is otherwise required to achieve OVS's mission, the TSC may approve the use of an alternative license for specific inbound or outbound contributions on an exception basis. Any exceptions must be approved by a majority vote of the entire TSC and must be limited in scope to what is required for such purpose. Please email tsc@openvswitch.org to obtain exception approval. e. Subject to available funds, OVS may engage The Linux Foundation to determine the availability of, and register, trademarks, service marks, which shall be owned by the LF. 9. Amendments a. This charter may be amended by a two-thirds vote of the entire TSC, subject to approval by The Linux Foundation. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/committer-emeritus-status.rst000066400000000000000000000054001514270232600315000ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================================== Emeritus Status for OVS Committers ================================== OVS committers are nominated and elected based on their impact on the Open vSwitch project. Over time, as committers' responsibilities change, some may become unable or uninterested in actively participating in project governance. Committer "emeritus" status provides a way for committers to take a leave of absence from OVS governance responsibilities. The following guidelines clarify the process around the emeritus status for committers: * A committer may choose to transition from active to emeritus, or from emeritus to active, by sending an email to the committers mailing list. * If a committer hasn't been heard from in 6 months, and does not respond to reasonable attempts to contact him or her, the other committers can vote as a majority to transition the committer from active to emeritus. (If the committer resurfaces, he or she can transition back to active by sending an email to the committers mailing list.) * Emeritus committers may stay on the committers mailing list to continue to follow any discussions there. * Emeritus committers do not nominate or vote in committer elections. From a governance perspective, they are equivalent to a non-committer. * Emeritus committers cannot merge patches to the OVS repository. * Emeritus committers will be listed in a separate section in the MAINTAINERS.rst file to continue to recognize their contributions to the project. Emeritus status does not replace the procedures for forcibly removing a committer. Note that just because a committer is not able to work on the project on a day-to-day basis, we feel they are still capable of providing input on the direction of the project. No committer should feel pressured to move themselves to this status. Again, it's just an option for those that do not currently have the time or interest. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/committer-grant-revocation.rst000066400000000000000000000332751514270232600316170ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ===================================== OVS Committer Grant/Revocation Policy ===================================== An OVS committer is a participant in the project with the ability to commit code directly to the main repository. Commit access grants a broad ability to affect the progress of the project as presented by its most important artifact, the code and related resources that produce working binaries of Open vSwitch. As such it represents a significant level of trust in an individual's commitment to working with other committers and the community at large for the benefit of the project. It can not be granted lightly and, in the worst case, must be revocable if the trust placed in an individual was inappropriate. This document suggests guidelines for granting and revoking commit access. It is intended to provide a framework for evaluation of such decisions without specifying deterministic rules that wouldn't be sensitive to the nuance of specific situations. In the end the decision to grant or revoke committer privileges is a judgment call made by the existing set of committers. Granting Commit Access ---------------------- Granting commit access should be considered when a candidate has demonstrated the following in their interaction with the project: - Contribution of significant new features through the patch submission process where: - Submissions are free of obvious critical defects - Submissions do not typically require many iterations of improvement to be accepted - Consistent participation in code review of other's patches, including existing committers, with comments consistent with the overall project standards - Assistance to those in the community who are less knowledgeable through active participation in project forums such as the ovs-discuss mailing list. - Plans for sustained contribution to the project compatible with the project's direction as viewed by current committers. - Commitment to meet the expectations described in the "Expectations of Developer's with Open vSwitch Access" The process to grant commit access to a candidate is simple: - An existing committer nominates the candidate by sending an email to all existing committers with information substantiating the contributions of the candidate in the areas described above. - All existing committers discuss the pros and cons of granting commit access to the candidate in the email thread. - When the discussion has converged or a reasonable time has elapsed without discussion developing (e.g. a few business days) the nominator calls for a final decision on the candidate with a followup email to the thread. - Each committer may vote yes, no, or abstain by replying to the email thread. A failure to reply is an implicit abstention. - After votes from all existing committers have been collected or a reasonable time has elapsed for them to be provided (e.g. a couple of business days) the votes are evaluated. To be granted commit access the candidate must receive yes votes from a majority of the existing committers and zero no votes. Since a no vote is effectively a veto of the candidate it should be accompanied by a reason for the vote. - The nominator summarizes the result of the vote in an email to all existing committers. - If the vote to grant commit access passed, the candidate is contacted with an invitation to become a committer to the project which asks them to agree to the committer expectations documented on the project web site. - If the candidate agrees access is granted by setting up commit access to the repos on github. Revoking Commit Access ---------------------- When a committer behaves in a manner that other committers view as detrimental to the future of the project, it raises a delicate situation with the potential for the creation of division within the greater community. These situations should be handled with care. The process in this case is: - Discuss the behavior of concern with the individual privately and explain why you believe it is detrimental to the project. Stick to the facts and keep the email professional. Avoid personal attacks and the temptation to hypothesize about unknowable information such as the other's motivations. Make it clear that you would prefer not to discuss the behavior more widely but will have to raise it with other contributors if it does not change. Ideally the behavior is eliminated and no further action is required. If not, - Start an email thread with all committers, including the source of the behavior, describing the behavior and the reason it is detrimental to the project. The message should have the same tone as the private discussion and should generally repeat the same points covered in that discussion. The person whose behavior is being questioned should not be surprised by anything presented in this discussion. Ideally the wider discussion provides more perspective to all participants and the issue is resolved. If not, - Start an email thread with all committers except the source of the detrimental behavior requesting a vote on revocation of commit rights. Cite the discussion among all committers and describe all the reasons why it was not resolved satisfactorily. This email should be carefully written with the knowledge that the reasoning it contains may be published to the larger community to justify the decision. - Each committer may vote yes, no, or abstain by replying to the email thread. A failure to reply is an implicit abstention. - After all votes have been collected or a reasonable time has elapsed for them to be provided (e.g. a couple of business days) the votes are evaluated. For the request to revoke commit access for the candidate to pass it must receive yes votes from two thirds of the existing committers. - anyone that votes no must provide their reasoning, and - if the proposal passes then counter-arguments for the reasoning in no votes should also be documented along with the initial reasons the revocation was proposed. Ideally there should be no new counter-arguments supplied in a no vote as all concerns should have surfaced in the discussion before the vote. - The original person to propose revocation summarizes the result of the vote in an email to all existing committers excepting the candidate for removal. - If the vote to revoke commit access passes, access is removed and the candidate for revocation is informed of that fact and the reasons for it as documented in the email requesting the revocation vote. - Ideally the revoked committer peacefully leaves the community and no further action is required. However, there is a distinct possibility that he/she will try to generate support for his/her point of view within the larger community. In this case the reasoning for removing commit access as described in the request for a vote will be published to the community. Changing the Policy ------------------- The process for changing the policy is: - Propose the changes to the policy in an email to all current committers and request discussion. - After an appropriate period of discussion (a few days) update the proposal based on feedback if required and resend it to all current committers with a request for a formal vote. - After all votes have been collected or a reasonable time has elapsed for them to be provided (e.g. a couple of business days) the votes are evaluated. For the request to modify the policy to pass it must receive yes votes from two thirds of the existing committers. Template Emails =============== Nomination to Grant Commit Access --------------------------------- I would like to nominate *[candidate]* for commit access. I believe *[he/she]* has met the conditions for commit access described in the committer grant policy on the project web site in the following ways: *[list of requirements & evidence]* Please reply to all in this message thread with your comments and questions. If that discussion concludes favorably I will request a formal vote on the nomination in a few days. Vote to Grant Commit Access --------------------------- I nominated *[candidate]* for commit access on *[date]*. Having allowed sufficient time for discussion it's now time to formally vote on the proposal. Please reply to all in this thread with your vote of: YES, NO, or ABSTAIN. A failure to reply will be counted as an abstention. If you vote NO, by our policy you must include the reasons for that vote in your reply. The deadline for votes is *[date and time]*. If a majority of committers vote YES and there are zero NO votes commit access will be granted. Vote Results for Grant of Commit Access --------------------------------------- The voting period for granting to commit access to *[candidate]* initiated at *[date and time]* is now closed with the following results: YES: *[count of yes votes]* (*[% of voters]*) NO: *[count of no votes]* (*[% of voters]*) ABSTAIN: *[count of abstentions]* (*[% of voters]*) Based on these results commit access *[is/is NOT]* granted. Invitation to Accepted Committer -------------------------------- Due to your sustained contributions to the Open vSwitch (OVS) project we would like to provide you with commit access to the project repository. Developers with commit access must agree to fulfill specific responsibilities described in the source repository: /Documentation/internals/committer-responsibilities.rst Please let us know if you would like to accept commit access and if so that you agree to fulfill these responsibilities. Once we receive your response we'll set up access. We're looking forward continuing to work together to advance the Open vSwitch project. Proposal to Revoke Commit Access for Detrimental Behavior --------------------------------------------------------- I regret that I feel compelled to propose revocation of commit access for *[candidate]*. I have privately discussed with *[him/her]* the following reasons I believe *[his/her]* actions are detrimental to the project and we have failed to come to a mutual understanding: *[List of reasons and supporting evidence]* Please reply to all in this thread with your thoughts on this proposal. I plan to formally propose a vote on the proposal on or after *[date and time]*. It is important to get all discussion points both for and against the proposal on the table during the discussion period prior to the vote. Please make it a high priority to respond to this proposal with your thoughts. Vote to Revoke Commit Access ---------------------------- I nominated *[candidate]* for revocation of commit access on *[date]*. Having allowed sufficient time for discussion it's now time to formally vote on the proposal. Please reply to all in this thread with your vote of: YES, NO, or ABSTAIN. A failure to reply will be counted as an abstention. If you vote NO, by our policy you must include the reasons for that vote in your reply. The deadline for votes is *[date and time]*. If 2/3rds of committers vote YES commit access will be revoked. The following reasons for revocation have been given in the original proposal or during discussion: *[list of reasons to remove access]* The following reasons for retaining access were discussed: *[list of reasons to retain access]* The counter-argument for each reason for retaining access is: *[list of counter-arguments for retaining access]* Vote Results for Revocation of Commit Access -------------------------------------------- The voting period for revoking the commit access of *[candidate]* initiated at *[date and time]* is now closed with the following results: - YES: *[count of yes votes]* (*[% of voters]*) - NO: *[count of no votes]* (*[% of voters]*) - ABSTAIN: *[count of abstentions]* (*[% of voters]*) Based on these results commit access *[is/is NOT]* revoked. The following reasons for retaining commit access were proposed in NO votes: *[list of reasons]* The counter-arguments for each of these reasons are: *[list of counter-arguments]* Notification of Commit Revocation for Detrimental Behavior ---------------------------------------------------------- After private discussion with you and careful consideration of the situation, the other committers to the Open vSwitch (OVS) project have concluded that it is in the best interest of the project that your commit access to the project repositories be revoked and this has now occurred. The reasons for this decision are: *[list of reasons for removing access]* While your goals and those of the project no longer appear to be aligned we greatly appreciate all the work you have done for the project and wish you continued success in your future work. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/committer-responsibilities.rst000066400000000000000000000125711514270232600317200ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================================================= Expectations for Developers with Open vSwitch Repo Access ========================================================= Pre-requisites -------------- Be familiar with the guidelines and standards defined in :doc:`contributing/index`. Review ------ Code (yours or others') must be reviewed publicly (by you or others) before you push it to the repository. With one exception (see below), every change needs at least one review. If one or more people know an area of code particularly well, code that affects that area should ordinarily get a review from one of them. The riskier, more subtle, or more complicated the change, the more careful the review required. When a change needs careful review, use good judgment regarding the quality of reviews. If a change adds 1000 lines of new code, and a review posted 5 minutes later says just "Looks good," then this is probably not a quality review. (The size of a change is correlated with the amount of care needed in review, but it is not strictly tied to it. A search and replace across many files may not need much review, but one-line optimization changes can have widespread implications.) Your own small changes to fix a recently broken build ("make") or tests ("make check"), that you believe to be visible to a large number of developers, may be checked in without review. If you are not sure, ask for review. If you do push a build fix without review, send the patch to ovs-dev afterward as usual, indicating in the email that you have already pushed it. Regularly review submitted code in areas where you have expertise. Consider reviewing other code as well. Git conventions --------------- Do not push merge commits to the Git repository without prior discussion on ovs-dev. If you apply a change (yours or another's) then it is your responsibility to handle any resulting problems, especially broken builds and other regressions. If it is someone else's change, then you can ask the original submitter to address it. Regardless, you need to ensure that the problem is fixed in a timely way. The definition of "timely" depends on the severity of the problem. If a bug is present on main and other branches, fix it on main first, then backport the fix to other branches. Straightforward backports do not require additional review (beyond that for the fix on main). Feature development should be done only on main. Occasionally it makes sense to add a feature to the most recent release branch, before the first actual release of that branch. These should be handled in the same way as bug fixes, that is, first implemented on main and then backported. Keep the authorship of a commit clear by maintaining a correct list of "Signed-off-by:"s. If a confusing situation comes up, as it occasionally does, bring it up on the mailing list. If you explain the use of "Signed-off-by:" to a new developer, explain not just how but why, since the intended meaning of "Signed-off-by:" is more important than the syntax. As part of your explanation, quote or provide a URL to the Developer's Certificate of Origin in :doc:`contributing/submitting-patches`. Use Reported-by: and Tested-by: tags in commit messages to indicate the source of a bug report. Keep the ``AUTHORS.rst`` file up to date. Pre-Push Hook ------------- The following script can be helpful because it provides an extra chance to check for mistakes while pushing to the main branch of OVS or OVN. If you would like to use it, install it as ``hooks/pre-push`` in your ``.git`` directory and make sure to mark it as executable with ``chmod +x``. For maximum utility, make sure ``checkpatch.py`` is in ``$PATH``: .. code-block:: bash #! /bin/bash remote=$1 case $remote in ovs|ovn|origin) ;; *) exit 0 ;; esac while read local_ref local_sha1 remote_ref remote_sha1; do case $remote_ref in refs/heads/main) n=0 while read sha do n=$(expr $n + 1) git log -1 $sha echo checkpatch.py -1 $sha done < /dev/null; then : else exit 1 fi ;; esac done exit 0 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/000077500000000000000000000000001514270232600262775ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/backporting-patches.rst000066400000000000000000000100011514270232600327510ustar00rootroot00000000000000.. Copyright (c) 2017 Nicira, Inc. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================== Backporting patches =================== .. note:: This is an advanced topic for developers and maintainers. Readers should familiarize themselves with building and running Open vSwitch, with the git tool, and with the Open vSwitch patch submission process. The backporting of patches from one git tree to another takes multiple forms within Open vSwitch, but is broadly applied in the following fashion: - Contributors submit their proposed changes to the latest development branch - Contributors and maintainers provide feedback on the patches - When the change is satisfactory, maintainers apply the patch to the development branch. - Maintainers backport changes from a development branch to release branches. With regards to Open vSwitch user space code and code that does not comprise the Linux datapath and compat code, the development branch is `main` in the Open vSwitch repository. Patches are applied first to this branch, then to the most recent `branch-X.Y`, then earlier `branch-X.Z`, and so on. The most common kind of patch in this category is a bugfix which affects main and other branches. Changes to userspace components ------------------------------- Patches which are fixing bugs should be considered for backporting from `main` to release branches. Open vSwitch contributors submit their patches targeted to the `main` branch, using the ``Fixes`` tag described in :doc:`submitting-patches`. The maintainer first applies the patch to `main`, then backports the patch to each older affected tree, as far back as it goes or at least to all currently supported branches. This is usually each branch back to the oldest maintained LTS release branch or the last 4 release branches if the oldest LTS is newer. If the fix only affects a particular branch and not `main`, contributors should submit the change with the target branch listed in the subject line of the patch. Contributors should list all versions that the bug affects. The ``git format-patch`` argument ``--subject-prefix`` may be used when posting the patch, for example: :: $ git format-patch HEAD --subject-prefix="PATCH branch-2.7" If a maintainer is backporting a change to older branches and the backport is not a trivial cherry-pick, then the maintainer may opt to submit the backport for the older branch on the mailing list for further review. This should be done in the same manner as described above. Changes to Linux kernel components ---------------------------------- Changes to the Linux kernel components in Open vSwitch go through review in the upstream Linux Netdev community. The `Netdev Maintainer Handbook`_ describes the general process for merging patches to the upstream Linux kernel networking subsystem. Backports to older kernel versions are handled via the `Stable tree`_ mechanism. Backports for Linux datapath code are no longer accepted into the Open vSwitch tree as that code is not present in the Open vSwitch distribution since Open vSwitch 3.0. .. _Netdev Maintainer Handbook: https://docs.kernel.org/process/maintainer-netdev.html .. _Stable tree: https://docs.kernel.org/process/maintainer-netdev.html#stable-tree openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/coding-style-windows.rst000066400000000000000000000153641514270232600331330ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============================= Windows Datapath Coding Style ============================= The :doc:`coding style ` guide gives the flexibility for each platform to use its own coding style for the kernel datapath. This file describes the specific coding style used in most of the C files in the Windows kernel datapath of the Open vSwitch distribution. Most of the coding conventions applicable for the Open vSwitch distribution are applicable to the Windows kernel datapath as well. There are some exceptions and new guidelines owing to the commonly followed practices in Windows kernel/driver code. They are noted as follows: Basics ------ - Limit lines to 79 characters. Many times, this is not possible due to long names of functions and it is fine to go beyond the characters limit. One common example is when calling into NDIS functions. Types ----- Use data types defined by Windows for most of the code. This is a common practice in Windows driver code, and it makes integrating with the data structures and functions defined by Windows easier. Example: ``DWORD`` and ``BOOLEAN``. Use caution in portions of the code that interface with the OVS userspace. OVS userspace does not use Windows specific data types, and when copying data back and forth between kernel and userspace, care should be exercised. Naming ------ It is common practice to use camel casing for naming variables, functions and files in Windows. For types, especially structures, unions and enums, using all upper case letters with words separated by '_' is common. These practices can be used for OVS Windows datapath. However, use the following guidelines: - Use lower case to begin the name of a variable. - Do not use '_' to begin the name of the variable. '_' is to be used to begin the parameters of a pre-processor macro. - Use upper case to begin the name of a function, enum, file name etc. - Static functions whose scope is limited to the file they are defined in can be prefixed with '_'. This is not mandatory though. - For types, use all upper case for all letters with words separated by '_'. If camel casing is preferred, use upper case for the first letter. - It is a common practice to define a pointer type by prefixing the letter 'P' to a data type. The same practice can be followed here as well. For example:: static __inline BOOLEAN OvsDetectTunnelRxPkt(POVS_FORWARDING_CONTEXT ovsFwdCtx, POVS_FLOW_KEY flowKey) { POVS_VPORT_ENTRY tunnelVport = NULL; if (!flowKey->ipKey.nwFrag && flowKey->ipKey.nwProto == IPPROTO_UDP && flowKey->ipKey.l4.tpDst == VXLAN_UDP_PORT_NBO) { tunnelVport = OvsGetTunnelVport(OVSWIN_VPORT_TYPE_VXLAN); ovsActionStats.rxVxlan++; } else { return FALSE; } if (tunnelVport) { ASSERT(ovsFwdCtx->tunnelRxNic == NULL); ovsFwdCtx->tunnelRxNic = tunnelVport; return TRUE; } return FALSE; } For declaring variables of pointer type, use of the pointer data type prefixed with 'P' is preferred over using '*'. This is not mandatory though, and is only prescribed since it is a common practice in Windows. Example, #1 is preferred over #2 though #2 is also equally correct: 1. ``PNET_BUFFER_LIST curNbl;`` 2. ``NET_BUFFER_LIST *curNbl;`` Comments -------- Comments should be written as full sentences that start with a capital letter and end with a period. Putting two spaces between sentences is not necessary. ``//`` can be used for comments as long as the comment is a single line comment. For block comments, use ``/* */`` comments Functions --------- Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. Before each function definition, write a comment that describes the function's purpose, including each parameter, the return value, and side effects. References to argument names should be given in single-quotes, e.g. 'arg'. The comment should not include the function name, nor need it follow any formal structure. The comment does not need to describe how a function does its work, unless this information is needed to use the function correctly (this is often better done with comments *inside* the function). Mention any side effects that the function has that are not obvious based on the name of the function or based on the workflow it is called from. In the interest of keeping comments describing functions similar in structure, use the following template. :: /* *---------------------------------------------------------------------------- * Any description of the function, arguments, return types, assumptions and * side effects. *---------------------------------------------------------------------------- */ Source Files ------------ Each source file should state its license in a comment at the very top, followed by a comment explaining the purpose of the code that is in that file. The comment should explain how the code in the file relates to code in other files. The goal is to allow a programmer to quickly figure out where a given module fits into the larger system. The first non-comment line in a .c source file should be:: #include ``#include`` directives should appear in the following order: 1. ``#include `` 2. The module's own headers, if any. Including this before any other header (besides ````) ensures that the module's header file is self-contained (see *Header Files*) below. 3. Standard C library headers and other system headers, preferably in alphabetical order. (Occasionally one encounters a set of system headers that must be included in a particular order, in which case that order must take precedence.) 4. Open vSwitch headers, in alphabetical order. Use ``""``, not ``<>``, to specify Open vSwitch header names. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/coding-style.rst000066400000000000000000000525661514270232600314500ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============ Coding Style ============ This file describes the coding style used in most C files in the Open vSwitch distribution. However, Linux kernel code datapath directory follows the Linux kernel's established coding conventions. For the Windows kernel datapath code, use the coding style described in :doc:`coding-style-windows`. The following GNU indent options approximate this style. :: -npro -bad -bap -bbb -br -blf -brs -cdw -ce -fca -cli0 -npcs -i4 -l79 \ -lc79 -nbfda -nut -saf -sai -saw -sbi4 -sc -sob -st -ncdb -pi4 -cs -bs \ -di1 -lp -il0 -hnl .. _basics: Basics ------ - Limit lines to 79 characters. - Use form feeds (control+L) to divide long source files into logical pieces. A form feed should appear as the only character on a line. - Do not use tabs for indentation. - Avoid trailing spaces on lines. .. _naming: Naming ------ - Use names that explain the purpose of a function or object. - Use underscores to separate words in an identifier: ``multi_word_name``. - Use lowercase for most names. Use uppercase for macros, macro parameters, and members of enumerations. - Give arrays names that are plural. - Pick a unique name prefix (ending with an underscore) for each module, and apply that prefix to all of that module's externally visible names. Names of macro parameters, struct and union members, and parameters in function prototypes are not considered externally visible for this purpose. - Do not use names that begin with ``_``. If you need a name for "internal use only", use ``__`` as a suffix instead of a prefix. - Avoid negative names: ``found`` is a better name than ``not_found``. - In names, a ``size`` is a count of bytes, a ``length`` is a count of characters. A buffer has size, but a string has length. The length of a string does not include the null terminator, but the size of the buffer that contains the string does. .. _comments: Comments -------- Comments should be written as full sentences that start with a capital letter and end with a period. Put two spaces between sentences. Write block comments as shown below. You may put the ``/*`` and ``*/`` on the same line as comment text if you prefer. :: /* * We redirect stderr to /dev/null because we often want to remove all * traffic control configuration on a port so its in a known state. If * this done when there is no such configuration, tc complains, so we just * always ignore it. */ Each function and each variable declared outside a function, and each struct, union, and typedef declaration should be preceded by a comment. See functions_ below for function comment guidelines. Each struct and union member should each have an inline comment that explains its meaning. structs and unions with many members should be additionally divided into logical groups of members by block comments, e.g.: :: /* An event that will wake the following call to poll_block(). */ struct poll_waiter { /* Set when the waiter is created. */ struct ovs_list node; /* Element in global waiters list. */ int fd; /* File descriptor. */ short int events; /* Events to wait for (POLLIN, POLLOUT). */ poll_fd_func *function; /* Callback function, if any, or null. */ void *aux; /* Argument to callback function. */ struct backtrace *backtrace; /* Event that created waiter, or null. */ /* Set only when poll_block() is called. */ struct pollfd *pollfd; /* Pointer to element of the pollfds array (null if added from a callback). */ }; Use ``XXX`` or ``FIXME`` comments to mark code that needs work. Don't use ``//`` comments. Don't comment out or ``#if 0`` out code. Just remove it. The code that was there will still be in version control history. .. _functions: Functions --------- Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. Before each function definition, write a comment that describes the function's purpose, including each parameter, the return value, and side effects. References to argument names should be given in single-quotes, e.g. ``'arg'``. The comment should not include the function name, nor need it follow any formal structure. The comment does not need to describe how a function does its work, unless this information is needed to use the function correctly (this is often better done with comments *inside* the function). Simple static functions do not need a comment. Within a file, non-static functions should come first, in the order that they are declared in the header file, followed by static functions. Static functions should be in one or more separate pages (separated by form feed characters) in logical groups. A commonly useful way to divide groups is by "level", with high-level functions first, followed by groups of progressively lower-level functions. This makes it easy for the program's reader to see the top-down structure by reading from top to bottom. All function declarations and definitions should include a prototype. Empty parentheses, e.g. ``int foo();``, do not include a prototype (they state that the function's parameters are unknown); write ``void`` in parentheses instead, e.g. ``int foo(void);``. Prototypes for static functions should either all go at the top of the file, separated into groups by blank lines, or they should appear at the top of each page of functions. Don't comment individual prototypes, but a comment on each group of prototypes is often appropriate. In the absence of good reasons for another order, the following parameter order is preferred. One notable exception is that data parameters and their corresponding size parameters should be paired. 1. The primary object being manipulated, if any (equivalent to the ``this`` pointer in C++). 2. Input-only parameters. 3. Input/output parameters. 4. Output-only parameters. 5. Status parameter. Example: :: /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value * is a bitmap of "enum ofp_port_features" bits, in host byte order. * Returns 0 if successful, otherwise a positive errno value. On failure, * all of the passed-in values are set to 0. */ int netdev_get_features(struct netdev *netdev, uint32_t *current, uint32_t *advertised, uint32_t *supported, uint32_t *peer) { ... } Functions that destroy an instance of a dynamically-allocated type should accept and ignore a null pointer argument. Code that calls such a function (including the C standard library function ``free()``) should omit a null-pointer check. We find that this usually makes code easier to read. Functions in ``.c`` files should not normally be marked ``inline``, because it does not usually help code generation and it does suppress compiler warnings about unused functions. (Functions defined in ``.h`` usually should be marked ``inline``.) .. _function prototypes: Function Prototypes ------------------- Put the return type and function name on the same line in a function prototype: :: static const struct option_class *get_option_class(int code); Omit parameter names from function prototypes when the names do not give useful information, e.g.: :: int netdev_get_mtu(const struct netdev *, int *mtup); Statements ---------- Indent each level of code with 4 spaces. Use BSD-style brace placement: :: if (a()) { b(); d(); } Put a space between ``if``, ``while``, ``for``, etc. and the expressions that follow them. Enclose single statements in braces: :: if (a > b) { return a; } else { return b; } Use comments and blank lines to divide long functions into logical groups of statements. Avoid assignments inside ``if`` and ``while`` conditions. Do not put gratuitous parentheses around the expression in a return statement, that is, write ``return 0;`` and not ``return(0);`` Write only one statement per line. Indent ``switch`` statements like this: :: switch (conn->state) { case S_RECV: error = run_connection_input(conn); break; case S_PROCESS: error = 0; break; case S_SEND: error = run_connection_output(conn); break; default: OVS_NOT_REACHED(); } ``switch`` statements with very short, uniform cases may use an abbreviated style: :: switch (code) { case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; case 204: return "No Content"; default: return "Unknown"; } Use ``for (;;)`` to write an infinite loop. In an ``if/else`` construct where one branch is the "normal" or "common" case and the other branch is the "uncommon" or "error" case, put the common case after the ``if``, not the ``else``. This is a form of documentation. It also places the most important code in sequential order without forcing the reader to visually skip past less important details. (Some compilers also assume that the ``if`` branch is the more common case, so this can be a real form of optimization as well.) Return Values ------------- For functions that return a success or failure indication, prefer one of the following return value conventions: - An ``int`` where ``0`` indicates success and a positive errno value indicates a reason for failure. - A ``bool`` where ``true`` indicates success and ``false`` indicates failure. Macros ------ Don't define an object-like macro if an enum can be used instead. Don't define a function-like macro if a ``static inline`` function can be used instead. If a macro's definition contains multiple statements, enclose them with ``do { ... } while (0)`` to allow them to work properly in all syntactic circumstances. Do use macros to eliminate the need to update different parts of a single file in parallel, e.g. a list of enums and an array that gives the name of each enum. For example: :: /* Logging importance levels. */ #define VLOG_LEVELS \ VLOG_LEVEL(EMER, LOG_ALERT) \ VLOG_LEVEL(ERR, LOG_ERR) \ VLOG_LEVEL(WARN, LOG_WARNING) \ VLOG_LEVEL(INFO, LOG_NOTICE) \ VLOG_LEVEL(DBG, LOG_DEBUG) enum vlog_level { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) VLL_##NAME, VLOG_LEVELS #undef VLOG_LEVEL VLL_N_LEVELS }; /* Name for each logging level. */ static const char *level_names[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME, VLOG_LEVELS #undef VLOG_LEVEL }; Thread Safety Annotations ------------------------- Use the macros in ``lib/compiler.h`` to annotate locking requirements. For example: :: static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER; void function_require_plain_mutex(void) OVS_REQUIRES(mutex); void function_require_rwlock(void) OVS_REQ_RDLOCK(rwlock); Pass lock objects, not their addresses, to the annotation macros. (Thus we have ``OVS_REQUIRES(mutex)`` above, not ``OVS_REQUIRES(&mutex)``.) .. _source files: Source Files ------------ Each source file should state its license in a comment at the very top, followed by a comment explaining the purpose of the code that is in that file. The comment should explain how the code in the file relates to code in other files. The goal is to allow a programmer to quickly figure out where a given module fits into the larger system. The first non-comment line in a ``.c`` source file should be: :: #include ``#include`` directives should appear in the following order: 1. ``#include `` 2. The module's own headers, if any. Including this before any other header (besides ) ensures that the module's header file is self-contained (see `header files`_ below). 3. Standard C library headers and other system headers, preferably in alphabetical order. (Occasionally one encounters a set of system headers that must be included in a particular order, in which case that order must take precedence.) 4. Open vSwitch headers, in alphabetical order. Use ``""``, not ``<>``, to specify Open vSwitch header names. .. _header files: Header Files ------------ Each header file should start with its license, as described under `source files`_ above, followed by a "header guard" to make the header file idempotent, like so: :: #ifndef NETDEV_H #define NETDEV_H 1 ... #endif /* netdev.h */ Header files should be self-contained; that is, they should ``#include`` whatever additional headers are required, without requiring the client to ``#include`` them for it. Don't define the members of a struct or union in a header file, unless client code is actually intended to access them directly or if the definition is otherwise actually needed (e.g. inline functions defined in the header need them). Similarly, don't ``#include`` a header file just for the declaration of a struct or union tag (e.g. just for ``struct ;``). Just declare the tag yourself. This reduces the number of header file dependencies. Types ----- Use typedefs sparingly. Code is clearer if the actual type is visible at the point of declaration. Do not, in general, declare a typedef for a ``struct``, ``union``, or ``enum``. Do not declare a typedef for a pointer type, because this can be very confusing to the reader. A function type is a good use for a typedef because it can clarify code. The type should be a function type, not a pointer-to-function type. That way, the typedef name can be used to declare function prototypes. (It cannot be used for function definitions, because that is explicitly prohibited by C89 and C99.) You may assume that ``char`` is exactly 8 bits and that ``int`` and ``long`` are at least 32 bits. Don't assume that ``long`` is big enough to hold a pointer. If you need to cast a pointer to an integer, use ``intptr_t`` or ``uintptr_t`` from . Use the ``int_t`` and ``uint_t`` types from for exact-width integer types. Use the ``PRId``, ``PRIu``, and ``PRIx`` macros from for formatting them with ``printf()`` and related functions. For compatibility with antique ``printf()`` implementations: - Instead of ``"%zu"``, use ``"%"PRIuSIZE``. - Instead of ``"%td"``, use ``"%"PRIdPTR``. - Instead of ``"%ju"``, use ``"%"PRIuMAX``. Other variants exist for different radixes. For example, use ``"%"PRIxSIZE`` instead of ``"%zx"`` or ``"%x"`` instead of ``"%hhx"``. Also, instead of ``"%hhd"``, use ``"%d"``. Be cautious substituting ``"%u"``, ``"%x"``, and ``"%o"`` for the corresponding versions with ``"hh"``: cast the argument to unsigned char if necessary, because ``printf("%hhu", -1)`` prints ``255`` but ``printf("%u", -1)`` prints ``4294967295``. Use bit-fields sparingly. Do not use bit-fields for layout of network protocol fields or in other circumstances where the exact format is important. Declare bit-fields to be signed or unsigned integer types or ``_Bool`` (aka ``bool``). Do *not* declare bit-fields of type ``int``: C99 allows these to be either signed or unsigned according to the compiler's whim. (A 1-bit bit-field of type ``int`` may have a range of ``-1...0``!) Try to order structure members such that they pack well on a system with 2-byte ``short``, 4-byte ``int``, and 4- or 8-byte ``long`` and pointer types. Prefer clear organization over size optimization unless you are convinced there is a size or speed benefit. Pointer declarators bind to the variable name, not the type name. Write ``int *x``, not ``int* x`` and definitely not ``int * x``. Expressions ----------- Put one space on each side of infix binary and ternary operators: :: * / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= Avoid comma operators. Do not put any white space around postfix, prefix, or grouping operators: :: () [] -> . ! ~ ++ -- + - * & Exception 1: Put a space after (but not before) the "sizeof" keyword. Exception 2: Put a space between the ``()`` used in a cast and the expression whose type is cast: ``(void *) 0``. Break long lines before the ternary operators ``?`` and ``:``, rather than after them, e.g. :: return (out_port != VIGP_CONTROL_PATH ? alpheus_output_port(dp, skb, out_port) : alpheus_output_control(dp, skb, fwd_save_skb(skb), VIGR_ACTION)); Parenthesize the operands of ``&&`` and ``||`` if operator precedence makes it necessary, or if the operands are themselves expressions that use ``&&`` and ``||``, but not otherwise. Thus:: if (rule && (!best || rule->priority > best->priority)) { best = rule; } but:: if (!isdigit((unsigned char)s[0]) || !isdigit((unsigned char)s[1]) || !isdigit((unsigned char)s[2])) { printf("string %s does not start with 3-digit code\n", s); } Do parenthesize a subexpression that must be split across more than one line, e.g.:: *idxp = ((l1_idx << PORT_ARRAY_L1_SHIFT) | (l2_idx << PORT_ARRAY_L2_SHIFT) | (l3_idx << PORT_ARRAY_L3_SHIFT)); Breaking a long line after a binary operator gives its operands a more consistent look, since each operand has the same horizontal position. This makes the end-of-line position a good choice when the operands naturally resemble each other, as in the previous two examples. On the other hand, breaking before a binary operator better draws the eye to the operator, which can help clarify code by making it more obvious what's happening, such as in the following example:: if (!ctx.freezing && xbridge->has_in_band && in_band_must_output_to_local_port(flow) && !actions_output_to_local_port(&ctx)) { Thus, decide whether to break before or after a binary operator separately in each situation, based on which of these factors appear to be more important. Try to avoid casts. Don't cast the return value of malloc(). The ``sizeof`` operator is unique among C operators in that it accepts two very different kinds of operands: an expression or a type. In general, prefer to specify an expression, e.g. ``int *x = xmalloc(sizeof *x);``. When the operand of ``sizeof`` is an expression, there is no need to parenthesize that operand, and please don't. Use the ``ARRAY_SIZE`` macro from ``lib/util.h`` to calculate the number of elements in an array. When using a relational operator like ``<`` or ``==``, put an expression or variable argument on the left and a constant argument on the right, e.g. ``x == 0``, *not* ``0 == x``. Blank Lines ----------- Put one blank line between top-level definitions of functions and global variables. C DIALECT --------- Most C99 features are OK because they are widely implemented: - Flexible array members (e.g. ``struct { int foo[]; }``). - ``static inline`` functions (but no other forms of ``inline``, for which GCC and C99 have differing interpretations). - ``long long`` - ``bool`` and ````, but don't assume that ``bool`` or ``_Bool`` can only take on the values ``0`` or ``1``, because this behavior can't be simulated on C89 compilers. Also, don't assume that a conversion to ``bool`` or ``_Bool`` follows C99 semantics, i.e. use ``(bool) (some_value != 0)`` rather than ``(bool) some_value``. The latter might produce unexpected results on non-C99 environments. For example, if ``bool`` is implemented as a typedef of char and ``some_value = 0x10000000``. - Designated initializers (e.g. ``struct foo foo = { .a = 1 };`` and ``int a[] = { [2] = 5 };``). - Mixing of declarations and code within a block. Favor positioning that allows variables to be initialized at their point of declaration. - Use of declarations in iteration statements (e.g. ``for (int i = 0; i < 10; i++)``). - Use of a trailing comma in an enum declaration (e.g. ``enum { x = 1, };``). As a matter of style, avoid ``//`` comments. Avoid using GCC or Clang extensions unless you also add a fallback for other compilers. You can, however, use C99 features or GCC extensions also supported by Clang in code that compiles only on GNU/Linux (such as ``lib/netdev-linux.c``), because GCC is the system compiler there. Python ------ When introducing new Python code, try to follow Python's `PEP 8 `__ style. Consider running the ``pep8`` or ``flake8`` tool against your code to find issues. Libraries --------- When introducing a new library, follow :doc:`Open vSwitch Library ABI guide ` openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/documentation-style.rst000066400000000000000000000261011514270232600330400ustar00rootroot00000000000000.. Copyright (c) 2016 Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================== Documentation Style =================== This file describes the documentation style used in all documentation found in Open vSwitch. Documentation includes any documents found in ``Documentation`` along with any ``README``, ``MAINTAINERS``, or generally ``rst`` suffixed documents found in the project tree. .. note:: This guide only applies to documentation for Open vSwitch v2.7. or greater. Previous versions of Open vSwitch used a combination of Markdown and raw plain text, and guidelines for these are not detailed here. reStructuredText vs. Sphinx --------------------------- `reStructuredText (rST)`__ is the syntax, while `Sphinx`__ is a documentation generator. Sphinx introduces a number of extensions to rST, like the ``:ref:`` role, which can and should be used in documentation, but these will not work correctly on GitHub. As such, these extensions should not be used in any documentation in the root level, such as the ``README``. __ http://docutils.sourceforge.net/rst.html __ http://www.sphinx-doc.org/ rST Conventions --------------- Basics ~~~~~~ Many of the basic documentation guidelines match those of the :doc:`coding-style`. - Use reStructuredText (rST) for all documentation. Sphinx extensions can be used, but only for documentation in the ``Documentation`` folder. - Limit lines at 79 characters. .. note:: An exception to this rule is text within code-block elements that cannot be wrapped and links within references. - Use spaces for indentation. - Match indentation levels. A change in indentation level usually signifies a change in content nesting, by either closing the existing level or introducing a new level. - Avoid trailing spaces on lines. - Include a license (see this file) in all docs. - Most importantly, always build and display documentation before submitting changes! Docs aren't unit testable, so visible inspection is necessary. File Names ~~~~~~~~~~ - Use hyphens as space delimiters. For example: ``my-readme-document.rst`` .. note:: An exception to this rule is any man pages, which take an trailing number corresponding to the number of arguments required. This number is preceded by an underscore. - Use lowercase filenames. .. note:: An exception to this rule is any documents found in the root-level of the project. Titles ~~~~~~ - Use the following headers levels. | ``=======`` Heading 0 (reserved for the title in a document) | ``-------`` Heading 1 | ``~~~~~~~`` Heading 2 | ``+++++++`` Heading 3 | ``'''''''`` Heading 4 .. note:: Avoid using lower heading levels by rewriting and reorganizing the information. - Under- and overlines should be of the same length as that of the heading text. - Use "title case" for headers. Code ~~~~ - Use ``::`` to prefix code. - Don't use syntax highlighting such as ``.. highlight:: `` or ``code-block:: `` because it depends on external ``pygments`` library. - Prefix commands with ``$``. - Where possible, include fully-working snippets of code. If there pre-requisites, explain what they are and how to achieve them. Admonitions ~~~~~~~~~~~ - Use admonitions to call attention to important information.:: .. note:: This is a sample callout for some useful tip or trick. Example admonitions include: ``warning``, ``important``, ``note``, ``tip`` or ``seealso``. - Use notes sparingly. Avoid having more than one per subsection. Tables ~~~~~~ - Use either graphic tables, list tables or CSV tables. Graphic tables ++++++++++++++ :: .. table:: OVS-Linux kernel compatibility ============ ============== Open vSwitch Linux kernel ============ ============== 1.4.x 2.6.18 to 3.2 1.5.x 2.6.18 to 3.2 1.6.x 2.6.18 to 3.2 ============ ============== :: .. table:: OVS-Linux kernel compatibility +--------------+---------------+ | Open vSwitch | Linux kernel | +==============+===============+ | 1.4.x | 2.6.18 to 3.2 | +--------------+---------------+ | 1.5.x | 2.6.18 to 3.2 | +--------------+---------------+ | 1.6.x | 2.6.18 to 3.2 | +--------------+---------------+ .. note:: The ``table`` role - ``.. table:: `` - can be safely omitted. List tables +++++++++++ :: .. list-table:: OVS-Linux kernel compatibility :widths: 10 15 :header-rows: 1 * - Open vSwitch - Linux kernel * - 1.4.x - 2.6.18 to 3.2 * - 1.5.x - 2.6.18 to 3.2 * - 1.6.x - 2.6.18 to 3.2 CSV tables ++++++++++ :: .. csv-table:: OVS-Linux kernel compatibility :header: Open vSwitch, Linux kernel :widths: 10 15 1.4.x, 2.6.18 to 3.2 1.5.x, 2.6.18 to 3.2 1.6.x, 2.6.18 to 3.2 Cross-referencing ~~~~~~~~~~~~~~~~~ - To link to an external file or document, include as a link.:: Here's a `link `__ to the Open vSwitch website. Here's a `link`_ in reference style. .. _link: http://openvswitch.org - You can also use citations.:: Refer to the Open vSwitch documentation [1]_. References ---------- .. [1]: http://openvswitch.org - To cross-reference another doc, use the ``doc`` role.:: Here is a link to the :doc:`/README.rst` .. note:: This is a Sphinx extension. Do not use this in any top-level documents. - To cross-reference an arbitrary location in a doc, use the ``ref`` role.:: .. _sample-crossref Title ~~~~~ Hello, world. Another Title ~~~~~~~~~~~~~ Here is a cross-reference to :ref:`sample-crossref`. .. note:: This is a Sphinx extension. Do not use this in any top-level documents. Figures and Other Media ~~~~~~~~~~~~~~~~~~~~~~~ - All images should be in PNG format and compressed where possible. For PNG files, use OptiPNG and AdvanceCOMP's ``advpng``: :: $ optipng -o7 -zm1-9 -i0 -strip all $ advpng -z4 - Any ASCII text "images" should be included in code-blocks to preserve formatting - Include other reStructuredText verbatim in a current document Comments ~~~~~~~~ - Comments are indicated by means of the ``..`` marker.:: .. TODO(stephenfin) This section needs some work. This TODO will not appear in the final generated document, however. Man Pages --------- In addition to the above, man pages have some specific requirements: - You **must** define the following sections: - Synopsis - Description - Options Note that `NAME` is not included - this is automatically generated by Sphinx and should not be manually defined. Also note that these do not need to be uppercase - Sphinx will do this automatically. Additional sections are allowed. Refer to `man-pages(8)` for information on the sections generally allowed. - You **must not** define a `NAME` section. See above. - The `OPTIONS` section must describe arguments and options using the `program`__ and `option`__ directives. This ensures the output is formatted correctly and that you can cross-reference various programs and commands from the documentation. For example:: .. program:: ovs-do-something .. option:: -f, --force Force the operation .. option:: -b , --bridge Name or ID of bridge .. important:: Option argument names should be enclosed in angle brackets, as above. - Any references to the application or any other Open vSwitch application must be marked up using the `program` role. This allows for easy linking in the HTML output and correct formatting in the man page output. For example:: To do something, run :program:`ovs-do-something`. - The man page must be included in the list of man page documents found in `conf.py`__ Refer to existing man pages, such as :doc:`/ref/ovs-vlan-test.8` for a worked example. __ http://www.sphinx-doc.org/en/stable/domains.html#directive-program __ http://www.sphinx-doc.org/en/stable/domains.html#directive-option __ http://www.sphinx-doc.org/en/stable/config.html#confval-man_pages Writing Style ------------- Follow these guidelines to ensure readability and consistency of the Open vSwitch documentation. These guidelines are based on the `/*IBM Style Guide/* `__. - Use standard US English Use a spelling and grammar checking tool as necessary. - Expand initialisms and acronyms on first usage. Commonly used terms like CPU or RAM are allowed. .. list-table:: :header-rows: 1 * - Do not use - Do use * - OVS is a virtual switch. OVS has... - Open vSwitch (OVS) is a virtual switch. OVS has... * - The VTEP emulator is... - The Virtual Tunnel Endpoint (VTEP) emulator is... - Write in the active voice The subject should do the verb's action, rather than be acted upon. .. list-table:: :header-rows: 1 * - Do not use - Do use * - A bridge is created by you - Create a bridge - Write in the present tense .. list-table:: :header-rows: 1 * - Do not use - Do use * - Once the bridge is created, you can create a port - Once the bridge is created, create a port - Write in second person .. list-table:: :header-rows: 1 * - Do not use - Do use * - To create a bridge, the user runs: - To create a bridge, run: - Keep sentences short and concise - Eliminate needless politeness Avoid "please" and "thank you" Helpful Tools ------------- There are a number of tools, online and offline, which can be used to preview documents are you edit them: - `ReText `__ A simple but powerful editor for Markdown and reStructuredText. ReText is written in Python. - `restview `__ A viewer for ReStructuredText documents that renders them on the fly. Useful Links ------------ - `Quick reStructuredText `__ - `Sphinx Documentation `__ openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/inclusive-language.rst000066400000000000000000000043261514270232600326200ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================== Inclusive Language ================== In order to help facilitate an inclusive environment in the Open vSwitch community we recognise the role of language in framing our communication with each other. It is important that terms that may exclude people through racial, cultural or other bias, are avoided as they may make people feel excluded. We recognise that this is subjective, and to some extent is a journey. But we also recognise that we cannot begin that journey without taking positive action. To this end Open vSwitch is adopting the practice of an inclusive word list, which helps to guide the use of language within the project. .. _word list: Word List --------- The intent of this document is to formally document the acceptance of a inclusive word list by Open vSwitch. Accordingly, this document specifies use of the use the `Inclusive Naming Word List `__ v1.0 (the word list) for Open vSwitch. The adoption of the word list intended that this act as a guide for developers creating patches to the Open vSwitch repository, including both source code and documentation. And to aid maintainers in their role of shepherding changes into the repository. Further steps to align usage of language in Open vSwitch, including clarification of application of the word list, to new and existing work, may follow. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/index.rst000066400000000000000000000023071514270232600301420ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============================ Contributing to Open vSwitch ============================ The below guides provide information on contributing to Open vSwitch itself. .. toctree:: :maxdepth: 2 submitting-patches backporting-patches coding-style coding-style-windows documentation-style inclusive-language libopenvswitch-abi openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/libopenvswitch-abi.rst000066400000000000000000000120171514270232600326230ustar00rootroot00000000000000.. Copyright (c) 2017 Red Hat, Inc. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================================== Open vSwitch Library ABI Updates =================================== This file describes the manner in which the Open vSwitch shared library manages different ABI and API revisions. This document aims to describe the background, goals, and concrete mechanisms used to export code-space functionality so that it may be shared between multiple applications. .. _definitions: Definitions ----------- .. csv-table:: Definitions for terms appearing in this document :header: "Term", "Definition" "ABI", "Abbreviation of Application Binary Interface" "API", "Abbreviation of Application Programming Interface" "Application Binary Interface", "The low-level runtime interface exposed by an object file." "Application Programming Interface", "The source-code interface descriptions intended for use in multiple translation units when compiling." "Code library", "A collection of function implementations and definitions intended to be exported and called through a well-defined interface." "Shared Library", "A code library which is imported at run time." .. _overview: Overview ---------- C and C++ applications often use 'external' functionality, such as printing specialized data types or parsing messages, which has been exported for common use. There are many possible ways for applications to call such external functionality, for instance by including an appropriate inline definition which the compiler can emit as code in each function it appears. One such way of exporting and importing such functionality is through the use of a library of code. When a compiler builds object code from source files to produce object code, the results are binary data arranged with specific calling conventions, alignments, and order suitable for a run-time environment or linker. This result defines a specific ABI. As library of code develops and its exported interfaces change over time, the resulting ABI may change as well. Therefore, care must be taken to ensure the changes made to libraries of code are effectively communicated to applications which use them. This includes informing the applications when incompatible changes are made. The Open vSwitch project exports much of its functionality through multiple such libraries of code. These libraries are intended for multiple applications to import and use. As the Open vSwitch project continues to evolve and change, its exported code will evolve as well. To ensure that applications linking to these libraries are aware of these changes, Open vSwitch employs libtool version stamps. .. _policies: ABI Policy ---------- Open vSwitch will export the ABI version at the time of release, such that the library name will be the major.minor version, and the rest of the release version information will be conveyed with a libtool interface version. The intent is for Open vSwitch to maintain an ABI stability for each minor revision only (so that Open vSwitch release 2.5 carries a guarantee for all 2.5.ZZ micro-releases). This means that any porting effort to stable branches must take not to disrupt the existing ABI. In the event that a bug must be fixed in a backwards-incompatible way, developers must bump the libtool 'current' version to inform the linker of the ABI breakage. This will signal that libraries exposed by the subsequent release will not maintain ABI stability with the previous version. Coding ------- At build time, if building shared libraries by passing the `--enable-shared` arguments to `./configure`, version information is extracted from the ``$PACKAGE_VERSION`` automake variable and formatted into the appropriate arguments. These get exported for use in Makefiles as ``$OVS_LTINFO``, and passed to each exported library along with other ``LDFLAGS``. Therefore, when adding a new library to the build system, these version flags should be included with the ``$LDFLAGS`` variable. Nothing else needs to be done. Changing an exported function definition (from a file in, for instance `lib/*.h`) is only permitted from minor release to minor release. Likewise changes to library data structures should only occur from minor release to minor release. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/contributing/submitting-patches.rst000066400000000000000000000451511514270232600326510ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================== Submitting Patches ================== Send changes to Open vSwitch as patches to dev@openvswitch.org. One patch per email. More details are included below. If you are using Git, then `git format-patch` takes care of most of the mechanics described below for you. Before You Start ---------------- Before you send patches at all, make sure that each patch makes sense. In particular: - A given patch should not break anything, even if later patches fix the problems that it causes. The source tree should still build and work after each patch is applied. (This enables `git bisect` to work best.) - A patch should make one logical change. Don't make multiple, logically unconnected changes to disparate subsystems in a single patch. - A patch that adds or removes user-visible features should also update the appropriate user documentation or manpages. Consider adding an item to NEWS for nontrivial changes. Check "Feature Deprecation Guidelines" section in this document if you intend to remove user-visible feature. Testing is also important: - Test a patch that modifies existing code with ``make check`` before submission. Refer to the "Unit Tests" in :doc:`/topics/testing`, for more information. We also encourage running the kernel and userspace system tests. - Consider testing a patch that adds or deletes files with ``make distcheck`` before submission. - A patch that modifies Linux kernel code should be at least build-tested on various Linux kernel versions before submission. I suggest versions 3.10 and whatever the current latest release version is at the time. - A patch that adds a new feature should add appropriate tests for the feature. A bug fix patch should preferably add a test that would fail if the bug recurs. If you are using GitHub, then you may utilize the GitHub Actions CI build systems. They will run some of the above tests automatically when you push changes to your repository. Email Subject ------------- The subject line of your email should be in the following format: [PATCH /] :

Where: ``[PATCH /]``: indicates that this is the nth of a series of m patches. It helps reviewers to read patches in the correct order. You may omit this prefix if you are sending only one patch. ````: indicates the area of the Open vSwitch to which the change applies (often the name of a source file or a directory). You may omit it if the change crosses multiple distinct pieces of code. ````: briefly describes the change. Use the imperative form, e.g. "Force SNAT for multiple gateway routers." or "Fix daemon exit for bad datapaths or flows." Try to keep the summary short, about 50 characters wide. The subject, minus the ``[PATCH /]`` prefix, becomes the first line of the commit's change log message. Description ----------- The body of the email should start with a more thorough description of the change. This becomes the body of the commit message, following the subject. There is no need to duplicate the summary given in the subject. Please limit lines in the description to 75 characters in width. That allows the description to format properly even when indented (e.g. by "git log" or in email quotations). The description should include: - The rationale for the change. - Design description and rationale (but this might be better added as code comments). - Testing that you performed (or testing that should be done but you could not for whatever reason). - Tags (see below). There is no need to describe what the patch actually changed, if the reader can see it for himself. If the patch refers to a commit already in the Open vSwitch repository, please include both the commit number and the subject of the patch, e.g. 'commit 632d136c (vswitch: Remove restriction on datapath names.)'. If you, the person sending the patch, did not write the patch yourself, then the very first line of the body should take the form ``From: ``, followed by a blank line. This will automatically cause the named author to be credited with authorship in the repository. Tags ---- The description ends with a series of tags, written one to a line as the last paragraph of the email. Each tag indicates some property of the patch in an easily machine-parseable manner. Please don't wrap a tag across multiple lines. If necessary, it's OK to have a tag extend beyond the customary maximum width of a commit message. Examples of common tags follow. ``Signed-off-by: Author Name `` Informally, this indicates that Author Name is the author or submitter of a patch and has the authority to submit it under the terms of the license. The formal meaning is to agree to the Developer's Certificate of Origin (see below). If the author and submitter are different, each must sign off. If the patch has more than one author, all must sign off. Signed-off-by tags should be the last tags in the commit message. If the author (or authors) and submitter are different, the author tags should come first. More generally, occasionally a patch might pass through a chain of submitters, and in such a case the sign-offs should be arranged in chronological order. :: Signed-off-by: Author Name Signed-off-by: Submitter Name ``Co-authored-by: Author Name `` Git can only record a single person as the author of a given patch. In the rare event that a patch has multiple authors, one must be given the credit in Git and the others must be credited via Co-authored-by: tags. (All co-authors must also sign off.) ``Acked-by: Reviewer Name `` Reviewers will often give an ``Acked-by:`` tag to code of which they approve. It is polite for the submitter to add the tag before posting the next version of the patch or applying the patch to the repository. Quality reviewing is hard work, so this gives a small amount of credit to the reviewer. Not all reviewers give ``Acked-by:`` tags when they provide positive reviews. It's customary only to add tags from reviewers who actually provide them explicitly. ``Tested-by: Tester Name `` When someone tests a patch, it is customary to add a Tested-by: tag indicating that. It's rare for a tester to actually provide the tag; usually the patch submitter makes the tag himself in response to an email indicating successful testing results. ``Tested-at: `` When a test report is publicly available, this provides a way to reference it. Typical s would be build logs from autobuilders or references to mailing list archives. Some autobuilders only retain their logs for a limited amount of time. It is less useful to cite these because they may be dead links for a developer reading the commit message months or years later. ``Reported-by: Reporter Name `` When a patch fixes a bug reported by some person, please credit the reporter in the commit log in this fashion. Please also add the reporter's name and email address to the list of people who provided helpful bug reports in the AUTHORS file at the top of the source tree. Fairly often, the reporter of a bug also tests the fix. Occasionally one sees a combined "Reported-and-tested-by:" tag used to indicate this. It is also acceptable, and more common, to include both tags separately. (If a bug report is received privately, it might not always be appropriate to publicly credit the reporter. If in doubt, please ask the reporter.) ``Requested-by: Requester Name `` When a patch implements a request or a suggestion made by some person, please credit that person in the commit log in this fashion. For a helpful suggestion, please also add the person's name and email address to the list of people who provided suggestions in the AUTHORS file at the top of the source tree. (If a suggestion or a request is received privately, it might not always be appropriate to publicly give credit. If in doubt, please ask.) ``Suggested-by: Suggester Name `` See ``Requested-by:``. ``CC: Person `` This is a way to tag a patch for the attention of a person when no more specific tag is appropriate. One use is to request a review from a particular person. It doesn't make sense to include the same person in CC and another tag, so e.g. if someone who is CCed later provides an Acked-by, add the Acked-by and remove the CC at the same time. ``Reported-at: `` If a patch fixes or is otherwise related to a bug reported in a public bug tracker, please include a reference to the bug in the form of a URL to the specific bug, e.g.: :: Reported-at: https://bugs.debian.org/743635 This is also an appropriate way to refer to bug report emails in public email archives, e.g.: :: Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2014-June/284495.html ``Submitted-at: `` If a patch was submitted somewhere other than the Open vSwitch development mailing list, such as a GitHub pull request, this header can be used to reference the source. :: Submitted-at: https://github.com/openvswitch/ovs/pull/92 ``VMware-BZ: #1234567`` If a patch fixes or is otherwise related to a bug reported in a private bug tracker, you may include some tracking ID for the bug for your own reference. Please include some identifier to make the origin clear, e.g. "VMware-BZ" refers to VMware's internal Bugzilla instance and "ONF-JIRA" refers to the Open Networking Foundation's JIRA bug tracker. ``ONF-JIRA: EXT-12345`` See ``VMware-BZ:``. ``Bug #1234567.`` These are obsolete forms of VMware-BZ: that can still be seen in old change log entries. (They are obsolete because they do not tell the reader what bug tracker is referred to.) ``Issue: 1234567`` See ``Bug:``. ``Fixes: 63bc9fb1c69f ("packets: Reorder CS_* flags to remove gap.")`` If you would like to record which commit introduced a bug being fixed, you may do that with a "Fixes" header. This assists in determining which OVS releases have the bug, so the patch can be applied to all affected versions. The easiest way to generate the header in the proper format is with this git command. This command also CCs the author of the commit being fixed, which makes sense unless the author also made the fix or is already named in another tag: :: $ git log -1 --pretty=format:"CC: %an <%ae>%nFixes: %h (\"%s\")" \ --abbrev=12 COMMIT_REF ``Vulnerability: CVE-2016-2074`` Specifies that the patch fixes or is otherwise related to a security vulnerability with the given CVE identifier. Other identifiers in public vulnerability databases are also suitable. If the vulnerability was reported publicly, then it is also appropriate to cite the URL to the report in a Reported-at tag. Use a Reported-by tag to acknowledge the reporters. ``Assisted-by: Name of model, and/or AI Code Assistant`` When a patch has been created with the assistance of an AI tool, this tag should be used to disclose that fact. Provide the name of the underlying model used, if known. For the name of the tool, only include the name, not an email address. For example: :: Assisted-by: model-name-42.0, OVS-Code-Assistant-Pro-9.0 The author of the patch remains fully responsible for the content and must ensure it complies with the `Developer's Certificate of Origin`_. See the `AI-assisted Contributions`_ section for more information. Developer's Certificate of Origin --------------------------------- To help track the author of a patch as well as the submission chain, and be clear that the developer has authority to submit a patch for inclusion in Open vSwitch please sign off your work. The sign off certifies the following: :: Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. See also http://developercertificate.org/. AI-assisted Contributions ------------------------- OVS is a Linux Foundation Collaborative Project, and the Linux Foundation's policy for AI-generated patches can be found here: https://www.linuxfoundation.org/legal/generative-ai . OVS allows the use of AI assistants in producing patches. Contributions which have used an AI assistant should disclose the use of the assistant by using the "Assisted-by" tag. While AI-assisted patches are allowed, the author of the patch is ultimately responsible for ensuring that the AI-generated code has not violated any terms of the Developer's Certificate of Origin. Feature Deprecation Guidelines ------------------------------ Open vSwitch is intended to be user friendly. This means that under normal circumstances we don't abruptly remove features from OVS that some users might still be using. Otherwise, if we would, then we would possibly break our user setup when they upgrade and would receive bug reports. Typical process to deprecate a feature in Open vSwitch is to: (a) Mention deprecation of a feature in the NEWS file. Also, mention expected release or absolute time when this feature would be removed from OVS altogether. Don't use relative time (e.g. "in 6 months") because that is not clearly interpretable. (b) If Open vSwitch is configured to use deprecated feature it should print a warning message to the log files clearly indicating that feature is deprecated and that use of it should be avoided. (c) If this feature is mentioned in man pages, then add "Deprecated" keyword to it. Also, if there is alternative feature to the one that is about to be marked as deprecated, then mention it in (a), (b) and (c) as well. Remember to follow-up and actually remove the feature from OVS codebase once deprecation grace period has expired and users had opportunity to use at least one OVS release that would have informed them about feature deprecation! Comments -------- If you want to include any comments in your email that should not be part of the commit's change log message, put them after the description, separated by a line that contains just ``---``. It may be helpful to include a diffstat here for changes that touch multiple files. Patch ----- The patch should be in the body of the email following the description, separated by a blank line. Patches should be in ``diff -up`` format. We recommend that you use Git to produce your patches, in which case you should use the ``-M -C`` options to ``git diff`` (or other Git tools) if your patch renames or copies files. `Quilt `__ might be useful if you do not want to use Git. Patches should be inline in the email message. Some email clients corrupt white space or wrap lines in patches. There are hints on how to configure many email clients to avoid this problem on `kernel.org `__. If you cannot convince your email client not to mangle patches, then sending the patch as an attachment is a second choice. Follow the style used in the code that you are modifying. :doc:`coding-style` file describes the coding style used in most of Open vSwitch. Use Linux kernel coding style for Linux kernel code. If your code is non-datapath code, you may use the ``utilities/checkpatch.py`` utility as a quick check for certain commonly occurring mistakes (improper leading/trailing whitespace, missing signoffs, some improper formatted patch files). For Linux datapath code, it is a good idea to use the Linux script ``checkpatch.pl``. Example ------- :: From fa29a1c2c17682879e79a21bb0cdd5bbe67fa7c0 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 8 Dec 2011 13:17:24 -0800 Subject: [PATCH] datapath: Alphabetize include/net/ipv6.h compat header. Signed-off-by: Jesse Gross --- datapath/linux/Modules.mk | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index fdd952e..f6cb88e 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -56,11 +56,11 @@ openvswitch_headers += \ linux/compat/include/net/dst.h \ linux/compat/include/net/genetlink.h \ linux/compat/include/net/ip.h \ + linux/compat/include/net/ipv6.h \ linux/compat/include/net/net_namespace.h \ linux/compat/include/net/netlink.h \ linux/compat/include/net/protocol.h \ linux/compat/include/net/route.h \ - linux/compat/include/net/ipv6.h \ linux/compat/genetlink.inc both_modules += brcompat -- 1.7.7.3 openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/documentation.rst000066400000000000000000000060431514270232600271760ustar00rootroot00000000000000.. Copyright (c) 2017 Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================================== How Open vSwitch's Documentation Works ====================================== This document provides a brief overview on how the documentation build system within Open vSwitch works. This is intended to maximize the "bus factor" and share best practices with other projects. reStructuredText and Sphinx --------------------------- Nearly all of Open vSwitch's documentation is written in `reStructuredText`__, with man pages being the sole exception. Of this documentation, most of it is fed into `Sphinx`__, which provides not only the ability to convert rST to a variety of other output formats but also allows for things like cross-referencing and indexing. for more information on the two, refer to the :doc:`contributing/documentation-style`. ovs-sphinx-theme ---------------- The documentation uses its own theme, `ovs-sphinx-theme`, which can be found on GitHub__ and is published on pypi__. This is packaged separately from Open vSwitch itself to ensure all documentation gets the latest version of the theme (assuming there are no major version bumps in that package). If building locally and the package is installed, it will be used. If the package is not installed, Sphinx will fallback to the default theme. The package is currently maintained by Stephen Finucane and Russell Bryant. Read the Docs ------------- The documentation is hosted on readthedocs.org and a CNAME redirect is in place to allow access from docs.openvswitch.org. *Read the Docs* provides a couple of nifty features for us, such as automatic building of docs whenever there are changes and versioning of documentation. The *Read the Docs* project is currently maintained by Stephen Finucane, Russell Bryant and Ben Pfaff. openvswitch.org --------------- The sources for openvswitch.org are maintained separately from docs.openvswitch.org. For modifications to this site, refer to the `GitHub project`__. __ http://docutils.sourceforge.net/rst.html __ http://www.sphinx-doc.org/ __ https://github.com/openvswitch/ovs-sphinx-theme __ https://pypi.python.org/pypi/ovs-sphinx-theme __ https://github.com/openvswitch/openvswitch.github.io openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/index.rst000066400000000000000000000025521514270232600254350ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================== Open vSwitch Internals ====================== Information for people who want to know more about the Open vSwitch project itself and how they might involved. .. toctree:: :maxdepth: 2 contributing/index mailing-lists patchwork release-process bugs security charter committer-emeritus-status committer-responsibilities committer-grant-revocation authors maintainers documentation openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/mailing-lists.rst000066400000000000000000000055601514270232600271040ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============= Mailing Lists ============= .. important:: Report security issues **only** to security@openvswitch.org. For more information, refer to our :doc:`security policies `. ovs-announce ------------ The `ovs-announce`__ mailing list is used to announce new versions of Open vSwitch and is extremely low-volume. `(subscribe)`__ `(archives)`__ __ mailto:ovs-announce@openvswitch.org __ https://mail.openvswitch.org/mailman/listinfo/ovs-announce/ __ https://mail.openvswitch.org/pipermail/ovs-announce/ ovs-discuss ----------- The `ovs-discuss`__ mailing list is used to discuss plans and design decisions for Open vSwitch. It is also an appropriate place for user questions. `(subscribe)`__ `(archives)`__ __ mailto:ovs-discuss@openvswitch.org __ https://mail.openvswitch.org/mailman/listinfo/ovs-discuss/ __ https://mail.openvswitch.org/pipermail/ovs-discuss/ ovs-dev ------- The `ovs-dev`__ mailing list is used to discuss development and review code before being committed. `(subscribe)`__ `(archives)`__ __ mailto:ovs-dev@openvswitch.org __ https://mail.openvswitch.org/mailman/listinfo/ovs-dev/ __ https://mail.openvswitch.org/pipermail/ovs-dev/ ovs-git ------- The `ovs-git`__ mailing list hooks into Open vSwitch's version control system to receive commits. `(subscribe)`__ `(archives)`__ __ mailto:ovs-git@openvswitch.org __ https://mail.openvswitch.org/mailman/listinfo/ovs-git/ __ https://mail.openvswitch.org/pipermail/ovs-git/ ovs-build --------- The `ovs-build`__ mailing list hooks into Open vSwitch's continuous integration system to receive build reports. `(subscribe)`__ `(archives)`__ __ mailto:ovs-build@openvswitch.org __ https://mail.openvswitch.org/mailman/listinfo/ovs-build/ __ https://mail.openvswitch.org/pipermail/ovs-build/ bugs ----- The `bugs`__ mailing list is an alias for the discuss mailing list. __ mailto:bugs@openvswitch.org security -------- The `security`__ mailing list is for submitting security vulnerabilities to the security team. __ mailto:security@openvswitch.org openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/maintainers.rst000066400000000000000000000022351514270232600266360ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. .. include:: ../../MAINTAINERS.rst :end-before: Cut here for the Documentation/internals/maintainers.rst .. |responsibilities| replace:: :doc:`committer-responsibilities` .. |grant-revocation| replace:: :doc:`committer-grant-revocation` .. |emeritus-status| replace:: :doc:`committer-emeritus-status` openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/patchwork.rst000066400000000000000000000055151514270232600263320ustar00rootroot00000000000000.. Copyright (C) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========= Patchwork ========= Open vSwitch uses `Patchwork`__ to track the status of patches sent to the :doc:`ovs-dev mailing list `. The Open vSwitch Patchwork instance can be found on `ozlabs.org`__. Patchwork provides a number of useful features for developers working on Open vSwitch: - Tracking the lifecycle of patches (accepted, rejected, under-review, ...) - Assigning reviewers (delegates) to patches - Downloading/applying patches, series, and bundles via the web UI or the REST API (see :ref:`git-pw`) - A usable UI for viewing patch discussions __ https://github.com/getpatchwork/patchwork __ https://patchwork.ozlabs.org/project/openvswitch/list/ .. _git-pw: git-pw ------ The *git-pw* tool provides a way to download and apply patches, series, and bundles. You can install *git-pw* from `PyPi`__ like so:: $ pip install --user git-pw To actually use *git-pw*, you must configure it with the Patchwork instance URL, Patchwork project, and your Patchwork user authentication token. The URL and project are provided below, but you must obtain your authentication token from your `Patchwork User Profile`__ page. If you do not already have a Patchwork user account, you should create one now. Once your token is obtained, configure *git-pw* as below. Note that this must be run from within the Open vSwitch Git repository:: $ git config pw.server https://patchwork.ozlabs.org/ $ git config pw.project openvswitch $ git config pw.token $PW_TOKEN # using the token obtained earlier Once configured, run the following to get information about available commands:: $ git pw --help __ https://pypi.python.org/pypi/git-pw __ https://patchwork.ozlabs.org/user/ .. _pwclient: pwclient -------- The *pwclient* is a legacy tool that provides some of the functionality of *git-pw* but uses the legacy XML-RPC API. It is considered deprecated in its current form and *git-pw* should be used instead. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/release-process.rst000066400000000000000000000242551514270232600274260ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =============== Release Process =============== This document describes the process ordinarily used for Open vSwitch development and release. Exceptions are sometimes necessary, so all of the statements here should be taken as subject to change through rough consensus of Open vSwitch contributors, obtained through public discussion on, e.g., ovs-dev or the #openvswitch IRC channel. Release Strategy ---------------- Open vSwitch feature development takes place on the "main" branch. Ordinarily, new features are rebased against main and applied directly. For features that take significant development, sometimes it is more appropriate to merge a separate branch into main; please discuss this on ovs-dev in advance. The process of making a release has the following stages. See `Release Scheduling`_ for the timing of each stage: 1. "Soft freeze" of the main branch. During the freeze, we ask committers to refrain from applying patches that add new features unless those patches were already being publicly discussed and reviewed before the freeze began. Bug fixes are welcome at any time. Please propose and discuss exceptions on ovs-dev. 2. Fork a release branch from main, named for the expected release number, e.g. "branch-2.3" for the branch that will yield Open vSwitch 2.3.x. Release branches are intended for testing and stabilization. At this stage and in later stages, they should receive only bug fixes, not new features. Bug fixes applied to release branches should be backports of corresponding bug fixes to the main branch, except for bugs present only on release branches (which are rare in practice). At this stage, sometimes there can be exceptions to the rule that a release branch receives only bug fixes. Like bug fixes, new features on release branches should be backports of the corresponding commits on the main branch. Features to be added to release branches should be limited in scope and risk and discussed on ovs-dev before creating the branch. 3. When committers come to rough consensus that the release is ready, they release the .0 release on its branch, e.g. 2.3.0 for branch-2.3. To make the actual release, a committer pushes a signed tag named, e.g. v2.3.0, to the Open vSwitch repository, makes a release tarball available on openvswitch.org, and posts a release announcement to ovs-announce. 4. As bug fixes accumulate, or after important bugs or vulnerabilities are fixed, committers may make additional releases from a branch: 2.3.1, 2.3.2, and so on. The process is the same for these additional release as for a .0 release. At most three release branches are formally maintained at any given time: the latest release, the latest release designed as LTS and a previous LTS release during the transition period. An LTS release is one that the OVS project has designated as being maintained for a longer period of time. Currently, an LTS release is maintained until the next major release after the new LTS is chosen. This one release time frame is a transition period which is intended for users to upgrade from old LTS to new one. New LTS release is chosen every 2 years. The process is that current latest stable release becomes an LTS release at the same time the next major release is out. That could change based on the current state of OVS development. For example, we do not want to designate a new release as LTS that includes disruptive internal changes, as that may make it harder to support for a longer period of time. Discussion about skipping designation of the next LTS release occurs on the OVS development mailing list. LTS designation schedule example (depends on current state of development): +---------+--------------+--------------------------------------------------+ | Version | Release Date | Actions | +---------+--------------+--------------------------------------------------+ | 2.17 | Feb 2022 | 2.17 - new latest stable | +---------+--------------+--------------------------------------------------+ | 3.0 | Aug 2022 | 3.0 - new latest stable, 2.17 stable ⟶ new LTS | +---------+--------------+--------------------------------------------------+ | 3.1 | Feb 2023 | 3.1 - new latest stable, 2.13 LTS ⟶ EOL | +---------+--------------+--------------------------------------------------+ | 3.2 | Aug 2023 | 3.2 - new latest stable | +---------+--------------+--------------------------------------------------+ | 3.3 | Feb 2024 | 3.3 - new latest stable | +---------+--------------+--------------------------------------------------+ | 3.4 | Aug 2024 | 3.4 - new latest stable, 3.3 stable ⟶ new LTS | +---------+--------------+--------------------------------------------------+ | 3.5 | Feb 2025 | 3.5 - new latest stable, 2.17 LTS ⟶ EOL | +---------+--------------+--------------------------------------------------+ | 3.6 | Aug 2025 | 3.6 - new latest stable | +---------+--------------+--------------------------------------------------+ While branches other than LTS and the latest release are not formally maintained, the OVS project usually provides stable releases for these branches for at least 2 years, i.e. stable releases are provided for the last 4 release branches. However, these branches may not include all the fixes that LTS has in case backporting is not straightforward and developers are not willing to spend their time on that (this mostly affects branches that are older than the LTS, because backporting to LTS implies backporting to all intermediate branches). Release Numbering ----------------- The version number on main should normally end in .90. This indicates that the Open vSwitch version is "almost" the next version to branch. Forking main into branch-x.y requires two commits to main. The first is titled "Prepare for x.y.0" and increments the version number to x.y. This is the initial commit on branch-x.y. The second is titled "Prepare for post-x.y.0 (x.y.90)" and increments the version number to x.y.90. The version number on a release branch is x.y.z, where z is initially 0. Making a release requires two commits. The first is titled *Set release dates for x.y.z.* and updates NEWS and debian/changelog to specify the release date of the new release. This commit is the one made into a tarball and tagged. The second is titled *Prepare for x.y.(z+1).* and increments the version number and adds a blank item to NEWS with an unspecified date. Release Scheduling ------------------ Open vSwitch makes releases at the following six-month cadence. All dates are approximate: +---------------+----------------+------------------------------------+ | Time (months) | Dates | Stage | +---------------+----------------+------------------------------------+ | T | Mar 1, Sep 1 | Begin x.y release cycle | +---------------+----------------+------------------------------------+ | T + 4 | Jul 1, Jan 1 | "Soft freeze" main for x.y release | +---------------+----------------+------------------------------------+ | T + 4.5 | Jul 15, Jan 15 | Fork branch-x.y from main | +---------------+----------------+------------------------------------+ | T + 5.5 | Aug 15, Feb 15 | Release version x.y.0 | +---------------+----------------+------------------------------------+ How to Branch ------------- To branch "main" for the eventual release of OVS version x.y.0, prepare two patches against main: 1. "Prepare for x.y.0." following the model of commit 836d1973c56e ("Prepare for 2.11.0."). 2. "Prepare for post-x.y.0 (x.y.90)." following the model of commit fe2870c574db ("Prepare for post-2.11.0 (2.11.90).") Post both patches to ovs-dev. Get them reviewed in the usual way. Apply both patches to main, and create branch-x.y by pushing only the first patch. The following command illustrates how to do both of these at once assuming the local repository HEAD points to the "Prepare for post-x.y.0" commit: git push origin HEAD:main HEAD^:refs/heads/branch-x.y Branching should be announced on ovs-dev. How to Release -------------- Follow these steps to release version x.y.z of OVS from branch-x.y. 1. Prepare two patches against branch-x.y: a. "Set release date for x.y.z". For z = 0, follow the model of commit d11f4cbbfe05 ("Set release date for 2.12.0."); for z > 0, follow the model of commit 53d5c18118b0 ("Set release date for 2.11.3."). b. "Prepare for x.y.(z+1)." following the model of commit db02dd23e48a ("Prepare for 2.11.1."). 3. Post the patches to ovs-dev. Get them reviewed in the usual way. 4. Apply the patches to branch-x.y. 5. If z = 0, apply the first patch (only) to main. 6. Sign a tag vx.y.z "Open vSwitch version x.y.z" and push it to the repo. 7. Update http://www.openvswitch.org/download/. See commit 31eaa72cafac ("Add 2.12.0 and older release announcements.") in the website repo (https://github.com/openvswitch/openvswitch.github.io) for an example. 8. Consider updating the Wikipedia page for Open vSwitch at https://en.wikipedia.org/wiki/Open_vSwitch 9. Tweet. Contact ------- Use dev@openvswitch.org to discuss the Open vSwitch development and release process. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/internals/security.rst000066400000000000000000000271611514270232600262000ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================ Security Process ================ This is a proposed security vulnerability reporting and handling process for Open vSwitch. It is based on the OpenStack vulnerability management process described at https://wiki.openstack.org/wiki/Vulnerability\_Management. The OVS security team coordinates vulnerability management using the ovs-security mailing list. Membership in the security team and subscription to its mailing list consists of a small number of trustworthy people, as determined by rough consensus of the Open vSwitch committers on the ovs-committers mailing list. The Open vSwitch security team should include Open vSwitch committers, to ensure prompt and accurate vulnerability assessments and patch review. We encourage everyone involved in the security process to GPG-sign their emails. We additionally encourage GPG-encrypting one-on-one conversations as part of the security process. What is a vulnerability? ------------------------ All vulnerabilities are bugs, but not every bug is a vulnerability. Vulnerabilities compromise one or more of: * Confidentiality (personal or corporate confidential data). * Integrity (trustworthiness and correctness). * Availability (uptime and service). Here are some examples of vulnerabilities to which one would expect to apply this process: * A crafted packet that causes a kernel or userspace crash (Availability). * A flow translation bug that misforwards traffic in a way likely to hop over security boundaries (Integrity). * An OpenFlow protocol bug that allows a controller to read arbitrary files from the file system (Confidentiality). * Misuse of the OpenSSL library that allows bypassing certificate checks (Integrity). * A bug (memory corruption, overflow, ...) that allows one to modify the behaviour of OVS through external configuration interfaces such as OVSDB (Integrity). * Privileged information is exposed to unprivileged users (Confidentiality). If in doubt, please do use the vulnerability management process. At worst, the response will be to report the bug through the usual channels. Step 1: Reception ----------------- To report an Open vSwitch vulnerability, send an email to the ovs-security mailing list (see contact_ at the end of this document). A security team member should reply to the reporter acknowledging that the report has been received. Consider reporting the information mentioned in :doc:`bugs`, where relevant. Reporters may ask for a GPG key while initiating contact with the security team to deliver more sensitive reports. The Linux kernel has `its own vulnerability management process `__. Handling of vulnerabilities that affect both the Open vSwitch tree and the upstream Linux kernel should be reported through both processes. Send your report as a single email to both the kernel and OVS security teams to allow those teams to most easily coordinate among themselves. Step 2: Assessment ------------------ The security team should discuss the vulnerability. The reporter should be included in the discussion (via "CC") to an appropriate degree. The assessment should determine which Open vSwitch versions are affected (e.g. every version, only the latest release, only unreleased versions), the privilege required to take advantage of the vulnerability (e.g. any network user, any local L2 network user, any local system user, connected OpenFlow controllers), the severity of the vulnerability, and how the vulnerability may be mitigated (e.g. by disabling a feature). The treatment of the vulnerability could end here if the team determines that it is not a realistic vulnerability. Step 3a: Document ----------------- The security team develops a security advisory document. The security team may, at its discretion, include the reporter (via "CC") in developing the security advisory document, but in any case should accept feedback from the reporter before finalizing the document. When the document is final, the security team should obtain a CVE for the vulnerability from a CNA (https://cve.mitre.org/cve/cna.html). The document credits the reporter and describes the vulnerability, including all of the relevant information from the assessment in step 2. Suitable sections for the document include: :: * Title: The CVE identifier, a short description of the vulnerability. The title should mention Open vSwitch. In email, the title becomes the subject. Pre-release advisories are often passed around in encrypted email, which have plaintext subjects, so the title should not be too specific. * Description: A few paragraphs describing the general characteristics of the vulnerability, including the versions of Open vSwitch that are vulnerable, the kind of attack that exposes the vulnerability, and potential consequences of the attack. The description should re-state the CVE identifier, in case the subject is lost when an advisory is sent over email. * Mitigation: How an Open vSwitch administrator can minimize the potential for exploitation of the vulnerability, before applying a fix. If no mitigation is possible or recommended, explain why, to reduce the chance that at-risk users believe they are not at risk. * Fix: Describe how to fix the vulnerability, perhaps in terms of applying a source patch. The patch or patches themselves, if included in the email, should be at the very end of the advisory to reduce the risk that a reader would stop reading at this point. * Recommendation: A concise description of the security team's recommendation to users. * Acknowledgments: Thank the reporters. * Vulnerability Check: A step-by-step procedure by which a user can determine whether an installed copy of Open vSwitch is vulnerable. The procedure should clearly describe how to interpret the results, including expected results in vulnerable and not-vulnerable cases. Thus, procedures that produce clear and easily distinguished results are preferred. The procedure should assume as little understanding of Open vSwitch as possible, to make it more likely that a competent administrator who does not specialize in Open vSwitch can perform it successfully. The procedure should have minimal dependencies on tools that are not widely installed. Given a choice, the procedure should be one that takes at least some work to turn into a useful exploit. For example, a procedure based on "ovs-appctl" commands, which require local administrator access, is preferred to one that sends test packets to a machine, which only requires network connectivity. The section should say which operating systems it is designed for. If the procedure is likely to be specific to particular architectures (e.g. x86-64, i386), it should state on which ones it has been tested. This section should state the risks of the procedure. For example, if it can crash Open vSwitch or disrupt packet forwarding, say so. It is more useful to explain how to check an installed and running Open vSwitch than one built locally from source, but if it is easy to use the procedure from a sandbox environment, it can be helpful to explain how to do so. * Patch: If a patch or patches are available, and it is practical to include them in the email, put them at the end. Format them as described in :doc:`contributing/submitting-patches`, that is, as output by "git format-patch". The patch subjects should include the version for which they are suited, e.g. "[PATCH branch-2.3]" for a patch against Open vSwitch 2.3.x. If there are multiple patches for multiple versions of Open vSwitch, put them in separate sections with clear titles. Multiple patches for a single version of Open vSwitch, that must be stacked on top of each other to fix a single vulnerability, are undesirable because users are less likely to apply all of them correctly and in the correct order. Each patch should include a Vulnerability tag with the CVE identifier, a Reported-by tag or tags to credit the reporters, and a Signed-off-by tag to acknowledge the Developer's Certificate of Origin. It should also include other appropriate tags, such as Acked-by tags obtained during review. `CVE-2016-2074 `__ is an example advisory document. Step 3b: Fix ------------ Steps 3a and 3b may proceed in parallel. The security team develops and obtains (private) reviews for patches that fix the vulnerability. If necessary, the security team pulls in additional developers, who must agree to maintain confidentiality. Step 4: Embargoed Disclosure ---------------------------- The security advisory and patches are sent to downstream stakeholders, with an embargo date and time set from the time sent. Downstream stakeholders are expected not to deploy or disclose patches until the embargo is passed. A disclosure date is negotiated by the security team working with the bug submitter as well as vendors. However, the Open vSwitch security team holds the final say when setting a disclosure date. The timeframe for disclosure is from immediate (esp. if it's already publicly known) to a few weeks. As a basic default policy, we expect report date to disclosure date to be 10 to 15 business days. Operating system vendors are obvious downstream stakeholders, however, any major Open vSwitch user who is interested and can be considered trustworthy enough could be included. To request being added to the Downstream mailing list, email the ovs-security mailing list. Please include a few sentences on how your organization uses Open vSwitch. If possible, please provide a security-related email alias rather than a direct end-user address. If the vulnerability is already public, skip this step. Step 5: Public Disclosure ------------------------- When the embargo expires, push the (reviewed) patches to appropriate branches, post the patches to the ovs-dev mailing list (noting that they have already been reviewed and applied), post the security advisory to appropriate mailing lists (ovs-announce, ovs-discuss), and post the security advisory on the Open vSwitch webpage. When the patch is applied to LTS (long-term support) branches, a new version should be released. The security advisory should be GPG-signed by a security team member with a key that is in a public web of trust. .. _contact: Contact ======= Report security vulnerabilities to the ovs-security mailing list: security@openvswitch.org Report problems with this document to the ovs-bugs mailing list: bugs@openvswitch.org openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/000077500000000000000000000000001514270232600227245ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/index.rst000066400000000000000000000021141514270232600245630ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =============== Getting Started =============== How to get started with Open vSwitch. .. toctree:: :maxdepth: 2 what-is-ovs why-ovs install/index openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/000077500000000000000000000000001514270232600243725ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/afxdp.rst000066400000000000000000000337571514270232600262450ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================== Open vSwitch with AF_XDP ======================== This document describes how to build and install Open vSwitch using AF_XDP netdev. .. warning:: The AF_XDP support of Open vSwitch is considered 'experimental'. Introduction ------------ AF_XDP, Address Family of the eXpress Data Path, is a new Linux socket type built upon the eBPF and XDP technology. It is aims to have comparable performance to DPDK but cooperate better with existing kernel's networking stack. An AF_XDP socket receives and sends packets from an eBPF/XDP program attached to the netdev, by-passing a couple of Linux kernel's subsystems. As a result, AF_XDP socket shows much better performance than AF_PACKET. For more details about AF_XDP, please see linux kernel's Documentation/networking/af_xdp.rst AF_XDP Netdev ------------- OVS has a couple of netdev types, i.e., system, tap, or dpdk. The AF_XDP feature adds a new netdev types called "afxdp", and implement its configuration, packet reception, and transmit functions. Since the AF_XDP socket, called xsk, operates in userspace, once ovs-vswitchd receives packets from xsk, the afxdp netdev re-uses the existing userspace dpif-netdev datapath. As a result, most of the packet processing happens at the userspace instead of linux kernel. :: | +-------------------+ | | ovs-vswitchd |<-->ovsdb-server | +-------------------+ | | ofproto |<-->OpenFlow controllers | +--------+-+--------+ | | netdev | |ofproto-| userspace | +--------+ | dpif | | | afxdp | +--------+ | | netdev | | dpif | | +---||---+ +--------+ | || | dpif- | | || | netdev | |_ || +--------+ || _ +---||-----+--------+ | | AF_XDP prog + | kernel | | xsk_map | |_ +--------||---------+ || physical NIC Build requirements ------------------ In addition to the requirements described in :doc:`general`, building Open vSwitch with AF_XDP will require the following: - ``libbpf`` and ``libxdp`` (if version of ``libbpf`` if higher than ``0.6``). - Linux kernel v5.4+ or a nominally older distribution kernel that has required features backported. - Linux kernel XDP support, with the following options (required) * CONFIG_BPF=y * CONFIG_BPF_SYSCALL=y * CONFIG_XDP_SOCKETS=y - The following optional Kconfig options are also recommended, but not required: * CONFIG_BPF_JIT=y (Performance) * CONFIG_HAVE_EBPF_JIT=y (Performance) * CONFIG_XDP_SOCKETS_DIAG=y (Debugging) - If you're building your own kernel, be sure that you're installing kernel headers too. For example, with the following command:: make headers_install INSTALL_HDR_PATH=/usr - If you're using kernel from the distribution, be sure that corresponding kernel headers package installed. - Once your AF_XDP-enabled kernel is ready, if possible, run **./xdpsock -r -N -z -i ** under linux/samples/bpf. This is an OVS independent benchmark tools for AF_XDP. It makes sure your basic kernel requirements are met for AF_XDP. Installing ---------- For OVS to use AF_XDP netdev, it has to be configured with LIBBPF support. First, install ``libbpf`` and ``libxdp``. For example, on Fedora these libraries along with development headers can be obtained by installing ``libbpf-devel`` and ``libxdp-devel`` packages. For Ubuntu that will be ``libbpf-dev`` package with additional ``libxdp-dev`` on Ubuntu 22.10 or later. Next, ensure the standard OVS requirements are installed and bootstrap/configure the package:: ./boot.sh && ./configure --enable-afxdp ``--enable-afxdp`` here is optional, but it will ensure that all dependencies are available at the build time. Finally, build and install OVS:: make && make install To kick start end-to-end autotesting:: uname -a # make sure having 5.4+ kernel ethtool --version # make sure ethtool is installed make check-afxdp TESTSUITEFLAGS='1' .. note:: Not all test cases pass at this time. Currently all cvlan tests are skipped due to kernel issues. If a test case fails, check the log at:: cat \ tests/system-afxdp-testsuite.dir//system-afxdp-testsuite.log Setup AF_XDP netdev ------------------- Before running OVS with AF_XDP, make sure the libbpf and libnuma are set-up right:: ldd vswitchd/ovs-vswitchd Open vSwitch should be started using userspace datapath as described in :doc:`general`:: ovs-vswitchd ... ovs-vsctl -- add-br br0 -- set Bridge br0 datapath_type=netdev Make sure your device driver support AF_XDP, netdev-afxdp supports the following additional options (see ``man ovs-vswitchd.conf.db`` for more details): * ``xdp-mode``: ``best-effort``, ``native-with-zerocopy``, ``native`` or ``generic``. Defaults to ``best-effort``, i.e. best of supported modes, so in most cases you don't need to change it. For example, to use 1 PMD (on core 4) on 1 queue (queue 0) device, configure these options: ``pmd-cpu-mask``, ``pmd-rxq-affinity``, and ``n_rxq``:: ethtool -L enp2s0 combined 1 ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x10 ovs-vsctl add-port br0 enp2s0 -- set interface enp2s0 type="afxdp" \ other_config:pmd-rxq-affinity="0:4" Or, use 4 pmds/cores and 4 queues by doing:: ethtool -L enp2s0 combined 4 ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x36 ovs-vsctl add-port br0 enp2s0 -- set interface enp2s0 type="afxdp" \ options:n_rxq=4 other_config:pmd-rxq-affinity="0:1,1:2,2:3,3:4" .. note:: ``pmd-rxq-affinity`` is optional. If not specified, system will auto-assign. ``n_rxq`` equals ``1`` by default. To validate that the bridge has successfully instantiated, you can use the:: ovs-vsctl show Should show something like:: Port "ens802f0" Interface "ens802f0" type: afxdp options: {n_rxq="1"} Otherwise, enable debugging by:: ovs-appctl vlog/set netdev_afxdp::dbg To check which XDP mode was chosen by ``best-effort``, you can look for ``xdp-mode`` in the output of ``ovs-vsctl get interface INT status:xdp-mode``:: # ovs-vsctl get interface ens802f0 status:xdp-mode "native-with-zerocopy" References ---------- Most of the design details are described in the paper presented at Linux Plumber 2018, "Bringing the Power of eBPF to Open vSwitch"[1], section 4, and slides[2][4]. "The Path to DPDK Speeds for AF XDP"[3] gives a very good introduction about AF_XDP current and future work. [1] http://vger.kernel.org/lpc_net2018_talks/ovs-ebpf-afxdp.pdf [2] http://vger.kernel.org/lpc_net2018_talks/ovs-ebpf-lpc18-presentation.pdf [3] http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf [4] https://ovsfall2018.sched.com/event/IO7p/fast-userspace-ovs-with-afxdp Performance Tuning ------------------ The name of the game is to keep your CPU running in userspace, allowing PMD to keep polling the AF_XDP queues without any interferences from kernel. #. Make sure everything is in the same NUMA node (memory used by AF_XDP, pmd running cores, device plug-in slot) #. Isolate your CPU by doing isolcpu at grub configure. #. IRQ should not set to pmd running core. #. The Spectre and Meltdown fixes increase the overhead of system calls. Debugging performance issue ~~~~~~~~~~~~~~~~~~~~~~~~~~~ While running the traffic, use linux perf tool to see where your cpu spends its cycle:: cd bpf-next/tools/perf make ./perf record -p `pidof ovs-vswitchd` sleep 10 ./perf report Measure your system call rate by doing:: pstree -p `pidof ovs-vswitchd` strace -c -p Or, use OVS pmd tool:: ovs-appctl dpif-netdev/pmd-stats-show Example Script -------------- Below is a script using namespaces and veth peer:: #!/bin/bash ovs-vswitchd --no-chdir --pidfile -vvconn -vofproto_dpif -vunixctl \ --disable-system --detach \ ovs-vsctl -- add-br br0 -- set Bridge br0 \ protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14 \ fail-mode=secure datapath_type=netdev ip netns add at_ns0 ovs-appctl vlog/set netdev_afxdp::dbg ip link add p0 type veth peer name afxdp-p0 ip link set p0 netns at_ns0 ip link set dev afxdp-p0 up ovs-vsctl add-port br0 afxdp-p0 -- \ set interface afxdp-p0 external-ids:iface-id="p0" type="afxdp" ip netns exec at_ns0 sh << NS_EXEC_HEREDOC ip addr add "10.1.1.1/24" dev p0 ip link set dev p0 up NS_EXEC_HEREDOC ip netns add at_ns1 ip link add p1 type veth peer name afxdp-p1 ip link set p1 netns at_ns1 ip link set dev afxdp-p1 up ovs-vsctl add-port br0 afxdp-p1 -- \ set interface afxdp-p1 external-ids:iface-id="p1" type="afxdp" ip netns exec at_ns1 sh << NS_EXEC_HEREDOC ip addr add "10.1.1.2/24" dev p1 ip link set dev p1 up NS_EXEC_HEREDOC ip netns exec at_ns0 ping -i .2 10.1.1.2 Limitations/Known Issues ------------------------ #. No QoS support because AF_XDP netdev by-pass the Linux TC layer. A possible work-around is to use OpenFlow meter action. #. Most of the tests are done using i40e single port. Multiple ports and also ixgbe driver also needs to be tested. #. No latency test result (TODO items) #. Due to limitations of current upstream kernel, various offloading (vlan, cvlan) is not working over virtual interfaces (i.e. veth pair). Also, TCP is not working over virtual interfaces (veth) in generic XDP mode. Some more information and possible workaround available `here `__ . For TAP interfaces generic mode seems to work fine (TCP works) and even could provide better performance than native mode in some cases. PVP using tap device -------------------- Assume you have enp2s0 as physical nic, and a tap device connected to VM. First, start OVS, then add physical port:: ethtool -L enp2s0 combined 1 ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x10 ovs-vsctl add-port br0 enp2s0 -- set interface enp2s0 type="afxdp" \ options:n_rxq=1 other_config:pmd-rxq-affinity="0:4" Start a VM with virtio and tap device:: qemu-system-x86_64 -hda ubuntu1810.qcow \ -m 4096 \ -cpu host,+x2apic -enable-kvm \ -device virtio-net-pci,mac=00:02:00:00:00:01,netdev=net0,mq=on,vectors=10,mrg_rxbuf=on,rx_queue_size=1024 \ -netdev type=tap,id=net0,vhost=on,queues=8 \ -object memory-backend-file,id=mem,size=4096M,mem-path=/dev/hugepages,share=on \ -numa node,memdev=mem -mem-prealloc -smp 2 Create OpenFlow rules:: ovs-vsctl add-port br0 tap0 -- set interface tap0 ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 "in_port=enp2s0, actions=output:tap0" ovs-ofctl add-flow br0 "in_port=tap0, actions=output:enp2s0" Inside the VM, use xdp_rxq_info to bounce back the traffic:: ./xdp_rxq_info --dev ens3 --action XDP_TX PVP using vhostuser device -------------------------- First, build OVS with DPDK and AFXDP:: ./configure --enable-afxdp --with-dpdk=shared|static make -j4 && make install Create a vhost-user port from OVS:: ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true ovs-vsctl -- add-br br0 -- set Bridge br0 datapath_type=netdev \ other_config:pmd-cpu-mask=0xfff ovs-vsctl add-port br0 vhost-user-1 \ -- set Interface vhost-user-1 type=dpdkvhostuserclient \ options:vhost-server-path=/tmp/vhost-user-1 Start VM using vhost-user mode:: qemu-system-x86_64 -hda ubuntu1810.qcow \ -m 4096 \ -cpu host,+x2apic -enable-kvm \ -chardev socket,id=char1,path=/tmp/vhost-user-1,server \ -netdev type=vhost-user,id=mynet1,chardev=char1,vhostforce,queues=4 \ -device virtio-net-pci,mac=00:00:00:00:00:01,netdev=mynet1,mq=on,vectors=10 \ -object memory-backend-file,id=mem,size=4096M,mem-path=/dev/hugepages,share=on \ -numa node,memdev=mem -mem-prealloc -smp 2 Setup the OpenFlow ruls:: ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 "in_port=enp2s0, actions=output:vhost-user-1" ovs-ofctl add-flow br0 "in_port=vhost-user-1, actions=output:enp2s0" Inside the VM, use xdp_rxq_info to drop or bounce back the traffic:: ./xdp_rxq_info --dev ens3 --action XDP_DROP ./xdp_rxq_info --dev ens3 --action XDP_TX PCP container using veth ------------------------ Create namespace and veth peer devices:: ip netns add at_ns0 ip link add p0 type veth peer name afxdp-p0 ip link set p0 netns at_ns0 ip link set dev afxdp-p0 up ip netns exec at_ns0 ip link set dev p0 up Attach the veth port to br0 (linux kernel mode):: ovs-vsctl add-port br0 afxdp-p0 -- set interface afxdp-p0 Or, use AF_XDP:: ovs-vsctl add-port br0 afxdp-p0 -- set interface afxdp-p0 type="afxdp" Setup the OpenFlow rules:: ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 "in_port=enp2s0, actions=output:afxdp-p0" ovs-ofctl add-flow br0 "in_port=afxdp-p0, actions=output:enp2s0" In the namespace, run drop or bounce back the packet:: ip netns exec at_ns0 ./xdp_rxq_info --dev p0 --action XDP_DROP ip netns exec at_ns0 ./xdp_rxq_info --dev p0 --action XDP_TX Bug Reporting ------------- Please report problems to dev@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/bash-completion.rst000066400000000000000000000064771514270232600302260ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ==================================== Bash command-line completion scripts ==================================== There are two completion scripts available: ``ovs-appctl-bashcomp.bash`` and ``ovs-vsctl-bashcomp.bash``. ovs-appctl-bashcomp ------------------- ``ovs-appctl-bashcomp.bash`` adds bash command-line completion support for ``ovs-appctl``, ``ovs-dpctl``, ``ovs-ofctl`` and ``ovsdb-tool`` commands. Features ~~~~~~~~ - Display available completion or complete on unfinished user input (long option, subcommand, and argument). - Subcommand hints - Convert between keywords like ``bridge``, ``port``, ``interface``, or ``dp`` and the available record in ovsdb. Limitations ~~~~~~~~~~~ - Only supports a small set of important keywords (``dp``, ``datapath``, ``bridge``, ``switch``, ``port``, ``interface``, ``iface``). - Does not support parsing of nested options. For example:: $ ovsdb-tool create [db [schema]] - Does not support expansion on repeated argument. For example:: $ ovs-dpctl show [dp...]). - Only supports matching on long options, and only in the format ``--option [arg]``. Do not use ``--option=[arg]``. ovs-vsctl-bashcomp ------------------- ``ovs-vsctl-bashcomp.bash`` adds Bash command-line completion support for ``ovs-vsctl`` command. Features ~~~~~~~~ - Display available completion and complete on user input for global/local options, command, and argument. - Query database and expand keywords like ``table``, ``record``, ``column``, or ``key``, to available completions. - Deal with argument relations like 'one and more', 'zero or one'. - Complete multiple ``ovs-vsctl`` commands cascaded via ``--``. Limitations ~~~~~~~~~~~ Completion of very long ``ovs-vsctl`` commands can take up to several seconds. Usage ----- The bashcomp scripts should be placed at ``/etc/bash_completion.d/`` to be available for all bash sessions. Running ``make install`` will place the scripts to ``$(sysconfdir)/bash_completion.d/``, thus, the user should specify ``--sysconfdir=/etc`` at configuration. If OVS is installed from packages, the scripts will automatically be placed inside ``/etc/bash_completion.d/``. If you just want to run the scripts in one bash, you can remove them from ``/etc/bash_completion.d/`` and run the scripts via ``. ovs-appctl-bashcomp.bash`` or ``. ovs-vsctl-bashcomp.bash``. Tests ----- Unit tests are added in ``tests/completion.at`` and integrated into autotest framework. To run the tests, just run ``make check``. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/debian.rst000066400000000000000000000077731514270232600263640ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================================= Debian Packaging for Open vSwitch ================================= This document describes how to build Debian packages for Open vSwitch. To install Open vSwitch on Debian without building Debian packages, refer to :doc:`general` instead. .. note:: These instructions should also work on Ubuntu and other Debian derivative distributions. Before You Begin ---------------- Before you begin, consider whether you really need to build packages yourself. Debian "wheezy" and "sid", as well as recent versions of Ubuntu, contain pre-built Debian packages for Open vSwitch. It is easier to install these than to build your own. To use packages from your distribution, skip ahead to "Installing .deb Packages", below. Building Open vSwitch Debian packages ------------------------------------- You may build from an Open vSwitch distribution tarball or from an Open vSwitch Git tree with these instructions. You do not need to be the superuser to build the Debian packages. 1. Install the "build-essential" and "fakeroot" packages. For example:: $ apt-get install build-essential fakeroot 2. Obtain and unpack an Open vSwitch source distribution and ``cd`` into its top level directory. 3. Install the build dependencies listed under "Build-Depends:" near the top of ``debian/control.in``. You can install these any way you like, e.g. with ``apt-get install``. 4. Prepare the package source. If you want to build the package with DPDK support execute the following command:: $ ./boot.sh && ./configure --with-dpdk=shared && make debian If not:: $ ./boot.sh && ./configure && make debian Check your work by running ``dpkg-checkbuilddeps`` in the top level of your OVS directory. If you've installed all the dependencies properly, ``dpkg-checkbuilddeps`` will exit without printing anything. If you forgot to install some dependencies, it will tell you which ones. 5. Build the package:: $ make debian-deb 5. The generated .deb files will be in the parent directory of the Open vSwitch source distribution. Installing .deb Packages ------------------------ These instructions apply to installing from Debian packages that you built yourself, as described in the previous section. In this case, use a command such as ``dpkg -i`` to install the .deb files that you build. You will have to manually install any missing dependencies. You can also use these instruction to install from packages provided by Debian or a Debian derivative distribution such as Ubuntu. In this case, use a program such as ``apt-get`` or ``aptitude`` to download and install the provided packages. These programs will also automatically download and install any missing dependencies. .. important:: You must be superuser to install Debian packages. Install the ``openvswitch-switch`` and ``openvswitch-common`` packages. These packages include the core userspace components of the switch. Open vSwitch ``.deb`` packages not mentioned above are rarely useful. Refer to their individual package descriptions to find out whether any of them are useful to you. Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/distributions.rst000066400000000000000000000047271514270232600300400ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ==================================== Distributions packaging Open vSwitch ==================================== This document lists various popular distributions packaging Open vSwitch. Open vSwitch is packaged by various distributions for multiple platforms and architectures. .. note:: The packaged version available with distributions may not be latest Open vSwitch release. Debian / Ubuntu --------------- You can use ``apt-get`` or ``aptitude`` to install the .deb packages and must be superuser. 1. Debian and Ubuntu has ``openvswitch-switch`` and ``openvswitch-common`` packages that includes the core userspace components of the switch. Extra packages for documentation, ipsec, pki, VTEP and Python support are also available. The Open vSwitch kernel datapath is maintained as part of the upstream kernel available in the distribution. 2. For fast userspace switching, Open vSwitch with DPDK support is bundled in the package ``openvswitch-switch-dpdk``. Fedora ------ Fedora provides ``openvswitch``, ``openvswitch-devel``, ``openvswitch-test`` and ``openvswitch-debuginfo`` rpm packages. You can install ``openvswitch`` package in minimum installation. Use ``yum`` or ``dnf`` to install the rpm packages and must be superuser. Red Hat ------- RHEL distributes ``openvswitch`` rpm package that supports kernel datapath. DPDK accelerated Open vSwitch can be installed using ``openvswitch-dpdk`` package. OpenSuSE -------- OpenSUSE provides ``openvswitch``, ``openvswitch-switch`` rpm packages. Also ``openvswitch-dpdk`` and ``openvswitch-dpdk-switch`` can be installed for Open vSwitch using DPDK accelerated datapath. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/documentation.rst000066400000000000000000000060161514270232600300000ustar00rootroot00000000000000.. Copyright (c) 2016 Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================== Open vSwitch Documentation ========================== This document describes how to build the OVS documentation for use offline. A continuously updated, online version can be found at `docs.openvswitch.org `__. .. note:: These instructions provide information on building the documentation locally. For information on writing documentation, refer to :doc:`/internals/contributing/documentation-style` Build Requirements ------------------ As described in the :doc:`/internals/contributing/documentation-style`, the Open vSwitch documentation is written in reStructuredText and built with Sphinx. A detailed guide on installing Sphinx in many environments is available on the `Sphinx website`__ but, for most Linux distributions, you can install with your package manager. For example, on Debian/Ubuntu run:: $ sudo apt-get install python3-sphinx Similarly, on RHEL/Fedora run:: $ sudo dnf install python3-sphinx A ``requirements.txt`` is also provided in the ``/Documentation``, should you wish to install using ``pip``:: $ virtualenv .venv $ source .venv/bin/activate $ pip install -r Documentation/requirements.txt __ http://www.sphinx-doc.org/en/master/usage/installation.html Configuring ----------- It's unlikely that you'll need to customize any aspect of the configuration. However, the ``Documentation/conf.py`` is the go-to place for all configuration. This file is well documented and further information is available on the `Sphinx website`__. Building -------- Once Sphinx is installed, the documentation can be built using the provided Makefile targets:: $ make docs-check .. important:: The ``docs-check`` target will fail if there are any syntax errors. However, it won't catch more succinct issues such as style or grammar issues. As a result, you should always inspect changes visually to ensure the result is as intended. Once built, documentation is available in the ``/Documentation/_build`` folder. Open the root ``index.html`` to browse the documentation. __ http://www.sphinx-doc.org/en/master/config.html openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/dpdk.rst000066400000000000000000000701171514270232600260540ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================== Open vSwitch with DPDK ====================== This document describes how to build and install Open vSwitch using a DPDK datapath. Open vSwitch can use the DPDK library to operate entirely in userspace. .. important:: The :doc:`releases FAQ ` lists support for the required versions of DPDK for each version of Open vSwitch. If building OVS and DPDK outside of the main build tree users should consult this list first. Build requirements ------------------ In addition to the requirements described in :doc:`general`, building Open vSwitch with DPDK will require the following: - DPDK 25.11 - A `DPDK supported NIC`_ Only required when physical ports are in use - A suitable kernel On Linux Distros running kernel version >= 3.0, only `IOMMU` needs to enabled via the grub cmdline, assuming you are using **VFIO**. For older kernels, ensure the kernel is built with ``UIO``, ``HUGETLBFS``, ``PROC_PAGE_MONITOR``, ``HPET``, ``HPET_MMAP`` support. If these are not present, it will be necessary to upgrade your kernel or build a custom kernel with these flags enabled. Detailed system requirements can be found at `DPDK requirements`_. .. _DPDK supported NIC: https://doc.dpdk.org/guides-25.11/nics/index.html .. _DPDK requirements: https://doc.dpdk.org/guides-25.11/linux_gsg/sys_reqs.html .. _dpdk-install: Installing ---------- Install DPDK ~~~~~~~~~~~~ #. Download the `DPDK sources`_, extract the file and set ``DPDK_DIR``:: $ cd /usr/src/ $ wget https://fast.dpdk.org/rel/dpdk-25.11.tar.xz $ tar xf dpdk-25.11.tar.xz $ export DPDK_DIR=/usr/src/dpdk-25.11 $ cd $DPDK_DIR #. Configure and install DPDK using Meson Build and install the DPDK library:: $ export DPDK_BUILD=$DPDK_DIR/build $ meson build $ ninja -C build $ sudo ninja -C build install $ sudo ldconfig Check if libdpdk can be found by pkg-config:: $ pkg-config --modversion libdpdk The above command should return the DPDK version installed. If not found, export the path to the installed DPDK libraries:: $ export PKG_CONFIG_PATH=/path/to/installed/".pc" file/for/DPDK For example, On Fedora 32:: $ export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig Detailed information can be found at `DPDK documentation`_. #. (Optional) Configure and export the DPDK shared library location Since DPDK is built both as static and shared library by default, no extra configuration is required for the build. Exporting the path to library is not necessary if the DPDK libraries are system installed. For libraries installed using a prefix, export the path to this library:: $ export LD_LIBRARY_PATH=/path/to/installed/DPDK/libraries .. note:: Minor performance loss is expected when using OVS with a shared DPDK library compared to a static DPDK library. .. _DPDK sources: http://dpdk.org/rel .. _DPDK documentation: https://doc.dpdk.org/guides-25.11/linux_gsg/build_dpdk.html Install OVS ~~~~~~~~~~~ OVS can be installed using different methods. For OVS to use DPDK, it has to be configured to build against the DPDK library (``--with-dpdk``). .. note:: This section focuses on generic recipe that suits most cases. For distribution specific instructions, refer to one of the more relevant guides. .. _OVS sources: http://openvswitch.org/releases/ #. Ensure the standard OVS requirements, described in :ref:`general-build-reqs`, are installed #. Bootstrap, if required, as described in :ref:`general-bootstrapping` #. Configure the package using the ``--with-dpdk`` flag: If OVS must consume DPDK static libraries (also equivalent to ``--with-dpdk=yes`` ):: $ ./configure --with-dpdk=static If OVS must consume DPDK shared libraries:: $ ./configure --with-dpdk=shared .. note:: While ``--with-dpdk`` is required, you can pass any other configuration option described in :ref:`general-configuring`. .. note:: The AVX512 Datapath Classifier Performance feature is deprecated and will be removed in a future release. It is strongly recommended to build OVS with at least ``-msse4.2`` and ``-mpopcnt`` optimization flags. If these flags are not enabled, the AVX512 optimized DPCLS implementation is not available in the resulting binary. For technical details see the subtable registration code in the ``lib/dpif-netdev-lookup.c`` file. An example that enables the AVX512 optimizations is:: $ ./configure --with-dpdk=static CFLAGS="-Ofast -msse4.2 -mpopcnt" #. Build and install OVS, as described in :ref:`general-building` Additional information can be found in :doc:`general`. .. note:: If you are running using the Fedora or Red Hat package, the Open vSwitch daemon will run as a non-root user. This implies that you must have a working IOMMU. Visit the `RHEL README`__ for additional information. __ https://github.com/openvswitch/ovs/blob/main/rhel/README.RHEL.rst Possible issues when enabling AVX512 ++++++++++++++++++++++++++++++++++++ The enabling of ISA optimized builds requires build-system support. Certain versions of the assembler provided by binutils is known to have AVX512 assembling issues. The binutils versions affected are 2.30 and 2.31. As many distros backport fixes to previous versions of a package, checking the version output of ``as -v`` can err on the side of disabling AVX512. To remedy this, the OVS build system uses a build-time check to see if ``as`` will correctly assemble the AVX512 code. The output of a good version when running the ``./configure`` step of the build process is as follows:: $ checking binutils avx512 assembler checks passing... yes If a bug is detected in the binutils assembler, it would indicate ``no``. Build an updated binutils, or request a backport of this binutils fix commit ``2069ccaf8dc28ea699bd901fdd35d90613e4402a`` to fix the issue. Setup ----- Setup Hugepages ~~~~~~~~~~~~~~~ Allocate a number of 2M Huge pages: - For persistent allocation of huge pages, write to hugepages.conf file in `/etc/sysctl.d`:: $ echo 'vm.nr_hugepages=2048' > /etc/sysctl.d/hugepages.conf - For run-time allocation of huge pages, use the ``sysctl`` utility:: $ sysctl -w vm.nr_hugepages=N # where N = No. of 2M huge pages To verify hugepage configuration:: $ grep HugePages_ /proc/meminfo Mount the hugepages, if not already mounted by default:: $ mount -t hugetlbfs none /dev/hugepages .. note:: The amount of hugepage memory required can be affected by various aspects of the datapath and device configuration. Refer to :doc:`/topics/dpdk/memory` for more details. .. _dpdk-vfio: Setup DPDK devices using VFIO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VFIO is preferred to the UIO driver when using recent versions of DPDK. VFIO support required support from both the kernel and BIOS. For the former, kernel version > 3.6 must be used. For the latter, you must enable VT-d in the BIOS and ensure this is configured via grub. To ensure VT-d is enabled via the BIOS, run:: $ dmesg | grep -e DMAR -e IOMMU If VT-d is not enabled in the BIOS, enable it now. To ensure VT-d is enabled in the kernel, run:: $ cat /proc/cmdline | grep iommu=pt $ cat /proc/cmdline | grep intel_iommu=on If VT-d is not enabled in the kernel, enable it now. Once VT-d is correctly configured, load the required modules and bind the NIC to the VFIO driver:: $ modprobe vfio-pci $ /usr/bin/chmod a+x /dev/vfio $ /usr/bin/chmod 0666 /dev/vfio/* $ $DPDK_DIR/usertools/dpdk-devbind.py --bind=vfio-pci eth1 $ $DPDK_DIR/usertools/dpdk-devbind.py --status Setup OVS ~~~~~~~~~ Open vSwitch should be started as described in :doc:`general` with the exception of ovs-vswitchd, which requires some special configuration to enable DPDK functionality. DPDK configuration arguments can be passed to ovs-vswitchd via the ``other_config`` column of the ``Open_vSwitch`` table. At a minimum, the ``dpdk-init`` option must be set to either ``true`` or ``try``. For example:: $ export PATH=$PATH:/usr/local/share/openvswitch/scripts $ export DB_SOCK=/usr/local/var/run/openvswitch/db.sock $ ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true $ ovs-ctl --no-ovsdb-server --db-sock="$DB_SOCK" start There are many other configuration options, the most important of which are listed below. Defaults will be provided for all values not explicitly set. ``dpdk-init`` Specifies whether OVS should initialize and support DPDK ports. This field can either be ``true`` or ``try``. A value of ``true`` will cause the ovs-vswitchd process to abort on initialization failure. A value of ``try`` will imply that the ovs-vswitchd process should continue running even if the EAL initialization fails. ``dpdk-lcore-mask`` Specifies the CPU cores on which dpdk lcore threads should be spawned and expects hex string (eg '0x123'). ``dpdk-socket-mem`` Comma separated list of memory to pre-allocate from hugepages on specific sockets. If not specified, this option will not be set by default. DPDK default will be used instead. ``dpdk-hugepage-dir`` Directory where hugetlbfs is mounted ``vhost-sock-dir`` Option to set the path to the vhost-user unix socket files. If allocating more than one GB hugepage, you can configure the amount of memory used from any given NUMA nodes. For example, to use 1GB from NUMA node 0 and 0GB for all other NUMA nodes, run:: $ ovs-vsctl --no-wait set Open_vSwitch . \ other_config:dpdk-socket-mem="1024,0" or:: $ ovs-vsctl --no-wait set Open_vSwitch . \ other_config:dpdk-socket-mem="1024" .. note:: Changing any of these options requires restarting the ovs-vswitchd application See the section ``Performance Tuning`` for important DPDK customizations. Validating ---------- DPDK support can be confirmed by validating the ``dpdk_initialized`` boolean value from the ovsdb. A value of ``true`` means that the DPDK EAL initialization succeeded:: $ ovs-vsctl get Open_vSwitch . dpdk_initialized true Additionally, the library version linked to ovs-vswitchd can be confirmed with either the ovs-vswitchd logs, or by running either of the commands:: $ ovs-vswitchd --version ovs-vswitchd (Open vSwitch) 2.9.0 DPDK 17.11.0 $ ovs-vsctl get Open_vSwitch . dpdk_version "DPDK 17.11.0" At this point you can use ovs-vsctl to set up bridges and other Open vSwitch features. Seeing as we've configured DPDK support, we will use DPDK-type ports. For example, to create a userspace bridge named ``br0`` and add two ``dpdk`` ports to it, run:: $ ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev $ ovs-vsctl add-port br0 myportnameone -- set Interface myportnameone \ type=dpdk options:dpdk-devargs=0000:06:00.0 $ ovs-vsctl add-port br0 myportnametwo -- set Interface myportnametwo \ type=dpdk options:dpdk-devargs=0000:06:00.1 DPDK devices will not be available for use until a valid dpdk-devargs is specified. Refer to ovs-vsctl(8) and :doc:`/howto/dpdk` for more details. Performance Tuning ------------------ To achieve optimal OVS performance, the system can be configured and that includes BIOS tweaks, Grub cmdline additions, better understanding of NUMA nodes and apt selection of PCIe slots for NIC placement. .. note:: This section is optional. Once installed as described above, OVS with DPDK will work out of the box. Recommended BIOS Settings ~~~~~~~~~~~~~~~~~~~~~~~~~ .. list-table:: Recommended BIOS Settings :header-rows: 1 * - Setting - Value * - C3 Power State - Disabled * - C6 Power State - Disabled * - MLC Streamer - Enabled * - MLC Spatial Prefetcher - Enabled * - DCU Data Prefetcher - Enabled * - DCA - Enabled * - CPU Power and Performance - Performance * - Memory RAS and Performance Config -> NUMA optimized - Enabled PCIe Slot Selection ~~~~~~~~~~~~~~~~~~~ The fastpath performance can be affected by factors related to the placement of the NIC, such as channel speeds between PCIe slot and CPU or the proximity of PCIe slot to the CPU cores running the DPDK application. Listed below are the steps to identify right PCIe slot. #. Retrieve host details using ``dmidecode``. For example:: $ dmidecode -t baseboard | grep "Product Name" #. Download the technical specification for product listed, e.g: S2600WT2 #. Check the Product Architecture Overview on the Riser slot placement, CPU sharing info and also PCIe channel speeds For example: On S2600WT, CPU1 and CPU2 share Riser Slot 1 with Channel speed between CPU1 and Riser Slot1 at 32GB/s, CPU2 and Riser Slot1 at 16GB/s. Running DPDK app on CPU1 cores and NIC inserted in to Riser card Slots will optimize OVS performance in this case. #. Check the Riser Card #1 - Root Port mapping information, on the available slots and individual bus speeds. In S2600WT slot 1, slot 2 has high bus speeds and are potential slots for NIC placement. Advanced Hugepage Setup ~~~~~~~~~~~~~~~~~~~~~~~ Allocate and mount 1 GB hugepages. - For persistent allocation of huge pages, add the following options to the kernel bootline:: default_hugepagesz=1GB hugepagesz=1G hugepages=N For platforms supporting multiple huge page sizes, add multiple options:: default_hugepagesz= hugepagesz= hugepages=N where: ``N`` number of huge pages requested ``size`` huge page size with an optional suffix ``[kKmMgG]`` - For run-time allocation of huge pages:: $ echo N > /sys/devices/system/node/nodeX/hugepages/hugepages-1048576kB/nr_hugepages where: ``N`` number of huge pages requested ``X`` NUMA Node .. note:: For run-time allocation of 1G huge pages, Contiguous Memory Allocator (``CONFIG_CMA``) has to be supported by kernel, check your Linux distro. Now mount the huge pages, if not already done so:: $ mount -t hugetlbfs -o pagesize=1G none /dev/hugepages Isolate Cores ~~~~~~~~~~~~~ The ``isolcpus`` option can be used to isolate cores from the Linux scheduler. The isolated cores can then be used to dedicatedly run HPC applications or threads. This helps in better application performance due to zero context switching and minimal cache thrashing. To run platform logic on core 0 and isolate cores between 1 and 19 from scheduler, add ``isolcpus=1-19`` to GRUB cmdline. .. note:: It has been verified that core isolation has minimal advantage due to mature Linux scheduler in some circumstances. Compiler Optimizations ~~~~~~~~~~~~~~~~~~~~~~ The default compiler optimization level is ``-O2``. Changing this to more aggressive compiler optimization such as ``-O3 -march=native`` with gcc (verified on 5.3.1) can produce performance gains though not significant. ``-march=native`` will produce optimized code on local machine and should be used when software compilation is done on Testbed. Multiple Poll-Mode Driver Threads ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With pmd multi-threading support, OVS creates one pmd thread for each NUMA node by default, if there is at least one DPDK interface from that NUMA node added to OVS. However, in cases where there are multiple ports/rxq's producing traffic, performance can be improved by creating multiple pmd threads running on separate cores. These pmd threads can share the workload by each being responsible for different ports/rxq's. Assignment of ports/rxq's to pmd threads is done automatically. A set bit in the mask means a pmd thread is created and pinned to the corresponding CPU core. For example, to run pmd threads on core 1 and 2:: $ ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x6 When using dpdk and dpdkvhostuser ports in a bi-directional VM loopback as shown below, spreading the workload over 2 or 4 pmd threads shows significant improvements as there will be more total CPU occupancy available:: NIC port0 <-> OVS <-> VM <-> OVS <-> NIC port 1 Refer to ovs-vswitchd.conf.db(5) for additional information on configuration options. Affinity ~~~~~~~~ For superior performance, DPDK pmd threads and Qemu vCPU threads need to have affinity set accordingly. - PMD thread Affinity A poll mode driver (pmd) thread handles the I/O of all DPDK interfaces assigned to it. A pmd thread shall poll the ports for incoming packets, switch the packets and send to tx port. A pmd thread is CPU bound, and needs to be have affinity set to isolated cores for optimum performance. Even though a PMD thread may exist, the thread only starts consuming CPU cycles if there is at least one receive queue assigned to the pmd. .. note:: On NUMA systems, PCI devices are also local to a NUMA node. Unbound rx queues for a PCI device will be assigned to a pmd on it's local NUMA node if a non-isolated PMD exists on that NUMA node. If not, the queue will be assigned to a non-isolated pmd on a remote NUMA node. This will result in reduced maximum throughput on that device and possibly on other devices assigned to that pmd thread. If such a queue assignment is made a warning message will be logged: "There's no available (non-isolated) pmd thread on numa node N. Queue Q on port P will be assigned to the pmd on core C (numa node N'). Expect reduced performance." Binding PMD threads to cores is described in the above section ``Multiple Poll-Mode Driver Threads``. - QEMU vCPU thread Affinity A VM performing simple packet forwarding or running complex packet pipelines has to ensure that the vCPU threads performing the work has as much CPU occupancy as possible. For example, on a multicore VM, multiple QEMU vCPU threads shall be spawned. When the DPDK ``testpmd`` application that does packet forwarding is invoked, the ``taskset`` command should be used to affinitize the vCPU threads to the dedicated isolated cores on the host system. Enable HyperThreading ~~~~~~~~~~~~~~~~~~~~~ With HyperThreading, or SMT, enabled, a physical core appears as two logical cores. SMT can be utilized to spawn worker threads on logical cores of the same physical core there by saving additional cores. With DPDK, when pinning pmd threads to logical cores, care must be taken to set the correct bits of the ``pmd-cpu-mask`` to ensure that the pmd threads are pinned to SMT siblings. Take a sample system configuration, with 2 sockets, 2 * 10 core processors, HT enabled. This gives us a total of 40 logical cores. To identify the physical core shared by two logical cores, run:: $ cat /sys/devices/system/cpu/cpuN/topology/thread_siblings_list where ``N`` is the logical core number. In this example, it would show that cores ``1`` and ``21`` share the same physical core. Logical cores can be specified in pmd-cpu-masks similarly to physical cores, as described in ``Multiple Poll-Mode Driver Threads``. NUMA/Cluster-on-Die ~~~~~~~~~~~~~~~~~~~ Ideally inter-NUMA datapaths should be avoided where possible as packets will go across QPI and there may be a slight performance penalty when compared with intra NUMA datapaths. On Intel Xeon Processor E5 v3, Cluster On Die is introduced on models that have 10 cores or more. This makes it possible to logically split a socket into two NUMA regions and again it is preferred where possible to keep critical datapaths within the one cluster. It is good practice to ensure that threads that are in the datapath are pinned to cores in the same NUMA area. e.g. pmd threads and QEMU vCPUs responsible for forwarding. If DPDK is built with ``CONFIG_RTE_LIBRTE_VHOST_NUMA=y``, vHost User ports automatically detect the NUMA socket of the QEMU vCPUs and will be serviced by a PMD from the same node provided a core on this node is enabled in the ``pmd-cpu-mask``. ``libnuma`` packages are required for this feature. Binding PMD threads is described in the above section ``Multiple Poll-Mode Driver Threads``. DPDK Physical Port Rx Queues ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: $ ovs-vsctl set Interface options:n_rxq= The above command sets the number of rx queues for DPDK physical interface. The rx queues are assigned to pmd threads on the same NUMA node in a round-robin fashion. .. _dpdk-queues-sizes: DPDK Physical Port Queue Sizes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: $ ovs-vsctl set Interface dpdk0 options:n_rxq_desc= $ ovs-vsctl set Interface dpdk0 options:n_txq_desc= The above command sets the number of rx/tx descriptors that the NIC associated with dpdk0 will be initialised with. Different ``n_rxq_desc`` and ``n_txq_desc`` configurations yield different benefits in terms of throughput and latency for different scenarios. Generally, smaller queue sizes can have a positive impact for latency at the expense of throughput. The opposite is often true for larger queue sizes. Note: increasing the number of rx descriptors eg. to 4096 may have a negative impact on performance due to the fact that non-vectorised DPDK rx functions may be used. This is dependent on the driver in use, but is true for the commonly used i40e and ixgbe DPDK drivers. Exact Match Cache ~~~~~~~~~~~~~~~~~ Each pmd thread contains one Exact Match Cache (EMC). After initial flow setup in the datapath, the EMC contains a single table and provides the lowest level (fastest) switching for DPDK ports. If there is a miss in the EMC then the next level where switching will occur is the datapath classifier. Missing in the EMC and looking up in the datapath classifier incurs a significant performance penalty. If lookup misses occur in the EMC because it is too small to handle the number of flows, its size can be increased. The EMC size can be modified by editing the define ``EM_FLOW_HASH_SHIFT`` in ``lib/dpif-netdev.c``. As mentioned above, an EMC is per pmd thread. An alternative way of increasing the aggregate amount of possible flow entries in EMC and avoiding datapath classifier lookups is to have multiple pmd threads running. Rx Mergeable Buffers ~~~~~~~~~~~~~~~~~~~~ Rx mergeable buffers is a virtio feature that allows chaining of multiple virtio descriptors to handle large packet sizes. Large packets are handled by reserving and chaining multiple free descriptors together. Mergeable buffer support is negotiated between the virtio driver and virtio device and is supported by the DPDK vhost library. This behavior is supported and enabled by default, however in the case where the user knows that rx mergeable buffers are not needed i.e. jumbo frames are not needed, it can be forced off by adding ``mrg_rxbuf=off`` to the QEMU command line options. By not reserving multiple chains of descriptors it will make more individual virtio descriptors available for rx to the guest using dpdkvhost ports and this can improve performance. Output Packet Batching ~~~~~~~~~~~~~~~~~~~~~~ To make advantage of batched transmit functions, OVS collects packets in intermediate queues before sending when processing a batch of received packets. Even if packets are matched by different flows, OVS uses a single send operation for all packets destined to the same output port. Furthermore, OVS is able to buffer packets in these intermediate queues for a configurable amount of time to reduce the frequency of send bursts at medium load levels when the packet receive rate is high, but the receive batch size still very small. This is particularly beneficial for packets transmitted to VMs using an interrupt-driven virtio driver, where the interrupt overhead is significant for the OVS PMD, the host operating system and the guest driver. The ``tx-flush-interval`` parameter can be used to specify the time in microseconds OVS should wait between two send bursts to a given port (default is ``0``). When the intermediate queue fills up before that time is over, the buffered packet batch is sent immediately:: $ ovs-vsctl set Open_vSwitch . other_config:tx-flush-interval=50 This parameter influences both throughput and latency, depending on the traffic load on the port. In general lower values decrease latency while higher values may be useful to achieve higher throughput. Low traffic (``packet rate < 1 / tx-flush-interval``) should not experience any significant latency or throughput increase as packets are forwarded immediately. At intermediate load levels (``1 / tx-flush-interval < packet rate < 32 / tx-flush-interval``) traffic should experience an average latency increase of up to ``1 / 2 * tx-flush-interval`` and a possible throughput improvement. Very high traffic (``packet rate >> 32 / tx-flush-interval``) should experience the average latency increase equal to ``32 / (2 * packet rate)``. Most send batches in this case will contain the maximum number of packets (``32``). A ``tx-burst-interval`` value of ``50`` microseconds has shown to provide a good performance increase in a ``PHY-VM-PHY`` scenario on ``x86`` system for interrupt-driven guests while keeping the latency increase at a reasonable level: https://mail.openvswitch.org/pipermail/ovs-dev/2017-December/341628.html .. note:: Throughput impact of this option significantly depends on the scenario and the traffic patterns. For example: ``tx-burst-interval`` value of ``50`` microseconds shows performance degradation in ``PHY-VM-PHY`` with bonded PHY scenario while testing with ``256 - 1024`` packet flows: https://mail.openvswitch.org/pipermail/ovs-dev/2017-December/341700.html The average number of packets per output batch can be checked in PMD stats:: $ ovs-appctl dpif-netdev/pmd-stats-show Limitations ------------ - Network Interface Firmware requirements: Each release of DPDK is validated against a specific firmware version for a supported Network Interface. New firmware versions introduce bug fixes, performance improvements and new functionality that DPDK leverages. The validated firmware versions are available as part of the release notes for DPDK. It is recommended that users update Network Interface firmware to match what has been validated for the DPDK release. The latest list of validated firmware versions can be found in the `DPDK release notes`_. .. _DPDK release notes: https://doc.dpdk.org/guides-25.11/rel_notes/release_25_11.html - Upper bound MTU: DPDK device drivers differ in how the L2 frame for a given MTU value is calculated e.g. i40e driver includes 2 x vlan headers in MTU overhead, em driver includes 1 x vlan header, ixgbe driver does not include a vlan header in overhead. Currently it is not possible for OVS DPDK to know what upper bound MTU value is supported for a given device. As such OVS DPDK must provision for the case where the L2 frame for a given MTU includes 2 x vlan headers. This reduces the upper bound MTU value for devices that do not include vlan headers in their L2 frames by 8 bytes e.g. ixgbe devices upper bound MTU is reduced from 9710 to 9702. This work around is temporary and is expected to be removed once a method is provided by DPDK to query the upper bound MTU value for a given device. - Flow Control: When using i40e devices (Intel(R) 700 Series) it is recommended to set Link State Change detection to interrupt mode. Otherwise it has been observed that using the default polling mode, flow control changes may not be applied, and flow control states will not be reflected correctly. The issue is under investigation, this is a temporary work around. For information about setting Link State Change detection, refer to :ref:`lsc-detection`. Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/fedora.rst000066400000000000000000000106731514270232600263730ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========================================== Fedora, RHEL 7.x Packaging for Open vSwitch =========================================== This document provides instructions for building and installing Open vSwitch RPM packages on a Fedora Linux host. Instructions for the installation of Open vSwitch on a Fedora Linux host without using RPM packages can be found in the :doc:`general`. These instructions have been tested with Fedora 23, and are also applicable for RHEL 7.x and its derivatives, including CentOS 7.x and Scientific Linux 7.x. Build Requirements ------------------ You will need to install all required packages to build the RPMs. Fedora 41 and newer version use ``dnf5`` by default. Other distributions typically use ``dnf``. If neither ``dnf5`` nor ``dnf`` is available, then use ``yum`` instructions. The command below will install RPM tools and generic build dependencies. And (optionally) include these packages: libcap-ng libcap-ng-devel dpdk-devel. DNF5: :: $ dnf5 install @development-tools rpm-build dnf-plugins-core DNF: :: $ dnf install @'Development Tools' rpm-build dnf-plugins-core YUM: :: $ yum install @'Development Tools' rpm-build yum-utils Then it is necessary to install Open vSwitch specific build dependencies. The dependencies are listed in the SPEC file, but first it is necessary to replace the VERSION tag to be a valid SPEC. The command below will create a temporary SPEC file:: $ sed -e 's/@VERSION@/0.0.1/' rhel/openvswitch-fedora.spec.in \ > /tmp/ovs.spec And to install specific dependencies, use the corresponding tool below. For some of the dependencies on RHEL you may need to add two additional repositories to help yum-builddep, e.g.:: $ subscription-manager repos --enable=rhel-7-server-extras-rpms $ subscription-manager repos --enable=rhel-7-server-optional-rpms or for RHEL 8:: $ subscription-manager repos \ --enable=codeready-builder-for-rhel-8-x86_64-rpms DNF:: $ dnf builddep /tmp/ovs.spec YUM:: $ yum-builddep /tmp/ovs.spec Once that is completed, remove the file ``/tmp/ovs.spec``. Bootstrapping ------------- Refer to :ref:`general-bootstrapping`. Configuring ----------- Refer to :ref:`general-configuring`. Building -------- User Space RPMs ~~~~~~~~~~~~~~~ To build Open vSwitch user-space RPMs, execute the following from the directory in which `./configure` was executed: :: $ make rpm-fedora This will create the RPMs `openvswitch`, `python3-openvswitch`, `openvswitch-test`, `openvswitch-devel` and `openvswitch-debuginfo`. To enable DPDK support in the openvswitch package, the ``--with dpdk`` option can be added: :: $ make rpm-fedora RPMBUILD_OPT="--with dpdk --without check" To enable AF_XDP support in the openvswitch package, the ``--with afxdp`` option can be added: :: $ make rpm-fedora RPMBUILD_OPT="--with afxdp --without check" You can also have the above commands automatically run the Open vSwitch unit tests. This can take several minutes. :: $ make rpm-fedora RPMBUILD_OPT="--with check" Installing ---------- RPM packages can be installed by using the command ``rpm -i``. Package installation requires superuser privileges. In most cases only the `openvswitch` RPM will need to be installed. The `python3-openvswitch`, `openvswitch-test`, `openvswitch-devel`, and `openvswitch-debuginfo` RPMs are optional unless required for a specific purpose. Refer to the `RHEL README`__ for additional usage and configuration information. __ https://github.com/openvswitch/ovs/blob/main/rhel/README.RHEL.rst Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/general.rst000066400000000000000000000525311514270232600265470ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ========================================= Open vSwitch on Linux, FreeBSD and NetBSD ========================================= This document describes how to build and install Open vSwitch on a generic Linux, FreeBSD, or NetBSD host. For specifics around installation on a specific platform, refer to one of the other installation guides listed in :doc:`index`. Obtaining Open vSwitch Sources ------------------------------ The canonical location for Open vSwitch source code is its Git repository, which you can clone into a directory named "ovs" with:: $ git clone https://github.com/openvswitch/ovs.git Cloning the repository leaves the "main" branch initially checked out. This is the right branch for general development. If, on the other hand, if you want to build a particular released version, you can check it out by running a command such as the following from the "ovs" directory:: $ git checkout v2.7.0 The repository also has a branch for each release series. For example, to obtain the latest fixes in the Open vSwitch 2.7.x release series, which might include bug fixes that have not yet been in any released version, you can check it out from the "ovs" directory with:: $ git checkout origin/branch-2.7 If you do not want to use Git, you can also obtain tarballs for Open vSwitch release versions via http://openvswitch.org/download/, or download a ZIP file for any snapshot from the web interface at https://github.com/openvswitch/ovs. .. _general-build-reqs: Build Requirements ------------------ To compile the userspace programs in the Open vSwitch distribution, you will need the following software: - GNU make - A C compiler, such as: - GCC 4.6 or later. - Clang 3.4 or later. - MSVC 2013. Refer to :doc:`windows` for additional Windows build instructions. While OVS may be compatible with other compilers, optimal support for atomic operations may be missing, making OVS very slow (see ``lib/ovs-atomic.h``). - libssl, from OpenSSL, is optional but recommended if you plan to connect the Open vSwitch to an OpenFlow controller. libssl is required to establish confidentiality and authenticity in the connections from an Open vSwitch to an OpenFlow controller. If libssl is installed, then Open vSwitch will automatically build with support for it. - libcap-ng, written by Steve Grubb, is optional but recommended. It is required to run OVS daemons as a non-root user with dropped root privileges. If libcap-ng is installed, then Open vSwitch will automatically build with support for it. - Python 3.7 or later. - Unbound library, from http://www.unbound.net, is optional but recommended if you want to enable ovs-vswitchd and other utilities to use DNS names when specifying OpenFlow and OVSDB remotes. If unbound library is already installed, then Open vSwitch will automatically build with support for it. The environment variable OVS_RESOLV_CONF can be used to specify DNS server configuration file (the default file on Linux is /etc/resolv.conf), and environment variable OVS_UNBOUND_CONF can be used to specify the configuration file for unbound. On Linux, you may use the kernel module distributed with the upstream Linux kernel 3.3 or later. You may also use the userspace-only implementation, at some cost in features and performance. Refer to :doc:`userspace` for details. If you are working from a Git tree or snapshot (instead of from a distribution tarball), or if you modify the Open vSwitch build system or the database schema, you will also need the following software: - Autoconf version 2.63 or later. - Automake version 1.10 or later. - libtool version 2.4 or later. (Older versions might work too.) The datapath tests for userspace and Linux datapaths also rely upon: - pyftpdlib. Version 1.2.0 is known to work. Earlier versions should also work. - netcat. Several common implementations are known to work. - curl. Version 7.47.0 is known to work. Earlier versions should also work. - tftpy. Version 0.6.2 is known to work. Earlier versions should also work. - netstat. Available from various distro specific packages The ovs-vswitchd.conf.db(5) manpage will include an E-R diagram, in formats other than plain text, only if you have the following: - dot from graphviz (http://www.graphviz.org/). If you are going to extensively modify Open vSwitch, consider installing the following to obtain better warnings: - "sparse" version 0.6.4 or later (https://git.kernel.org/pub/scm/devel/sparse/sparse.git/). - GNU make. - clang, version 3.4 or later - flake8 (for Python code) - the python packages listed in "python/test_requirements.txt" (compatible with pip). If they are installed, the pytest-based Python unit tests will be run. You may find the ovs-dev script found in ``utilities/ovs-dev.py`` useful. .. _general-install-reqs: Installation Requirements ------------------------- The machine you build Open vSwitch on may not be the one you run it on. To simply install and run Open vSwitch you require the following software: - Shared libraries compatible with those used for the build. - On Linux, if you want to use the kernel-based datapath (which is the most common use case), then a kernel with a compatible kernel module. The kernel module is distributed with the upstream Linux kernel 3.3 and later. Open vSwitch features and performance can vary based on the kernel version. Refer to :doc:`/faq/releases` for more information. - For optional support of ingress policing on Linux, the "tc" program from iproute2 (part of all major distributions and available at https://wiki.linuxfoundation.org/networking/iproute2). - Python 3.7 or later. On Linux you should ensure that ``/dev/urandom`` exists. To support TAP devices, you must also ensure that ``/dev/net/tun`` exists. .. _general-bootstrapping: Bootstrapping ------------- This step is not needed if you have downloaded a released tarball. If you pulled the sources directly from an Open vSwitch Git tree or got a Git tree snapshot, then run boot.sh in the top source directory to build the "configure" script:: $ ./boot.sh .. _general-configuring: Configuring ----------- Configure the package by running the configure script. You can usually invoke configure without any arguments. For example:: $ ./configure By default all files are installed under ``/usr/local``. Open vSwitch also expects to find its database in ``/usr/local/etc/openvswitch`` by default. If you want to install all files into, e.g., ``/usr`` and ``/var`` instead of ``/usr/local`` and ``/usr/local/var`` and expect to use ``/etc/openvswitch`` as the default database directory, add options as shown here:: $ ./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc .. note:: Open vSwitch installed with packages like .rpm (e.g. via ``yum install`` or ``rpm -ivh``) and .deb (e.g. via ``apt-get install`` or ``dpkg -i``) use the above configure options. By default, static libraries are built and linked against. If you want to use shared libraries instead:: $ ./configure --enable-shared To use a specific C compiler for compiling Open vSwitch user programs, also specify it on the configure command line, like so:: $ ./configure CC=gcc-4.2 To use 'clang' compiler:: $ ./configure CC=clang To supply special flags to the C compiler, specify them as ``CFLAGS`` on the configure command line. If you want the default CFLAGS, which include ``-g`` to build debug symbols and ``-O2`` to enable optimizations, you must include them yourself. For example, to build with the default CFLAGS plus ``-mssse3``, you might run configure as follows:: $ ./configure CFLAGS="-g -O2 -mssse3" For efficient hash computation special flags can be passed to leverage built-in intrinsics. For example on X86_64 with SSE4.2 instruction set support, CRC32 intrinsics can be used by passing ``-msse4.2``:: $ ./configure CFLAGS="-g -O2 -msse4.2"` Also builtin popcnt instruction can be used to speedup the counting of the bits set in an integer. For example on X86_64 with POPCNT support, it can be enabled by passing ``-mpopcnt``:: $ ./configure CFLAGS="-g -O2 -mpopcnt"` If you are on a different processor and don't know what flags to choose, it is recommended to use ``-march=native`` settings:: $ ./configure CFLAGS="-g -O2 -march=native" With this, GCC will detect the processor and automatically set appropriate flags for it. This should not be used if you are compiling OVS outside the target machine. If you are a developer and want to enable Address Sanitizer for debugging purposes, at about a 2x runtime cost, you can add ``-fsanitize=address -fno-omit-frame-pointer -fno-common`` to CFLAGS. For example:: $ ./configure CFLAGS="-g -O2 -fsanitize=address -fno-omit-frame-pointer -fno-common" If you plan to do much Open vSwitch development, you might want to add ``--enable-Werror``, which adds the ``-Werror`` option to the compiler command line, turning warnings into errors. That makes it impossible to miss warnings generated by the build. For example:: $ ./configure --enable-Werror If you're building with GCC, then, for improved warnings, install ``sparse`` (see "Prerequisites") and enable it for the build by adding ``--enable-sparse``. Use this with ``--enable-Werror`` to avoid missing both compiler and ``sparse`` warnings, e.g.:: $ ./configure --enable-Werror --enable-sparse To build with gcov code coverage support, add ``--enable-coverage``:: $ ./configure --enable-coverage The configure script accepts a number of other options and honors additional environment variables. For a full list, invoke configure with the ``--help`` option:: $ ./configure --help You can also run configure from a separate build directory. This is helpful if you want to build Open vSwitch in more than one way from a single source directory, e.g. to try out both GCC and Clang builds. For example:: $ mkdir _gcc && (cd _gcc && ./configure CC=gcc) $ mkdir _clang && (cd _clang && ./configure CC=clang) Under certain loads the ovsdb-server and other components perform better when using the jemalloc memory allocator, instead of the glibc memory allocator. If you wish to link with jemalloc add it to LIBS:: $ ./configure LIBS=-ljemalloc .. note:: Linking Open vSwitch with the jemalloc shared library may not work as expected in certain operating system development environments. You can override the automatic compiler decision to avoid possible linker issues by passing ``-fno-lto`` or ``-fno-builtin`` flag since the jemalloc override standard built-in memory allocation functions such as malloc, calloc, etc. Both options can solve possible jemalloc linker issues with pros and cons for each case, feel free to choose the path that appears best to you. Disabling LTO flag example:: $ ./configure LIBS=-ljemalloc CFLAGS="-g -O2 -fno-lto" Disabling built-in flag example:: $ ./configure LIBS=-ljemalloc CFLAGS="-g -O2 -fno-builtin" .. _general-building: Building -------- 1. Run GNU make in the build directory, e.g.:: $ make or if GNU make is installed as "gmake":: $ gmake If you used a separate build directory, run make or gmake from that directory, e.g.:: $ make -C _gcc $ make -C _clang .. note:: Some versions of Clang and ccache are not completely compatible. If you see unusual warnings when you use both together, consider disabling ccache. 2. Consider running the testsuite. Refer to :doc:`/topics/testing` for instructions. 3. Run ``make install`` to install the executables and manpages into the running system, by default under ``/usr/local``:: $ make install .. _general-starting: Starting -------- On Unix-alike systems, such as BSDs and Linux, starting the Open vSwitch suite of daemons is a simple process. Open vSwitch includes a shell script, and helpers, called ovs-ctl which automates much of the tasks for starting and stopping ovsdb-server, and ovs-vswitchd. After installation, the daemons can be started by using the ovs-ctl utility. This will take care to setup initial conditions, and start the daemons in the correct order. The ovs-ctl utility is located in '$(pkgdatadir)/scripts', and defaults to '/usr/local/share/openvswitch/scripts'. An example after install might be:: $ export PATH=$PATH:/usr/local/share/openvswitch/scripts $ ovs-ctl start Additionally, the ovs-ctl script allows starting / stopping the daemons individually using specific options. To start just the ovsdb-server:: $ export PATH=$PATH:/usr/local/share/openvswitch/scripts $ ovs-ctl --no-ovs-vswitchd start Likewise, to start just the ovs-vswitchd:: $ export PATH=$PATH:/usr/local/share/openvswitch/scripts $ ovs-ctl --no-ovsdb-server start Refer to ovs-ctl(8) for more information on ovs-ctl. In addition to using the automated script to start Open vSwitch, you may wish to manually start the various daemons. Before starting ovs-vswitchd itself, you need to start its configuration database, ovsdb-server. Each machine on which Open vSwitch is installed should run its own copy of ovsdb-server. Before ovsdb-server itself can be started, configure a database that it can use:: $ mkdir -p /usr/local/etc/openvswitch $ ovsdb-tool create /usr/local/etc/openvswitch/conf.db \ vswitchd/vswitch.ovsschema Configure ovsdb-server to use database created above, to listen on a Unix domain socket, to connect to any managers specified in the database itself, and to use the SSL/TLS configuration in the database:: $ mkdir -p /usr/local/var/run/openvswitch $ ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \ --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ --private-key=db:Open_vSwitch,SSL,private_key \ --certificate=db:Open_vSwitch,SSL,certificate \ --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ --pidfile --detach --log-file .. note:: If you built Open vSwitch without SSL/TLS support, then omit ``--private-key``, ``--certificate``, and ``--bootstrap-ca-cert``.) Initialize the database using ovs-vsctl. This is only necessary the first time after you create the database with ovsdb-tool, though running it at any time is harmless:: $ ovs-vsctl --no-wait init Start the main Open vSwitch daemon, telling it to connect to the same Unix domain socket:: $ ovs-vswitchd --pidfile --detach --log-file Starting OVS in container ------------------------- For ovs vswitchd, we need to load ovs kernel modules on host. Hence, OVS containers kernel version needs to be same as that of host kernel. Export following variables in .env and place it under project root:: $ OVS_BRANCH= $ OVS_VERSION= $ DISTRO= $ KERNEL_VERSION= $ GITHUB_SRC= $ DOCKER_REPO= To build ovs modules:: $ cd utilities/docker $ make build Compiled Modules will be tagged with docker image To Push ovs modules:: $ make push OVS docker image will be pushed to specified docker repo. Start ovsdb-server using below command:: $ docker run -itd --net=host --name=ovsdb-server \ : ovsdb-server Start ovs-vswitchd with privileged mode as it needs to load kernel module in host using below command:: $ docker run -itd --net=host --name=ovs-vswitchd \ --volumes-from=ovsdb-server -v /lib:/lib --privileged \ : ovs-vswitchd .. note:: The debian docker file uses ubuntu 16.04 as a base image for reference. User can use any other base image for debian, e.g. u14.04, etc. RHEL based docker build support needs to be added. Validating ---------- At this point you can use ovs-vsctl to set up bridges and other Open vSwitch features. For example, to create a bridge named ``br0`` and add ports ``eth0`` and ``vif1.0`` to it:: $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 vif1.0 Refer to ovs-vsctl(8) for more details. You may also wish to refer to :doc:`/topics/testing` for information on more generic testing of OVS. When using ovs in container, exec to container to run above commands:: $ docker exec -it /bin/bash Upgrading --------- When you upgrade Open vSwitch from one version to another you should also upgrade the database schema: .. note:: The following manual steps may also be accomplished by using ovs-ctl to stop and start the daemons after upgrade. The ovs-ctl script will automatically upgrade the schema. 1. Stop the Open vSwitch daemons, e.g.:: $ kill `cd /usr/local/var/run/openvswitch && cat ovsdb-server.pid ovs-vswitchd.pid` 2. Install the new Open vSwitch release by using the same configure options as was used for installing the previous version. If you do not use the same configure options, you can end up with two different versions of Open vSwitch executables installed in different locations. 3. Upgrade the database, in one of the following two ways: - If there is no important data in your database, then you may delete the database file and recreate it with ovsdb-tool, following the instructions under "Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD". - If you want to preserve the contents of your database, back it up first, then use ``ovsdb-tool convert`` to upgrade it, e.g.:: $ ovsdb-tool convert /usr/local/etc/openvswitch/conf.db \ vswitchd/vswitch.ovsschema 4. Start the Open vSwitch daemons as described under `Starting`_ above. Hot Upgrading ------------- Upgrading Open vSwitch from one version to the next version with minimum disruption of traffic going through the system that is using that Open vSwitch needs some considerations: 1. If the upgrade only involves upgrading the userspace utilities and daemons of Open vSwitch, make sure that the new userspace version is compatible with the previously loaded kernel module. 2. An upgrade of userspace daemons means that they have to be restarted. Restarting the daemons means that the OpenFlow flows in the ovs-vswitchd daemon will be lost. One way to restore the flows is to let the controller re-populate it. Another way is to save the previous flows using a utility like ovs-ofctl and then re-add them after the restart. Restoring the old flows is accurate only if the new Open vSwitch interfaces retain the old 'ofport' values. 3. When the new userspace daemons get restarted, they automatically flush the old flows setup in the kernel. This can be expensive if there are hundreds of new flows that are entering the kernel but userspace daemons are busy setting up new userspace flows from either the controller or an utility like ovs-ofctl. Open vSwitch database provides an option to solve this problem through the ``other_config:flow-restore-wait`` column of the ``Open_vSwitch`` table. Refer to the ovs-vswitchd.conf.db(5) manpage for details. 4. If the upgrade also involves upgrading the kernel module, the old kernel module needs to be unloaded and the new kernel module should be loaded. This means that the kernel network devices belonging to Open vSwitch is recreated and the kernel flows are lost. The downtime of the traffic can be reduced if the userspace daemons are restarted immediately and the userspace flows are restored as soon as possible. 5. When upgrading ovs running in container on host that is managed by ovn, simply stop the docker container, remove and re-run with new docker image that has newer ovs version. 6. When running ovs in container, if ovs is used in bridged mode where management interface is managed by ovs, docker restart will result in loss of network connectivity. Hence, make sure to delete the bridge mapping of physical interface from ovs, upgrade ovs via docker and then add back the interface to ovs bridge. This mapping need not be deleted in case of multi nics if management interface is not managed by ovs. The ovs-ctl utility's ``restart`` function only restarts the userspace daemons, makes sure that the 'ofport' values remain consistent across restarts, restores userspace flows using the ovs-ofctl utility and also uses the ``other_config:flow-restore-wait`` column to keep the traffic downtime to the minimum. The ovs-ctl utility's ``force-reload-kmod`` function does all of the above, but also replaces the old kernel module with the new one. Open vSwitch startup scripts for Debian and RHEL use ovs-ctl's functions and it is recommended that these functions be used for other software platforms too. Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/index.rst000066400000000000000000000034131514270232600262340ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================= Installing Open vSwitch ======================= A collection of guides detailing how to install Open vSwitch in a variety of different environments and using different configurations. Installation from Source ------------------------ .. TODO(stephenfin): Based on the title alone, the NetBSD doc should probably be merged into the general install doc .. toctree:: :maxdepth: 2 general netbsd windows userspace dpdk afxdp Installation from Packages -------------------------- Open vSwitch is packaged on a variety of distributions. The tooling required to build these packages is included in the Open vSwitch tree. The instructions are provided below. .. toctree:: :maxdepth: 2 distributions debian fedora rhel Others ------ .. toctree:: :maxdepth: 2 bash-completion documentation openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/netbsd.rst000066400000000000000000000040141514270232600264020ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ====================== Open vSwitch on NetBSD ====================== On NetBSD, you might want to install requirements from pkgsrc. In that case, you need at least the following packages. - automake - libtool-base - gmake - python37 Some components have additional requirements. Refer to :doc:`general` for more information. Assuming you are running NetBSD/amd64 7.0.2, you can download and install pre-built binary packages as the following:: $ PKG_PATH=http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/7.0.2/All/ $ export PKG_PATH $ pkg_add automake libtool-base gmake python37 pkg_alternatives .. note:: You might get some warnings about minor version mismatch. These can be safely ignored. NetBSD's ``/usr/bin/make`` is not GNU make. GNU make is installed as ``/usr/pkg/bin/gmake`` by the above mentioned ``gmake`` package. As all executables installed with pkgsrc are placed in ``/usr/pkg/bin/`` directory, it might be a good idea to add it to your PATH. Or install OVS by ``gmake`` and ``gmake install``. Open vSwitch on NetBSD is currently "userspace switch" implementation in the sense described in :doc:`userspace` and :doc:`/topics/porting`. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/rhel.rst000066400000000000000000000164371514270232600260710ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================================== RHEL 5.6, 6.x Packaging for Open vSwitch ======================================== This document describes how to build and install Open vSwitch on a Red Hat Enterprise Linux (RHEL) host. If you want to install Open vSwitch on a generic Linux host, refer to :doc:`general` instead. We have tested these instructions with RHEL 5.6 and RHEL 6.0. For RHEL 7.x (or derivatives, such as CentOS 7.x), you should follow the instructions in the :doc:`fedora`. The Fedora spec files are used for RHEL 7.x. .. _rhel-prerequisites: Prerequisites ------------- You may build from an Open vSwitch distribution tarball or from an Open vSwitch Git tree. The default RPM build directory, ``_topdir``, has five directories in the top-level. BUILD/ where the software is unpacked and built RPMS/ where the newly created binary package files are written SOURCES/ contains the original sources, patches, and icon files SPECS/ contains the spec files for each package to be built SRPMS/ where the newly created source package files are written Before you begin, note the RPM sources directory on your version of RHEL. The command ``rpmbuild --showrc`` will show the configuration for each of those directories. Alternatively, the command ``rpm --eval '%{_topdir}'`` shows the current configuration for the top level directory and the command ``rpm --eval '%{_sourcedir}'`` does the same for the sources directory. On RHEL 5, the default RPM ``_topdir`` is ``/usr/src/redhat`` and the default RPM sources directory is ``/usr/src/redhat/SOURCES``. On RHEL 6, the default ``_topdir`` is ``$HOME/rpmbuild`` and the default RPM sources directory is ``$HOME/rpmbuild/SOURCES``. Build Requirements ------------------ You will need to install all required packages to build the RPMs. The command below will install RPM tools and generic build dependencies:: $ yum install @'Development Tools' rpm-build yum-utils Then it is necessary to install Open vSwitch specific build dependencies. The dependencies are listed in the SPEC file, but first it is necessary to replace the VERSION tag to be a valid SPEC. The command below will create a temporary SPEC file:: $ sed -e 's/@VERSION@/0.0.1/' rhel/openvswitch.spec.in > /tmp/ovs.spec And to install specific dependencies, use yum-builddep tool:: $ yum-builddep /tmp/ovs.spec Once that is completed, remove the file ``/tmp/ovs.spec``. If python3-sphinx package is not available in your version of RHEL, you can install it via pip with 'pip install sphinx'. Open vSwitch requires python 3.7 or newer which is not available in older distributions. For those, one option is to build and install required version from source. .. _rhel-bootstrapping: Bootstrapping and Configuring ----------------------------- If you are building from a distribution tarball, skip to :ref:`rhel-building`. If not, you must be building from an Open vSwitch Git tree. Determine what version of Autoconf is installed (e.g. run ``autoconf --version``). If it is not at least version 2.63, then you must upgrade or use another machine to build the packages. Assuming all requirements have been met, build the tarball by running:: $ ./boot.sh $ ./configure $ make dist You must run this on a machine that has the tools listed in :ref:`general-build-reqs` as prerequisites for building from a Git tree. Afterward, proceed with the rest of the instructions using the distribution tarball. Now you have a distribution tarball, named something like ``openvswitch-x.y.z.tar.gz``. Copy this file into the RPM sources directory, e.g.:: $ cp openvswitch-x.y.z.tar.gz $HOME/rpmbuild/SOURCES Broken ``build`` symlink ~~~~~~~~~~~~~~~~~~~~~~~~ Some versions of the RHEL 6 kernel-devel package contain a broken ``build`` symlink. If you are using such a version, you must fix the problem before continuing. To find out whether you are affected, run:: $ cd /lib/modules/ $ ls -l build/ where ```` is the version number of the RHEL 6 kernel. .. note:: The trailing slash in the final command is important. Be sure to include it. If the ``ls`` command produces a directory listing, your kernel-devel package is OK. If it produces a ``No such file or directory`` error, your kernel-devel package is buggy. If your kernel-devel package is buggy, then you can fix it with:: $ cd /lib/modules/ $ rm build $ ln -s /usr/src/kernels/ build where ```` is the name of an existing directory under ``/usr/src/kernels``, whose name should be similar to ```` but may contain some extra parts. Once you have done this, verify the fix with the same procedure you used above to check for the problem. .. _rhel-building: Building -------- You should have a distribution tarball named something like openvswitch-x.y.z.tar.gz. Copy this file into the RPM sources directory:: $ cp openvswitch-x.y.z.tar.gz $HOME/rpmbuild/SOURCES Make another copy of the distribution tarball in a temporary directory. Then unpack the tarball and ``cd`` into its root:: $ tar xzf openvswitch-x.y.z.tar.gz $ cd openvswitch-x.y.z Userspace ~~~~~~~~~ To build Open vSwitch userspace, run:: $ rpmbuild -bb rhel/openvswitch.spec This produces two RPMs: "openvswitch" and "openvswitch-debuginfo". The above command automatically runs the Open vSwitch unit tests. To disable the unit tests, run:: $ rpmbuild -bb --without check rhel/openvswitch.spec .. note:: If the build fails with ``configure: error: source dir /lib/modules/2.6.32-279.el6.x86_64/build doesn't exist`` or similar, then the kernel-devel package is missing or buggy. .. _rhel-script-integrations: Red Hat Network Scripts Integration ----------------------------------- A RHEL host has default firewall rules that prevent any Open vSwitch tunnel traffic from passing through. If a user configures Open vSwitch tunnels like Geneve, GRE, VXLAN, etc., they will either have to manually add iptables firewall rules to allow the tunnel traffic or add it through a startup script Refer to the "enable-protocol" command in the ovs-ctl(8) manpage for more information. In addition, simple integration with Red Hat network scripts has been implemented. Refer to `README.RHEL.rst`__ in the source tree or /usr/share/doc/openvswitch/README.RHEL.rst in the installed openvswitch package for details. __ https://github.com/openvswitch/ovs/blob/main/rhel/README.RHEL.rst Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/userspace.rst000066400000000000000000000077341514270232600271310ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =================================== Open vSwitch without Kernel Support =================================== Open vSwitch can operate, at a cost in performance, entirely in userspace, without assistance from a kernel module. This file explains how to install Open vSwitch in such a mode. This version of Open vSwitch should be built manually with ``configure`` and ``make``. Debian packaging for Open vSwitch is also included, but it has not been recently tested, and so Debian packages are not a recommended way to use this version of Open vSwitch. .. warning:: The userspace-only mode of Open vSwitch without DPDK is considered experimental. It has not been thoroughly tested. Building and Installing ----------------------- The requirements and procedure for building, installing, and configuring Open vSwitch are the same as those given in :doc:`general`. You may omit configuring, building, and installing the kernel module, and the related requirements. On Linux, the userspace switch additionally requires the kernel TUN/TAP driver to be available, either built into the kernel or loaded as a module. If you are not sure, check for a directory named ``/sys/class/misc/tun``. If it does not exist, then attempt to load the module with ``modprobe tun``. The tun device must also exist as ``/dev/net/tun``. If it does not exist, then create ``/dev/net`` (if necessary) with ``mkdir /dev/net``, then create ``/dev/net/tun`` with ``mknod /dev/net/tun c 10 200``. On FreeBSD and NetBSD, the userspace switch additionally requires the kernel tap(4) driver to be available, either built into the kernel or loaded as a module. Using the Userspace Datapath with ovs-vswitchd ---------------------------------------------- To use ovs-vswitchd in userspace mode, create a bridge with ``datapath_type=netdev`` in the configuration database. For example:: $ ovs-vsctl add-br br0 $ ovs-vsctl set bridge br0 datapath_type=netdev $ ovs-vsctl add-port br0 eth0 $ ovs-vsctl add-port br0 eth1 $ ovs-vsctl add-port br0 eth2 ovs-vswitchd will create a TAP device as the bridge's local interface, named the same as the bridge, as well as for each configured internal interface. Currently, on FreeBSD, the functionality required for in-band control support is not implemented. To avoid related errors, you can disable the in-band support with the following command:: $ ovs-vsctl set bridge br0 other_config:disable-in-band=true Firewall Rules -------------- On Linux, when a physical interface is in use by the userspace datapath, packets received on the interface still also pass into the kernel TCP/IP stack. This can cause surprising and incorrect behavior. You can use "iptables" to avoid this behavior, by using it to drop received packets. For example, to drop packets received on eth0:: $ iptables -A INPUT -i eth0 -j DROP $ iptables -A FORWARD -i eth0 -j DROP Other Settings -------------- On NetBSD, depending on your network topology and applications, the following configuration might help. See sysctl(7).:: $ sysctl -w net.inet.ip.checkinterface=1 Reporting Bugs -------------- Report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/install/windows.rst000066400000000000000000001126161514270232600266250ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ======================= Open vSwitch on Windows ======================= .. _windows-build-reqs: Build Requirements ------------------ Open vSwitch on Linux uses autoconf and automake for generating Makefiles. It will be useful to maintain the same build system while compiling on Windows too. One approach is to compile Open vSwitch in a MinGW environment that contains autoconf and automake utilities and then use Visual C++ as a compiler and linker. The following explains the steps in some detail. - Mingw Install Mingw on a Windows machine by following the instructions on `mingw.org `__. This should install mingw at ``C:\Mingw`` and msys at ``C:\Mingw\msys``. Add ``C:\MinGW\bin`` and ``C:\Mingw\msys\1.0\bin`` to PATH environment variable of Windows. You can either use the MinGW installer or the command line utility ``mingw-get`` to install both the base packages and additional packages like automake and autoconf(version 2.68). Also make sure that ``/mingw`` mount point exists. If its not, please add/create the following entry in ``/etc/fstab``:: 'C:/MinGW /mingw'. - Python 3.7 or later. Install the latest Python 3.x from python.org and verify that its path is part of Windows' PATH environment variable. We require that you have pypiwin32 library installed. The library can be installed via pip command: :: $ pip install pypiwin32 - Visual Studio You will need at least Visual Studio 2013 (update 4) to compile userspace binaries. In addition to that, if you want to compile the kernel module you will also need to install Windows Driver Kit (WDK) 8.1 Update or later. To generate the Windows installer you need `WiX Toolset `__ and also be able to build the kernel module. We recommend using the latest Visual Studio version together with the latest WDK installed. It is important to get the Visual Studio related environment variables and to have the $PATH inside the bash to point to the proper compiler and linker. One easy way to achieve this for VS2013 is to get into the "VS2013 x86 Native Tools Command Prompt" (in a default installation of Visual Studio 2013 this can be found under the following location: ``C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts``) and through it enter into the bash shell available from msys by typing ``bash --login``. There is support for generating 64 bit binaries too. To compile under x64, open the "VS2013 x64 Native Tools Command Prompt" (if your current running OS is 64 bit) or "VS2013 x64 Cross Tools Command Prompt" (if your current running OS is not 64 bit) instead of opening its x86 variant. This will point the compiler and the linker to their 64 bit equivalent. If after the above step, a ``which link`` inside MSYS's bash says, ``/bin/link.exe``, rename ``/bin/link.exe`` to something else so that the Visual studio's linker is used. You should also see a 'which sort' report ``/bin/sort.exe``. - PThreads4W For pthread support, install the library, dll and includes of PThreads4W project from `sourceware `__ to a directory (e.g.: ``C:/pthread``). You should add the PThreads4W's dll path (e.g.: ``C:\pthread\bin``) to the Windows' PATH environment variable. - OpenSSL To get SSL support for Open vSwitch on Windows, you will need to install `OpenSSL for Windows `__ Note down the directory where OpenSSL is installed (e.g.: ``C:/OpenSSL-Win64``) for later use. .. note:: Commands prefixed by ``$`` must be run in the Bash shell provided by MinGW. Open vSwitch commands, such as ``ovs-dpctl`` are shown running under the DOS shell (``cmd.exe``), as indicated by the ``>`` prefix, but will also run under Bash. The remainder, prefixed by ``>``, are PowerShell commands and must be run in PowerShell. Install Requirements -------------------- * Share network adaptors We require that you don't disable the "Allow management operating system to share this network adapter" under 'Virtual Switch Properties' > 'Connection type: External network', in the Hyper-V virtual network switch configuration. * Checksum Offloads While there is some support for checksum/segmentation offloads in software, this is still a work in progress. Till the support is complete we recommend disabling TX/RX offloads for both the VM's as well as the Hyper-V. Bootstrapping ------------- This step is not needed if you have downloaded a released tarball. If you pulled the sources directly from an Open vSwitch Git tree or got a Git tree snapshot, then run boot.sh in the top source directory to build the "configure" script: :: $ ./boot.sh .. _windows-configuring: Configuring ----------- Configure the package by running the configure script. You should provide some configure options to choose the right compiler, linker, libraries, Open vSwitch component installation directories, etc. For example: :: $ ./configure CC=./build-aux/cccl LD="$(which link)" \ LIBS="-lws2_32 -lShlwapi -liphlpapi -lwbemuuid -lole32 -loleaut32" \ --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" \ --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" .. note:: By default, the above enables compiler optimization for fast code. For default compiler optimization, pass the ``--with-debug`` configure option. To configure with SSL support, add the requisite additional options: :: $ ./configure CC=./build-aux/cccl LD="`which link`" \ LIBS="-lws2_32 -lShlwapi -liphlpapi -lwbemuuid -lole32 -loleaut32" \ --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" \ --enable-ssl --with-openssl="C:/OpenSSL-Win64" Finally, to the kernel module also: :: $ ./configure CC=./build-aux/cccl LD="`which link`" \ LIBS="-lws2_32 -lShlwapi -liphlpapi -lwbemuuid -lole32 -loleaut32" \ --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" \ --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" \ --enable-ssl --with-openssl="C:/OpenSSL-Win64" \ --with-vstudiotarget="" \ --with-vstudiotargetver="" Possible values for ```` are: ``Debug`` and ``Release`` Possible values for ```` is a comma separated list of target versions to compile among: ``Win8,Win8.1,Win10`` .. note:: You can directly use the Visual Studio 2013 IDE to compile the kernel datapath. Open the ovsext.sln file in the IDE and build the solution. Refer to :doc:`general` for information on additional configuration options. .. _windows-building: Building -------- Once correctly configured, building Open vSwitch on Windows is similar to building on Linux, FreeBSD, or NetBSD. #. Run make for the ported executables in the top source directory, e.g.: :: $ make For faster compilation, you can pass the ``-j`` argument to make. For example, to run 4 jobs simultaneously, run ``make -j4``. .. note:: MSYS 1.0.18 has a bug that causes parallel make to hang. You can overcome this by downgrading to MSYS 1.0.17. A simple way to downgrade is to exit all MinGW sessions and then run the below command from MSVC developers command prompt.: :: > mingw-get upgrade msys-core-bin=1.0.17-1 #. To run all the unit tests in Open vSwitch, one at a time: :: $ make check To run all the unit tests in Open vSwitch, up to 8 in parallel: :: $ make check TESTSUITEFLAGS="-j8" #. To install all the compiled executables on the local machine, run: :: $ make install .. note:: This will install the Open vSwitch executables in ``C:/openvswitch``. You can add ``C:\openvswitch\usr\bin`` and ``C:\openvswitch\usr\sbin`` to Windows' PATH environment variable for easy access. The Kernel Module ~~~~~~~~~~~~~~~~~ If you are building the kernel module, you will need to copy the below files to the target Hyper-V machine. - ``./datapath-windows/x64/Win8.1Debug/package/ovsext.inf`` - ``./datapath-windows/x64/Win8.1Debug/package/OVSExt.sys`` - ``./datapath-windows/x64/Win8.1Debug/package/ovsext.cat`` - ``./datapath-windows/misc/install.cmd`` - ``./datapath-windows/misc/uninstall.cmd`` .. note:: The above path assumes that the kernel module has been built using Windows DDK 8.1 in Debug mode. Change the path appropriately, if a different WDK has been used. Now run ``./uninstall.cmd`` to remove the old extension. Once complete, run ``./install.cmd`` to insert the new one. For this to work you will have to turn on ``TESTSIGNING`` boot option or 'Disable Driver Signature Enforcement' during boot. The following commands can be used: :: > bcdedit /set LOADOPTIONS DISABLE_INTEGRITY_CHECKS > bcdedit /set TESTSIGNING ON > bcdedit /set nointegritychecks ON .. note:: You may have to restart the machine for the settings to take effect. In the Virtual Switch Manager configuration you can enable the Open vSwitch Extension on an existing switch or create a new switch. If you are using an existing switch, make sure to enable the "Allow Management OS" option for VXLAN to work (covered later). The command to create a new switch named 'OVS-Extended-Switch' using a physical NIC named 'Ethernet 1' is: :: PS > New-VMSwitch "OVS-Extended-Switch" -NetAdapterName "Ethernet 1" .. note:: You can obtain the list of physical NICs on the host using 'Get-NetAdapter' command. In the properties of any switch, you should now see "Open vSwitch Extension" under 'Extensions'. Click the check box to enable the extension. An alternative way to do the same is to run the following command: :: PS > Enable-VMSwitchExtension "Open vSwitch Extension" OVS-Extended-Switch .. note:: If you enabled the extension using the command line, a delay of a few seconds has been observed for the change to be reflected in the UI. This is not a bug in Open vSwitch. Generate the Windows installer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To generate the Windows installler run the following command from the top source directory: :: $ make windows_installer .. note:: This will generate the Windows installer in the following location (relative to the top source directory): windows/ovs-windows-installer/bin/Release/OpenvSwitch.msi Starting -------- .. important:: The following steps assume that you have installed the Open vSwitch utilities in the local machine via 'make install'. Before starting ovs-vswitchd itself, you need to start its configuration database, ovsdb-server. Each machine on which Open vSwitch is installed should run its own copy of ovsdb-server. Before ovsdb-server itself can be started, configure a database that it can use: :: > ovsdb-tool create C:\openvswitch\etc\openvswitch\conf.db \ C:\openvswitch\usr\share\openvswitch\vswitch.ovsschema Configure ovsdb-server to use database created above and to listen on a Unix domain socket: :: > ovsdb-server -vfile:info --remote=punix:db.sock --log-file \ --pidfile --detach .. note:: The logfile is created at ``C:/openvswitch/var/log/openvswitch/`` Initialize the database using ovs-vsctl. This is only necessary the first time after you create the database with ovsdb-tool, though running it at any time is harmless: :: > ovs-vsctl --no-wait init .. tip:: If you would later like to terminate the started ovsdb-server, run: :: > ovs-appctl -t ovsdb-server exit Start the main Open vSwitch daemon, telling it to connect to the same Unix domain socket: :: > ovs-vswitchd -vfile:info --log-file --pidfile --detach .. tip:: If you would like to terminate the started ovs-vswitchd, run: :: > ovs-appctl exit .. note:: The logfile is created at ``C:/openvswitch/var/log/openvswitch/`` Validating ---------- At this point you can use ovs-vsctl to set up bridges and other Open vSwitch features. Add bridges ~~~~~~~~~~~ Let's start by creating an integration bridge, ``br-int`` and a PIF bridge, ``br-pif``: :: > ovs-vsctl add-br br-int > ovs-vsctl add-br br-pif .. note:: There's a known bug that running the ovs-vsctl command does not terminate. This is generally solved by having ovs-vswitchd running. If you face the issue despite that, hit Ctrl-C to terminate ovs-vsctl and check the output to see if your command succeeded. Validate that ports are added by dumping from both ovs-dpctl and ovs-vsctl: :: > ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 2: br-pif (internal) <<< internal port on 'br-pif' bridge port 1: br-int (internal) <<< internal port on 'br-int' bridge > ovs-vsctl show a56ec7b5-5b1f-49ec-a795-79f6eb63228b Bridge br-pif Port br-pif Interface br-pif type: internal Bridge br-int Port br-int Interface br-int type: internal .. note:: There's a known bug that the ports added to OVSDB via ovs-vsctl don't get to the kernel datapath immediately, ie. they don't show up in the output of ``ovs-dpctl show`` even though they show up in output of ``ovs-vsctl show``. In order to workaround this issue, restart ovs-vswitchd. (You can terminate ovs-vswitchd by running ``ovs-appctl exit``.) Add physicals NICs (PIF) ~~~~~~~~~~~~~~~~~~~~~~~~ Now, let's add the physical NIC and the internal port to ``br-pif``. In OVS for Hyper-V, we use the name of the adapter on top of which the Hyper-V virtual switch was created, as a special name to refer to the physical NICs connected to the Hyper-V switch, e.g. if we created the Hyper-V virtual switch on top of the adapter named ``Ethernet0``, then in OVS we use that name (``Ethernet0``) as a special name to refer to that adapter. .. note:: We assume that the OVS extension is enabled Hyper-V switch. Internal ports are the virtual adapters created on the Hyper-V switch using the ``ovs-vsctl add-br `` command. By default they are created under the following rule "" and the adapters are disabled. One needs to enable them and set the corresponding values to it to make them IP-able. As a whole example, if we issue the following in a powershell console: :: PS > Get-NetAdapter | select Name,InterfaceDescription Name InterfaceDescription ---- -------------------- Ethernet1 Intel(R) PRO/1000 MT Network Connection br-pif Hyper-V Virtual Ethernet Adapter #2 Ethernet0 Intel(R) PRO/1000 MT Network Connection #2 br-int Hyper-V Virtual Ethernet Adapter #3 PS > Get-VMSwitch Name SwitchType NetAdapterInterfaceDescription ---- ---------- ------------------------------ external External Intel(R) PRO/1000 MT Network Connection #2 We can see that we have a switch(external) created upon adapter name 'Ethernet0' with the internal ports under name 'br-pif' and 'br-int'. Thus resulting into the following ovs-vsctl commands: :: > ovs-vsctl add-port br-pif Ethernet0 Dumping the ports should show the additional ports that were just added: :: > ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 2: br-pif (internal) <<< internal port adapter on Hyper-V switch port 1: br-int (internal) <<< internal port adapter on Hyper-V switch port 3: Ethernet0 <<< Physical NIC > ovs-vsctl show a56ec7b5-5b1f-49ec-a795-79f6eb63228b Bridge br-pif Port br-pif Interface br-pif type: internal Port "Ethernet0" Interface "Ethernet0" Bridge br-int Port br-int Interface br-int type: internal Add virtual interfaces (VIFs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adding VIFs to Open vSwitch is a two step procedure. The first step is to assign a 'OVS port name' which is a unique name across all VIFs on this Hyper-V. The next step is to add the VIF to the ovsdb using its 'OVS port name' as key. First, assign a unique 'OVS port name' to the VIF. The VIF needs to have been disconnected from the Hyper-V switch before assigning a 'OVS port name' to it. In the example below, we assign a 'OVS port name' called ``ovs-port-a`` to a VIF on a VM ``VM1``. By using index 0 for ``$vnic``, the first VIF of the VM is being addressed. After assigning the name ``ovs-port-a``, the VIF is connected back to the Hyper-V switch with name ``OVS-HV-Switch``, which is assumed to be the Hyper-V switch with OVS extension enabled.: :: PS > import-module .\datapath-windows\misc\OVS.psm1 PS > $vnic = Get-VMNetworkAdapter PS > Disconnect-VMNetworkAdapter -VMNetworkAdapter $vnic[0] PS > $vnic[0] | Set-VMNetworkAdapterOVSPort -OVSPortName ovs-port-a PS > Connect-VMNetworkAdapter -VMNetworkAdapter $vnic[0] \ -SwitchName OVS-Extended-Switch Next, add the VIFs to ``br-int``: :: > ovs-vsctl add-port br-int ovs-port-a Dumping the ports should show the additional ports that were just added: :: > ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 4: ovs-port-a port 2: br-pif (internal) port 1: br-int (internal port 3: Ethernet0 > ovs-vsctl show 4cd86499-74df-48bd-a64d-8d115b12a9f2 Bridge br-pif Port "vEthernet (external)" Interface "vEthernet (external)" Port "Ethernet0" Interface "Ethernet0" Port br-pif Interface br-pif type: internal Bridge br-int Port br-int Interface br-int type: internal Port "ovs-port-a" Interface "ovs-port-a" Add multiple NICs to be managed by OVS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To leverage support of multiple NICs into OVS, we will be using the MSFT cmdlets for forwarding team extension. More documentation about them can be found at technet_. .. _technet: https://technet.microsoft.com/en-us/library/jj553812%28v=wps.630%29.aspx For example, to set up a switch team combined from ``Ethernet0 2`` and ``Ethernet1 2`` named ``external``: :: PS > Get-NetAdapter Name InterfaceDescription ---- -------------------- br-int Hyper-V Virtual Ethernet Adapter #3 br-pif Hyper-V Virtual Ethernet Adapter #2 Ethernet3 2 Intel(R) 82574L Gigabit Network Co...#3 Ethernet2 2 Intel(R) 82574L Gigabit Network Co...#4 Ethernet1 2 Intel(R) 82574L Gigabit Network Co...#2 Ethernet0 2 Intel(R) 82574L Gigabit Network Conn... PS > New-NetSwitchTeam -Name external -TeamMembers "Ethernet0 2","Ethernet1 2" PS > Get-NetSwitchTeam Name : external Members : {Ethernet1 2, Ethernet0 2} This will result in a new adapter bound to the host called ``external``: :: PS > Get-NetAdapter Name InterfaceDescription ---- -------------------- br-test Hyper-V Virtual Ethernet Adapter #4 br-pif Hyper-V Virtual Ethernet Adapter #2 external Microsoft Network Adapter Multiplexo... Ethernet3 2 Intel(R) 82574L Gigabit Network Co...#3 Ethernet2 2 Intel(R) 82574L Gigabit Network Co...#4 Ethernet1 2 Intel(R) 82574L Gigabit Network Co...#2 Ethernet0 2 Intel(R) 82574L Gigabit Network Conn... Next we will set up the Hyper-V VMSwitch on the new adapter ``external``: :: PS > New-VMSwitch -Name external -NetAdapterName external \ -AllowManagementOS $false Under OVS the adapters under the team ``external``, ``Ethernet0 2`` and ``Ethernet1 2``, can be added either under a bond device or separately. The following example shows how the bridges look with the NICs being separated: :: > ovs-vsctl show 6cd9481b-c249-4ee3-8692-97b399dd29d8 Bridge br-test Port br-test Interface br-test type: internal Port "Ethernet1 2" Interface "Ethernet1 2" Bridge br-pif Port "Ethernet0 2" Interface "Ethernet0 2" Port br-pif Interface br-pif type: internal Add patch ports and configure VLAN tagging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Windows Open vSwitch implementation support VLAN tagging in the switch. Switch VLAN tagging along with patch ports between ``br-int`` and ``br-pif`` is used to configure VLAN tagging functionality between two VMs on different Hyper-Vs. To start, add a patch port from ``br-int`` to ``br-pif``: :: > ovs-vsctl add-port br-int patch-to-pif > ovs-vsctl set interface patch-to-pif type=patch \ options:peer=patch-to-int Add a patch port from ``br-pif`` to ``br-int``: :: > ovs-vsctl add-port br-pif patch-to-int > ovs-vsctl set interface patch-to-int type=patch \ options:peer=patch-to-pif Re-Add the VIF ports with the VLAN tag: :: > ovs-vsctl add-port br-int ovs-port-a tag=900 > ovs-vsctl add-port br-int ovs-port-b tag=900 Add tunnels ~~~~~~~~~~~ #. IPv4 tunnel, e.g.: The Windows Open vSwitch implementation supports VXLAN and Geneve tunnels. To add tunnels. For example, first add the tunnel port between 172.168.201.101 <->172.168.201.102: :: > ovs-vsctl add-port br-int tun-1 > ovs-vsctl set Interface tun-1 type= > ovs-vsctl set Interface tun-1 options:local_ip=172.168.201.101 > ovs-vsctl set Interface tun-1 options:remote_ip=172.168.201.102 > ovs-vsctl set Interface tun-1 options:in_key=flow > ovs-vsctl set Interface tun-1 options:out_key=flow ...and the tunnel port between 172.168.201.101 <-> 172.168.201.105: :: > ovs-vsctl add-port br-int tun-2 > ovs-vsctl set Interface tun-2 type= > ovs-vsctl set Interface tun-2 options:local_ip=172.168.201.102 > ovs-vsctl set Interface tun-2 options:remote_ip=172.168.201.105 > ovs-vsctl set Interface tun-2 options:in_key=flow > ovs-vsctl set Interface tun-2 options:out_key=flow Where ```` is one of: ``geneve`` or ``vxlan`` .. note:: Any patch ports created between br-int and br-pif MUST be deleted prior to adding tunnels. #. IPv6 tunnel, e.g.: To add IPV6 Geneve tunnels. For example, add the tunnel port between 5000::2 <-> 5000::9. :: > ovs-vsctl add-port br-int tun-3 -- set interface tun-3 type=Geneve \ options:csum=true options:key=flow options:local_ip="5000::2"\ options:remote_ip=flow add the tunnel port between 5000::2 <-> 5000::9 > ovs-ofctl add-flow br-int "table=0,priority=100,ipv6,ipv6_src=6000::2 \ actions=load:0x9->NXM_NX_TUN_IPV6_DST[0..63], \ load:0x5000000000000000->NXM_NX_TUN_IPV6_DST[64..127], output:tun-3" add the specified flow from 6000::2 go via IPV6 Geneve tunnel .. note:: Till the checksum offload support is complete we recommend disabling TX/RX offloads for IPV6 on Windows VM. Add conntrack for ipv6 ~~~~~~~~~~~~~~~~~~~~~~ The Windows Open vSwitch implementation support conntrack ipv6. To use the conntrack ipv6. Using the following commands. Take tcp6(replace Protocol to icmp6, udp6 to other protocol) for example. :: normal scenario Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3) Vif38Name="podvif38" Vif40Name="podvif40" Vif38Port=2 Vif38Address="20::1" Vif40Port=3 Vif40Address="20::2" Vif40MacAddressCli="00-15-5D-F0-01-0C" Vif38MacAddressCli="00-15-5D-F0-01-0b" Protocol="tcp6" > netsh int ipv6 set neighbors $Vif38Name $Vif40Address \ Vif40MacAddressCli > netsh int ipv6 set neighbors $Vif40Name $Vif38Address \ $Vif38MacAddressCli > ovs-ofctl del-flows --strict br-int "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0,priority=1,ip6, \ ipv6_dst=$Vif40Address,$Protocol,actions=ct(table=1)" > ovs-ofctl add-flow br-int "table=0,priority=1,ip6, \ ipv6_dst=$Vif38Address,$Protocol,actions=ct(table=1)" > ovs-ofctl add-flow br-int "table=1,priority=1,ip6,ct_state=+new+trk, \ $Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=2,ip6, \ ct_state=-new+rpl+trk,$Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=1,ip6, \ ct_state=+trk+est-new,$Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6, \ ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6, \ ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port" :: nat scenario Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4) Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually. Vif38Name="podvif38" Vif42Name="podvif42" Vif38Ip="20::1" Vif38Port=2 Vif42Port=4 NatAddress="20::9" NatMacAddress="aa:bb:cc:dd:ee:ff" NatMacAddressForCli="aa-bb-cc-dd-ee-ff" Vif42Ip="21::3" Vif38MacAddress="00:15:5D:F0:01:0B" Vif38MacAddressCli="00-15-5D-F0-01-0B" Vif42MacAddress="00:15:5D:F0:01:0D" Protocol="tcp6" > netsh int ipv6 set neighbors $Vif38Name $NatAddress \ $NatMacAddressForCli > netsh int ipv6 set neighbors $Vif42Name $Vif38Ip \ $Vif38MacAddressCli > ovs-ofctl del-flows --strict br-int "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0, priority=2,ipv6, \ dl_dst=$NatMacAddress,ct_state=-trk,$Protocol \ actions=ct(table=1,zone=456,nat)" > ovs-ofctl add-flow br-int "table=0, priority=1,ipv6,ct_state=-trk, \ ip6,$Protocol actions=ct(nat, zone=456,table=1)" > ovs-ofctl add-flow br-int "table=1, ipv6,in_port=$Vif38Port, \ ipv6_dst=$NatAddress,$Protocol,ct_state=+trk+new, \ actions=ct(commit,nat(dst=$Vif42Ip),zone=456, \ exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress, \ mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+dnat,$Protocol, \ action=resubmit(,2)" > ovs-ofctl add-flow br-int "table=1, ipv6,ct_state=+trk+snat, \ $Protocol, action=resubmit(,2)" > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif38Port, \ ipv6_dst=$Vif42Ip,$Protocol, actions=mod_dl_src=$NatMacAddress, \ mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" > ovs-ofctl add-flow br-int "table=2, ipv6,in_port=$Vif42Port, \ ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol, \ actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress, \ output:$Vif38Port" Ftp is a specific protocol, it contains an related flow, we need to match is related state. :: normal scenario Vif38(20::1, ofport:2)->Vif40(20:2, ofport:3) Vif38Name="podvif70" Vif40Name="Ethernet1" Vif38Port=2 Vif38Address="20::88" Vif40Port=3 Vif40Address="20::45" Vif40MacAddressCli="00-50-56-98-9d-97" Vif38MacAddressCli="00-15-5D-F0-01-0B" Protocol="tcp6" > netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli > netsh int ipv6 set neighbors $Vif42Name $Vif38Ip $Vif38MacAddressCli > ovs-ofctl del-flows br-int --strict "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0,priority=1,$Protocol actions=ct(table=1)" > ovs-ofctl add-flow br-int "table=1,priority=1,tp_dst=21, $Protocol,\ actions=ct(commit,table=2,alg=ftp)" > ovs-ofctl add-flow br-int "table=1,priority=1,tp_src=21, $Protocol,\ actions=ct(commit,table=2,alg=ftp)" > ovs-ofctl add-flow br-int "table=1,priority=1, ct_state=+new+trk+rel,\ $Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=1, \ ct_state=-new+trk+est+rel,$Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,\ ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,\ ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port" :: nat scenario Vif38(20::1, ofport:2) -> nat address(20::9) -> Vif42(21::3, ofport:4) Due to not construct flow to return neighbor mac address, we set the neighbor mac address manually Vif38Name="podvif70" Vif42Name="Ethernet1" Vif38Ip="20::88" Vif38Port=2 Vif42Port=3 NatAddress="20::9" NatMacAddress="aa:bb:cc:dd:ee:ff" NatMacAddressForCli="aa-bb-cc-dd-ee-ff" Vif42Ip="21::3" Vif38MacAddress="00:15:5D:F0:01:14" Vif38MacAddressCli="00-15-5D-F0-01-14" Vif42MacAddress="00:50:56:98:9d:97" Protocol="tcp6" netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli netsh int ipv6 set neighbors $Vif42Name $Vif38Ip $Vif38MacAddressCli > ovs-ofctl del-flows br-int --strict "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0,priority=2,ipv6,ipv6_dst=$NatAddress,\ ct_state=-trk,$Protocol actions=ct(table=1,zone=456)" > ovs-ofctl add-flow br-int "table=0,priority=1,ipv6,ipv6_dst=$Vif38Ip,\ ct_state=-trk,ip6,$Protocol actions=ct(zone=456,table=1)" > ovs-ofctl add-flow br-int "table=1,priority=2,ipv6,in_port=$Vif38Port,\ ipv6_dst=$NatAddress,ct_state=+trk-rel,tp_dst=21,$Protocol \ actions=ct(commit,alg=ftp,nat(dst=$Vif42Ip),zone=456, \ exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,\ mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" > ovs-ofctl add-flow br-int "table=1,priority=1,ipv6,ct_state=+trk-rel,\ ipv6_dst=$Vif38Ip,$Protocol,action=ct(nat,alg=ftp,zone=456,table=2)" > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+rel,\ ipv6_dst=$NatAddress,$Protocol,\ action=ct(table=2,commit,nat(dst=$Vif42Ip),\ zone=456, exec(set_field:1->ct_mark))" > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+rel,$Protocol,\ ipv6_dst=$Vif38Ip, action=ct(nat,zone=456,table=2)" > ovs-ofctl add-flow br-int "table=2,ipv6,ipv6_dst=$Vif42Ip,$Protocol,\ actions=mod_dl_src=$NatMacAddress, mod_dl_dst=$Vif42MacAddress,\ output:$Vif42Port" > ovs-ofctl add-flow br-int "table=2,ipv6,ipv6_dst=$Vif38Ip,\ ct_state=-new+est,ct_mark=1,ct_zone=456,$Protocol,\ actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif38MacAddress,\ output:$Vif38Port" > ovs-ofctl add-flow br-int "table=2,ipv6,ipv6_dst=$Vif38Ip,\ ct_state=+new,ct_mark=1,ct_zone=456,$Protocol,\ actions=mod_dl_src=$NatMacAddress,\ mod_dl_dst=$Vif38MacAddress, output:$Vif38Port" Tftp same with ftp, it also contains a related connection, we could use following follow test the tftp connection. :: normal scenario Vif38Name="podvif70" Vif40Name="Ethernet1" Vif38Port=2 Vif38Address="20::88" Vif40Port=3 Vif40Address="20::45" Vif40MacAddressCli="00-50-56-98-9d-97" Vif38MacAddressCli="00-15-5D-F0-01-14" Protocol="udp6" netsh int ipv6 set neighbors $Vif38Name $Vif40Address $Vif40MacAddressCli netsh int ipv6 set neighbors $Vif40Name $Vif38Address $Vif38MacAddressCli > ovs-ofctl del-flows br-int --strict "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0,priority=1,$Protocol, ipv6_src=$Vif38Address actions=ct(table=1)" > ovs-ofctl add-flow br-int "table=0,priority=1,$Protocol, ipv6_src=$Vif40Address actions=ct(table=1)" > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=+new+trk-est, tp_dst=69,$Protocol,udp6 actions=ct(commit,alg=tftp,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est-rel,\ udp6 $Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=-new+trk+est+rel,\ $Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=1,priority=1,ct_state=+new+trk+rel,\ $Protocol,actions=ct(commit,table=2)" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,\ ipv6_dst=$Vif38Address,$Protocol,actions=output:$Vif38Port" > ovs-ofctl add-flow br-int "table=2,priority=1,ip6,\ ipv6_dst=$Vif40Address,$Protocol,actions=output:$Vif40Port" :: nat scenario Vif38Name="podvif70" Vif42Name="Ethernet1" Vif38Ip="20::88" Vif38Port=2 Vif42Port=3 NatAddress="20::9" NatMacAddress="aa:bb:cc:dd:ee:ff" NatMacAddressForCli="aa-bb-cc-dd-ee-ff" Vif42Ip="21::3" Vif38MacAddress="00:15:5D:F0:01:14" Vif38MacAddressCli="00-15-5D-F0-01-14" Vif42MacAddress="00:50:56:98:9d:97" Protocol="ip6" netsh int ipv6 set neighbors $Vif38Name $NatAddress $NatMacAddressForCli netsh int ipv6 set neighbors $Vif42Name $Vif38Ip $Vif38MacAddressCli > ovs-ofctl del-flows br-int --strict "table=0,priority=0" > ovs-ofctl add-flow br-int "table=0,priority=2,ipv6,\ dl_dst=$NatMacAddress,ct_state=-trk,$Protocol \ actions=ct(table=1,zone=456)" > ovs-ofctl add-flow br-int "table=0,priority=1,ipv6,ct_state=-trk,ip6,\ $Protocol actions=ct(table=1,zone=456)" > ovs-ofctl add-flow br-int "table=1,in_port=$Vif38Port,\ ipv6_dst=$NatAddress,ct_state=+trk+new-rel,$Protocol,udp6\ actions=ct(commit,alg=tftp,nat(dst=$Vif42Ip),zone=456,\ exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,\ mod_dl_dst=$Vif42MacAddress,output:$Vif42Port" > ovs-ofctl add-flow br-int "table=1,ipv6,in_port=$Vif42Port,\ ipv6_dst=$Vif38Ip,ct_state=+trk+rel-rpl,$Protocol\ actions=ct(commit,nat(src=$NatAddress),zone=456,\ exec(set_field:1->ct_mark)),mod_dl_src=$NatMacAddress,\ mod_dl_dst=$Vif38MacAddress,output:$Vif38Port" > ovs-ofctl add-flow br-int "table=1,ipv6,ct_state=+trk+rel+est+rpl,\ $Protocol,action=ct(nat,table=2,zone=456)" > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif38Port,\ ct_state=+rel+dnat,ipv6_dst=$Vif42Ip,$Protocol,\ actions=mod_dl_src=$NatMacAddress,mod_dl_dst=$Vif42MacAddress,\ output:$Vif42Port" > ovs-ofctl add-flow br-int "table=2,ipv6,in_port=$Vif42Port,\ ct_state=-new+est,$Protocol,actions=mod_dl_src=$NatMacAddress,\ mod_dl_dst=$Vif38MacAddress,output:$Vif38Port" .. note:: Till the checksum offload support is complete we recommend disabling TX/RX offloads for IPV6 on Windows VM. Windows Services ---------------- Open vSwitch daemons come with support to run as a Windows service. The instructions here assume that you have installed the Open vSwitch utilities and daemons via ``make install``. To start, create the database: :: > ovsdb-tool create C:/openvswitch/etc/openvswitch/conf.db \ "C:/openvswitch/usr/share/openvswitch/vswitch.ovsschema" Create the ovsdb-server service and start it: :: > sc create ovsdb-server \ binpath="C:/openvswitch/usr/sbin/ovsdb-server.exe \ C:/openvswitch/etc/openvswitch/conf.db \ -vfile:info --log-file --pidfile \ --remote=punix:db.sock --service --service-monitor" > sc start ovsdb-server .. tip:: One of the common issues with creating a Windows service is with mungled paths. You can make sure that the correct path has been registered with the Windows services manager by running: :: > sc qc ovsdb-server Check that the service is healthy by running: :: > sc query ovsdb-server Initialize the database: :: > ovs-vsctl --no-wait init Create the ovs-vswitchd service and start it: :: > sc create ovs-vswitchd \ binpath="C:/openvswitch/usr/sbin/ovs-vswitchd.exe \ --pidfile -vfile:info --log-file --service --service-monitor" > sc start ovs-vswitchd Check that the service is healthy by running: :: > sc query ovs-vswitchd To stop and delete the services, run: :: > sc stop ovs-vswitchd > sc stop ovsdb-server > sc delete ovs-vswitchd > sc delete ovsdb-server Windows CI Service ------------------ `AppVeyor `__ provides a free Windows autobuild service for open source projects. Open vSwitch has integration with AppVeyor for continuous build. A developer can build test his changes for Windows by logging into appveyor.com using a github account, creating a new project by linking it to his development repository in github and triggering a new build. TODO ---- * Investigate the working of sFlow on Windows and re-enable the unit tests. * Investigate and add the feature to provide QoS. * Sign the driver. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/what-is-ovs.rst000066400000000000000000000024371514270232600256450ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ===================== What Is Open vSwitch? ===================== .. image:: ../_static/overview.png :align: center Overview -------- .. NOTE(stephenfin): The below start-after/end-before may need to be updated if the README is modified. .. include:: ../../README.rst :start-after: What is Open vSwitch? :end-before: What other documentation is available? openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/intro/why-ovs.rst000066400000000000000000000144071514270232600251000ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================= Why Open vSwitch? ================= Hypervisors need the ability to bridge traffic between VMs and with the outside world. On Linux-based hypervisors, this used to mean using the built-in L2 switch (the Linux bridge), which is fast and reliable. So, it is reasonable to ask why Open vSwitch is used. The answer is that Open vSwitch is targeted at multi-server virtualization deployments, a landscape for which the previous stack is not well suited. These environments are often characterized by highly dynamic end-points, the maintenance of logical abstractions, and (sometimes) integration with or offloading to special purpose switching hardware. The following characteristics and design considerations help Open vSwitch cope with the above requirements. The mobility of state --------------------- All network state associated with a network entity (say a virtual machine) should be easily identifiable and migratable between different hosts. This may include traditional "soft state" (such as an entry in an L2 learning table), L3 forwarding state, policy routing state, ACLs, QoS policy, monitoring configuration (e.g. NetFlow, IPFIX, sFlow), etc. Open vSwitch has support for both configuring and migrating both slow (configuration) and fast network state between instances. For example, if a VM migrates between end-hosts, it is possible to not only migrate associated configuration (SPAN rules, ACLs, QoS) but any live network state (including, for example, existing state which may be difficult to reconstruct). Further, Open vSwitch state is typed and backed by a real data-model allowing for the development of structured automation systems. Responding to network dynamics ------------------------------ Virtual environments are often characterized by high-rates of change. VMs coming and going, VMs moving backwards and forwards in time, changes to the logical network environments, and so forth. Open vSwitch supports a number of features that allow a network control system to respond and adapt as the environment changes. This includes simple accounting and visibility support such as NetFlow, IPFIX, and sFlow. But perhaps more useful, Open vSwitch supports a network state database (OVSDB) that supports remote triggers. Therefore, a piece of orchestration software can "watch" various aspects of the network and respond if/when they change. This is used heavily today, for example, to respond to and track VM migrations. Open vSwitch also supports OpenFlow as a method of exporting remote access to control traffic. There are a number of uses for this including global network discovery through inspection of discovery or link-state traffic (e.g. LLDP, CDP, OSPF, etc.). Maintenance of logical tags ---------------------------- Distributed virtual switches (such as VMware vDS and Cisco's Nexus 1000V) often maintain logical context within the network through appending or manipulating tags in network packets. This can be used to uniquely identify a VM (in a manner resistant to hardware spoofing), or to hold some other context that is only relevant in the logical domain. Much of the problem of building a distributed virtual switch is to efficiently and correctly manage these tags. Open vSwitch includes multiple methods for specifying and maintaining tagging rules, all of which are accessible to a remote process for orchestration. Further, in many cases these tagging rules are stored in an optimized form so they don't have to be coupled with a heavyweight network device. This allows, for example, thousands of tagging or address remapping rules to be configured, changed, and migrated. In a similar vein, Open vSwitch supports a GRE implementation that can handle thousands of simultaneous GRE tunnels and supports remote configuration for tunnel creation, configuration, and tear-down. This, for example, can be used to connect private VM networks in different data centers. Hardware integration -------------------- Open vSwitch's forwarding path (the in-kernel datapath) is designed to be amenable to "offloading" packet processing to hardware chipsets, whether housed in a classic hardware switch chassis or in an end-host NIC. This allows for the Open vSwitch control path to be able to both control a pure software implementation or a hardware switch. There are many ongoing efforts to port Open vSwitch to hardware chipsets. These include multiple merchant silicon chipsets (Broadcom and Marvell), as well as a number of vendor-specific platforms. The "Porting" section in the documentation discusses how one would go about making such a port. The advantage of hardware integration is not only performance within virtualized environments. If physical switches also expose the Open vSwitch control abstractions, both bare-metal and virtualized hosting environments can be managed using the same mechanism for automated network control. Summary ------- In many ways, Open vSwitch targets a different point in the design space than previous hypervisor networking stacks, focusing on the need for automated and dynamic network control in large-scale Linux-based virtualization environments. The goal with Open vSwitch is to keep the in-kernel code as small as possible (as is necessary for performance) and to reuse existing subsystems when applicable (for example Open vSwitch uses the existing QoS stack). As of Linux 3.3, Open vSwitch is included as a part of the kernel and packaging for the userspace utilities are available on most popular distributions. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/000077500000000000000000000000001514270232600223455ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/index.rst000066400000000000000000000141261514270232600242120ustar00rootroot00000000000000.. Copyright (c) 2016, Stephen Finucane 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =============== Reference Guide =============== Man Pages --------- .. TODO(stephenfin): Remove the below notice once everything is converted to rST The following man pages are written in rST and converted to roff at compile time: .. toctree:: :maxdepth: 3 ovs-actions.7 ovs-appctl.8 ovs-ctl.8 ovs-flowviz.8 ovs-l3ping.8 ovs-pki.8 ovs-sim.1 ovs-parse-backtrace.8 ovs-tcpdump.8 ovs-tcpundump.1 ovs-test.8 ovs-vlan-test.8 ovsdb-server.7 ovsdb.5 ovsdb.7 The remainder are still in roff format can be found below: .. list-table:: * - ovs-bugtool(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovsdb-client(1) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovsdb-server(1) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovsdb-tool(1) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-dpctl(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-dpctl-top(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-fields(7) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-ofctl(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-pcap(1) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-test(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-testcontroller(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-vlan-test(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-vsctl(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-vswitchd(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - ovs-vswitchd.conf.db(5) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - vtep(5) - `(pdf) `__ - `(html) `__ - `(plain text) `__ * - vtep-ctl(8) - `(pdf) `__ - `(html) `__ - `(plain text) `__ openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/ovs-actions.7.rst000066400000000000000000002765001514270232600255230ustar00rootroot00000000000000.. Copyright (c) 2018 Nicira, Inc. Copyright (c) 2021 RedHat, Inc. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========== ovs-actions =========== Introduction ============ This document aims to comprehensively document all of the OpenFlow actions and instructions, both standard and non-standard, supported by Open vSwitch, regardless of origin. The document includes information of interest to Open vSwitch users, such as the semantics of each supported action and the syntax used by Open vSwitch tools, and to developers seeking to build controllers and switches compatible with Open vSwitch, such as the wire format for each supported message. Actions ------- In this document, we define an ``action`` as an OpenFlow action, which is a kind of command that specifies what to do with a packet. Actions are used in OpenFlow flows to describe what to do when the flow matches a packet, and in a few other places in OpenFlow. Each version of the OpenFlow specification defines standard actions, and beyond that many OpenFlow switches, including Open vSwitch, implement extensions to the standard. OpenFlow groups actions in two ways: as an ``action list`` or an ``action set``, described below. Action Lists ~~~~~~~~~~~~ An ``action list``, a concept present in every version of OpenFlow, is simply an ordered sequence of actions. The OpenFlow specifications require a switch to execute actions within an action list in the order specified, and to refuse to execute an action list entirely if it cannot implement the actions in that order [OpenFlow 1.0, section 3.3], with one exception: when an action list outputs multiple packets, the switch may output the packets in an order different from that specified. Usually, this exception is not important, especially in the common case when the packets are output to different ports. Action Sets ~~~~~~~~~~~ OpenFlow 1.1 introduced the concept of an ``action set``. An action set is also a sequence of actions, but the switch reorders the actions and drops duplicates according to rules specified in the OpenFlow specifications. Because of these semantics, some standard OpenFlow actions cannot usefully be included in an action set. For some, but not all, Open vSwitch extension actions, Open vSwitch defines its own action set semantics and ordering. The OpenFlow pipeline has an action set associated with it as a packet is processed. After pipeline processing is otherwise complete, the switch executes the actions in the action set. Open vSwitch applies actions in an action set in the following order: Except as noted otherwise below, the action set only executes at most a single action of each type, and when more than one action of a given type is present, the one added to the set later replaces the earlier action: #. ``strip_vlan`` #. ``pop_mpls`` #. ``decap`` #. ``encap`` #. ``push_mpls`` #. ``push_vlan`` #. ``dec_ttl`` #. ``dec_mpls_ttl`` #. ``dec_nsh_ttl`` #. All of the following actions are executed in the order added to the action set, with cumulative effect. That is, when multiple actions modify the same part of a field, the later modification takes effect, and when they modify different parts of a field (or different fields), then both modifications are applied: - ``load`` - ``move`` - ``mod_dl_dst`` - ``mod_dl_src`` - ``mod_nw_dst`` - ``mod_nw_src`` - ``mod_nw_tos`` - ``mod_nw_ecn`` - ``mod_nw_ttl`` - ``mod_tp_dst`` - ``mod_tp_src`` - ``mod_vlan_pcp`` - ``mod_vlan_vid`` - ``set_field`` - ``set_tunnel`` - ``set_tunnel64`` #. ``set_queue`` #. ``group``, ``output``, ``resubmit``, ``ct_clear``, or ``ct``. If more than one of these actions is present, then the one listed earliest above is executed and the others are ignored, regardless of the order in which they were added to the action set. (If none of these actions is present, the action set has no real effect, because the modified packet is not sent anywhere and thus the modifications are not visible.) An action set may only contain the actions listed above. Error Handling -------------- Packet processing can encounter a variety of errors: Bridge not found Open vSwitch supports an extension to the standard OpenFlow ``controller`` action called a ``continuation``, which allows the controller to interrupt and later resume the processing of a packet through the switch pipeline. This error occurs when such a packet's processing cannot be resumed, e.g. because the bridge processing it has been destroyed. Open vSwitch reports this error to the controller as Open vSwitch extension error ``NXR_STALE``. This error prevents packet processing entirely. Recursion too deep While processing a given packet, Open vSwitch limits the flow table recursion depth to 64, to ensure that packet processing uses a finite amount of time and space. Actions that count against the recursion limit include ``resubmit`` from a given OpenFlow table to the same or an earlier table, ``group``, and ``output`` to patch ports. A ``resubmit`` from one table to a later one (or, equivalently, a ``goto_table`` instruction) does not count against the depth limit because resubmits to strictly monotonically increasing tables will eventually terminate. OpenFlow tables are most commonly traversed in numerically increasing order, so this limit has little effect on conventionally designed OpenFlow pipelines. This error terminates packet processing. Any previous side effects (e.g. output actions) are retained. Usually this error indicates a loop or other bug in the OpenFlow flow tables. To assist debugging, when this error occurs, Open vSwitch 2.10 and later logs a trace of the packet execution, as if by ``ovs-appctl ofproto/trace``, rate-limited to one per minute to reduce the log volume. Too many resubmits Open vSwitch limits the total number of ``resubmit`` actions that a given packet can execute to 4,096. For this purpose, ``goto_table`` instructions and output to the ``table`` port are treated like ``resubmit``. This limits the amount of time to process a single packet. Unlike the limit on recursion depth, the limit on resubmits counts all resubmits, regardless of direction. This error has the same effect, including logging, as exceeding the recursion depth limit. Stack too deep Open vSwitch limits the amount of data that the ``push`` action can put onto the stack at one time to 64 kB of data. This error terminates packet processing. Any previous side effects (e.g. output actions) are retained. No recirculation context / Recirculation conflict These errors indicate internal errors inside Open vSwitch and should generally not occur. If you notice recurring log messages about these errors, please report a bug. Too many MPLS labels Open vSwitch can process packets with any number of MPLS labels, but its ability to push and pop MPLS labels is limited, currently to 3 labels. Attempting to push more than the supported number of labels onto a packet, or to pop any number of labels from a packet with more than the supported number, raises this error. This error terminates packet processing, retaining any previous side effects (e.g. output actions). When this error arises within the execution of a group bucket, it only terminates that bucket's execution, not packet processing overall. Invalid tunnel metadata Open vSwitch raises this error when it processes a Geneve packet that has TLV options with an invalid form, e.g. where the length in a TLV would extend past the end of the options. This error prevents packet processing entirely. Unsupported packet type When a ``encap`` action encapsulates a packet, Open vSwitch raises this error if it does not support the combination of the new encapsulation with the current packet. ``encap(ethernet)`` raises this error if the current packet is not an L3 packet, and ``encap(nsh)`` raises this error if the current packet is not Ethernet, IPv4, IPv6, or NSH. The ``decap`` action is supported only for packet types ethernet, NSH and MPLS. Openvswitch raises this error for other packet types. When a ``decap`` action decapsulates a packet, Open vSwitch raises this error if it does not support the type of inner packet. ``decap`` of an Ethernet header raises this error if a VLAN header is present, ``decap`` of a NSH packet raises this error if the NSH inner packet is not Ethernet, IPv4, IPv6, or NSH. This error terminates packet processing, retaining any previous side effects (e.g. output actions). When this error arises within the execution of a group bucket, it only terminates that bucket's execution, not packet processing overall. Inconsistencies --------------- OpenFlow 1.0 allows any action to be part of any flow, regardless of the flow's match. Some combinations do not make sense, e.g. an ``set_nw_tos`` action in a flow that matches only ARP packets or ``strip_vlan`` in a flow that matches packets without VLAN tags. Other combinations have varying results depending on the kind of packet that the flow processes, e.g. a ``set_nw_src`` action in a flow that does not match on Ethertype will be treated as a no-op when it processes a non-IPv4 packet. Nevertheless OVS allows all of the above in conformance with OpenFlow 1.0, that is, the following will succeed:: $ ovs-ofctl -O OpenFlow10 add-flow br0 arp,actions=mod_nw_tos:12 $ ovs-ofctl -O OpenFlow10 add-flow br0 dl_vlan=0xffff,actions=strip_vlan $ ovs-ofctl -O OpenFlow10 add-flow br0 actions=mod_nw_src:1.2.3.4 Open vSwitch calls these kinds of combinations ``inconsistencies`` between match and actions. OpenFlow 1.1 and later forbid inconsistencies, and disallow the examples described above by preventing such flows from being added. All of the above, for example, will fail with an error message if one replaces ``OpenFlow10`` by ``OpenFlow11``. OpenFlow 1.1 and later cannot detect and disallow all inconsistencies. For example, the ``write_actions`` instruction arbitrarily delays execution of the actions inside it, which can even be canceled with ``clear_actions``, so that there is no way to ensure that its actions are consistent with the packet at the time they execute. Thus, actions with ``write_actions`` and some other contexts are exempt from consistency requirements. When OVS executes an action inconsistent with the packet, it treats it as a no-op. Inter-Version Compatibility --------------------------- Open vSwitch supports multiple OpenFlow versions simultaneously on a single switch. When actions are added with one OpenFlow version and then retrieved with another, Open vSwitch does its best to translate between them. Inter-version compatibility issues can still arise when different connections use different OpenFlow versions. Backward compatibility is the most obvious case. Suppose, for example, that an OpenFlow 1.1 session adds a flow with a ``push_vlan`` action, for which there is no equivalent in OpenFlow 1.0. If an OpenFlow 1.0 session retrieves this flow, Open vSwitch must somehow represent the action. Forward compatibility can also be an issue, because later OpenFlow versions sometimes remove functionality. The best example is the ``enqueue`` action from OpenFlow 1.0, which OpenFlow 1.1 removed. In practice, Open vSwitch uses a variety of strategies for inter-version compatibility: - Most standard OpenFlow actions, such as ``output`` actions, translate without compatibility issues. - Open vSwitch supports its extension actions in every OpenFlow version, so they do not pose inter-version compatibility problems. - Open vSwitch sometimes adds extension actions to ensure backward or forward compatibility. For example, for backward compatibility with the ``group`` action added in OpenFlow 1.1, Open vSwitch includes an OpenFlow 1.0 extension ``group`` action. Perfect inter-version compatibility is not possible, so best results require OpenFlow connections to use a consistent version. One may enforce use of a particular version by setting the ``protocols`` column for a bridge, e.g. to force ``br0`` to use only OpenFlow 1.3:: ovs-vsctl set bridge br0 protocols=OpenFlow13 Field Specifications -------------------- Many Open vSwitch actions refer to fields. In such cases, fields may usually be referred to by their common names, such as ``eth_dst`` for the Ethernet destination field, or by their full OXM or NXM names, such as ``NXM_OF_ETH_DST`` or ``OXM_OF_ETH_DST``. Before Open vSwitch 2.7, only OXM or NXM field names were accepted. Many actions that act on fields can also act on ``subfields``, that is, parts of fields, written as ``field[start..end]``, where ``start`` is the first bit and ``end`` is the last bit to use in ``field``, e.g. ``vlan_tci[13..15]`` for the VLAN PCP. A single-bit subfield may also be written as ``field[offset]``, e.g. ``vlan_tci[13]`` for the least-significant bit of the VLAN PCP. Empty brackets may be used to explicitly designate an entire field, e.g. ``vlan_tci[]`` for the entire 16-bit VLAN TCI header. Before Open vSwitch 2.7, brackets were required in field specifications. See ``ovs-fields(7)`` for a list of fields and their names. Port Specifications ------------------- Many Open vSwitch actions refer to OpenFlow ports. In such cases, the port may be specified as a numeric port number in the range 0 to 65,535, although Open vSwitch only assigns port numbers in the range 1 through 62,279 to ports. OpenFlow 1.1 and later use 32-bit port numbers, but Open vSwitch never assigns a port number that requires more than 16 bits. In most contexts, the name of a port may also be used. (The most obvious context where a port name may not be used is in an ``ovs-ofctl`` command along with the ``--no-names`` option.) When a port's name contains punctuation or could be ambiguous with other actions, the name may be enclosed in double quotes, with JSON-like string escapes supported (see [RFC 8259]). Open vSwitch also supports the following standard OpenFlow port names (even in contexts where port names are not otherwise supported). The corresponding OpenFlow 1.0 and 1.1+ port numbers are listed alongside them but should not be used in flow syntax: - ``in_port`` (65528 or 0xfff8; 0xfffffff8) - ``table`` (65529 or 0xfff9; 0xfffffff9) - ``normal`` (65530 or 0xfffa; 0xfffffffa) - ``flood`` (65531 or 0xfffb; 0xfffffffb) - ``all`` (65532 or 0xfffc; 0xfffffffc) - ``controller`` (65533 or 0xfffd; 0xfffffffd) - ``local`` (65534 or 0xfffe; 0xfffffffe) - ``any`` or ``none`` (65535 or 0xffff; 0xffffffff) - ``unset`` (not in OpenFlow 1.0; 0xfffffff7) .. Output Actions ============== These actions send a packet to a physical port or a controller. A packet that never encounters an output action on its trip through the Open vSwitch pipeline is effectively dropped. Because actions are executed in order, a packet modification action that is not eventually followed by an output action will not have an externally visible effect. The ``output`` action --------------------- .. name: OUTPUT, OUTPUT_REG, OUTPUT_TRUNC **Syntax**: | *port* | ``output:``\ *port* | ``output:``\ *field* | ``output(port=``\ *port*\ ``, max_len=``\ *nbytes*\ ``)`` Outputs the packet to an OpenFlow port most commonly specified as *port*. Alternatively, the output port may be read from *field*, a field or subfield in the syntax described under `Field Specifications`_ above. Either way, if the port is the packet's input port, the packet is not output. The *port* may be one of the following standard OpenFlow ports: ``local`` Outputs the packet on the ``local port`` that corresponds to the network device that has the same name as the bridge, unless the packet was received on the local port. OpenFlow switch implementations are not required to have a local port, but Open vSwitch bridges always do. ``in_port`` Outputs the packet on the port on which it was received. This is the only standard way to output the packet to the input port (but see `Output to the Input port`_, below). The *port* may also be one of the following additional OpenFlow ports, unless ``max_len`` is specified: ``normal`` Subjects the packet to the device's normal L2/L3 processing. This action is not implemented by all OpenFlow switches, and each switch implements it differently. The section `The OVS Normal Pipeline`_ below documents the OVS implementation. ``flood`` Outputs the packet on all switch physical ports, except the port on which it was received and any ports on which flooding is disabled. Flooding can be disabled automatically on a port by Open vSwitch when IEEE 802.1D spanning tree (STP) or rapid spanning tree (RSTP) is enabled, or by a controller using an OpenFlow ``OFPT_MOD_PORT`` request to set the port's ``OFPPC_NO_FLOOD`` flag (``ovs-ofctl mod-port`` provides a command-line interface to set this flag). ``all`` Outputs the packet on all switch physical ports except the port on which it was received. ``controller`` Sends the packet and its metadata to an OpenFlow controller or controllers encapsulated in an OpenFlow ``packet-in`` message. The separate ``controller`` action, described below, provides more options for output to a controller. Open vSwitch rejects output to other standard OpenFlow ports, including ``none``, ``unset``, and port numbers reserved for future use as standard ports, with the error ``OFPBAC_BAD_OUT_PORT``. With ``max_len``, the packet is truncated to at most *nbytes* bytes before being output. In this case, the output port may not be a patch port. Truncation is just for the single output action, so that later actions in the OpenFlow pipeline work with the complete packet. The truncation feature is meant for use in monitoring applications, e.g. for mirroring packets to a collector. When an ``output`` action specifies the number of a port that does not currently exist (and is not in the range for standard ports), the OpenFlow specification allows but does not require OVS to reject the action. All versions of Open vSwitch treat such an action as a no-op. If a port with the number is created later, then the action will be honored at that point. (OpenFlow requires OVS to reject output to a port number that will never be valid, with ``OFPBAC_BAD_OUT_PORT``, but this situation does not arise when OVS is a software switch, since the user can add or renumber ports at any time.) A controller can suppress output to a port by setting its ``OFPPC_NO_FORWARD`` flag using an OpenFlow ``OFPT_MOD_PORT`` request (``ovs-ofctl mod-port`` provides a command-line interface to set this flag). When output is disabled, ``output`` actions (and other actions that output to the port) are allowed but have no effect. Open vSwitch allows output to a port that does not exist, although OpenFlow allows switches to reject such actions. .. **Conformance** All versions of OpenFlow and Open vSwitch support ``output`` to a literal ``port``. Output to a register is an OpenFlow extension introduced in Open vSwitch 1.3. Output with truncation is an OpenFlow extension introduced in Open vSwitch 2.6. Output to the Input Port ~~~~~~~~~~~~~~~~~~~~~~~~ OpenFlow requires a switch to ignore attempts to send a packet out its ingress port in the most straightforward way. For example, ``output:234`` has no effect if the packet has ingress port 234. The rationale is that dropping these packets makes it harder to loop the network. Sometimes this behavior can even be convenient, e.g. it is often the desired behavior in a flow that forwards a packet to several ports (``floods`` the packet). Sometimes one really needs to send a packet out its ingress port (``hairpin``). In this case, use ``in_port`` to explicitly output the packet to its input port, e.g.:: $ ovs-ofctl add-flow br0 in_port=2,actions=in_port This also works in some circumstances where the flow doesn't match on the input port. For example, if you know that your switch has five ports numbered 2 through 6, then the following will send every received packet out every port, even its ingress port:: $ ovs-ofctl add-flow br0 actions=2,3,4,5,6,in_port or, equivalently:: $ ovs-ofctl add-flow br0 actions=all,in_port Sometimes, in complicated flow tables with multiple levels of ``resubmit`` actions, a flow needs to output to a particular port that may or may not be the ingress port. It's difficult to take advantage of output to ``in_port`` in this situation. To help, Open vSwitch provides, as an OpenFlow extension, the ability to modify the ``in_port`` field. Whatever value is currently in the ``in_port`` field is both the port to which output will be dropped and the destination for ``in_port``. This means that the following adds flows that reliably output to port 2 or to ports 2 through 6, respectively:: $ ovs-ofctl add-flow br0 "in_port=2,actions=load:0->in_port,2" $ ovs-ofctl add-flow br0 "actions=load:0->in_port,2,3,4,5,6" If ``in_port`` is important for matching or other reasons, one may save and restore it on the stack:: $ ovs-ofctl add-flow br0 \ actions="push:in_port,load:0->in_port,2,3,4,5,6,pop:in_port" The OVS Normal Pipeline ----------------------- This section documents how Open vSwitch implements output to the ``normal`` port. The OpenFlow specification places no requirements on how this port works, so all of this documentation is specific to Open vSwitch. Open vSwitch uses the ``Open_vSwitch`` database, detailed in ``ovs-vswitchd.conf.db(5)``, to determine the details of the normal pipeline. The normal pipeline executes the following ingress stages for each packet. Each stage either accepts the packet, in which case the packet goes on to the next stage, or drops the packet, which terminates the pipeline. The result of the ingress stages is a set of output ports, which is the empty set if some ingress stage drops the packet: #. **Input port lookup**: Looks up the OpenFlow ``in_port`` field's value to the corresponding ``Port`` and ``Interface`` record in the database. The ``in_port`` is normally the OpenFlow port that the packet was received on. If ``set_field`` or another actions changes the ``in_port``, the updated value is honored. Accept the packet if the lookup succeeds, which it normally will. If the lookup fails, for example because ``in_port`` was changed to an unknown value, drop the packet. #. **Drop malformed packet**: If the packet is malformed enough that it contains only part of an 802.1Q header, then drop the packet with an error. #. **Drop packets sent to a port reserved for mirroring**: If the packet was received on a port that is configured as the output port for a mirror (that is, it is the ``output_port`` in some ``Mirror`` record), then drop the packet. #. **VLAN input processing**: This stage determines what VLAN the packet is in. It also verifies that this VLAN is valid for the port; if not, drop the packet. How the VLAN is determined and which ones are valid vary based on the ``vlan-mode`` in the input port's ``Port`` record: ``trunk`` The packet is in the VLAN specified in its 802.1Q header, or in VLAN 0 if there is no 802.1Q header. The ``trunks`` column in the ``Port`` record lists the valid VLANs; if it is empty, all VLANs are valid. ``access`` The packet is in the VLAN specified in the ``tag`` column of its ``Port`` record. The packet must not have an 802.1Q header with a nonzero VLAN ID; if it does, drop the packet. ``native-tagged`` / ``native-untagged`` Same as ``trunk`` except that the VLAN of a packet without an 802.1Q header is not necessarily zero; instead, it is taken from the ``tag`` column. ``dot1q-tunnel`` The packet is in the VLAN specified in the ``tag`` column of its ``Port`` record, which is a QinQ service VLAN with the Ethertype specified by the ``Port``'s ``other_config:qinq-ethtype``. If the packet has an 802.1Q header, then it specifies the customer VLAN. The ``cvlans`` column specifies the valid customer VLANs; if it is empty, all customer VLANs are valid. #. **Drop reserved multicast addresses**: If the packet is addressed to a reserved Ethernet multicast address and the ``Bridge`` record does not have ``other_config:forward-bpdu`` set to ``true``, drop the packet. #. **LACP bond admissibility**: This step applies only if the input port is a member of a bond (a ``Port`` with more than one ``Interface``) and that bond is configured to use LACP. Otherwise, skip to the next step. The behavior here depends on the state of LACP negotiation: - If LACP has been negotiated with the peer, accept the packet if the bond member is enabled (i.e. carrier is up and it hasn't been administratively disabled). Otherwise, drop the packet. - If LACP negotiation is incomplete, then drop the packet. There is one exception: if fallback to active-backup mode is enabled, continue with the next step, pretending that the active-backup balancing mode is in use. #. **Non-LACP bond admissibility**: This step applies if the input port is a member of a bond without LACP configured, or if a LACP bond falls back to active-backup as described in the previous step. If neither of these applies, skip to the next step. If the packet is an Ethernet multicast or broadcast, and not received on the bond's active member, drop the packet. The remaining behavior depends on the bond's balancing mode: L4 (aka TCP balancing) Drop the packet (this balancing mode is only supported with LACP). Active-backup Accept the packet only if it was received on the active member. SLB (Source Load Balancing) Drop the packet if the bridge has not learned the packet's source address (in its VLAN) on the port that received it. Otherwise, accept the packet unless it is a gratuitous ARP. Otherwise, accept the packet if the MAC entry we found is ARP-locked. Otherwise, drop the packet. (See the ``SLB Bonding`` section in the OVS bonding document for more information and a rationale.) #. **Learn source MAC**: If the source Ethernet address is not a multicast address, then insert a mapping from packet's source Ethernet address and VLAN to the input port in the bridge's MAC learning table. (This is skipped if the packet's VLAN is listed in the switch's ``Bridge`` record in the ``flood_vlans`` column, since there is no use for MAC learning when all packets are flooded.) When learning happens on a non-bond port, if the packet is a gratuitous ARP, the entry is marked as ARP-locked. The lock expires after 5 seconds. (See the ``SLB Bonding`` section in the OVS bonding document for more information and a rationale.) #. **IP multicast path**: If multicast snooping is enabled on the bridge, and the packet is an Ethernet multicast but not an Ethernet broadcast, and the packet is an IP packet, then the packet takes a special processing path. This path is not yet documented here. .. #. **Output port set**: Search the MAC learning table for the port corresponding to the packet's Ethernet destination and VLAN. If the search finds an entry, the output port set is just the learned port. Otherwise (including the case where the packet is an Ethernet multicast or in ``flood_vlans``), the output port set is all of the ports in the bridge that belong to the packet's VLAN, except for any ports that were disabled for flooding via OpenFlow or that are configured in a ``Mirror`` record as a mirror destination port. The following egress stages execute once for each element in the set of output ports. They execute (conceptually) in parallel, so that a decision or action taken for a given output port has no effect on those for another one: #. **Drop loopback**: If the output port is the same as the input port, drop the packet. #. **VLAN output processing**: This stage adjusts the packet to represent the VLAN in the correct way for the output port. Its behavior varies based on the ``vlan-mode`` in the output port's ``Port`` record: ``trunk`` / ``native-tagged`` / ``native-untagged`` If the packet is in VLAN 0 (for ``native-untagged``, if the packet is in the native VLAN) drops any 802.1Q header. Otherwise, ensures that there is an 802.1Q header designating the VLAN. ``access`` Remove any 802.1Q header that was present. ``dot1q-tunnel`` Ensures that the packet has an outer 802.1Q header with the QinQ Ethertype and the specified configured tag, and an inner 802.1Q header with the packet's VLAN. #. **VLAN priority tag processing**: If VLAN output processing discarded the 802.1Q headers, but priority tags are enabled with ``other_config:priority-tags`` in the output port's ``Port`` record, then a priority-only tag is added (perhaps only if the priority would be nonzero, depending on the configuration). #. **Bond member choice**: If the output port is a bond, the code chooses a particular member. This step is skipped for non-bonded ports. If the bond is configured to use LACP, but LACP negotiation is incomplete, then normally the packet is dropped. The exception is that if fallback to active-backup mode is enabled, the egress pipeline continues choosing a bond member as if active-backup mode was in use. For active-backup mode, the output member is the active member. Other modes hash appropriate header fields and use the hash value to choose one of the enabled members. #. **Output**: The pipeline sends the packet to the output port. The ``controller`` action ------------------------- .. name: CONTROLLER **Syntax**: | ``controller`` | ``controller:``\ *max_len* | ``controller(``\ *key*\ ``[=``\ *value*\ ``], ...)`` Sends the packet and its metadata to an OpenFlow controller or controllers encapsulated in an OpenFlow ``packet-in`` message. The supported options are: ``max_len=``\ *max_len* Limit to *max_len* the number of bytes of the packet to send in the ``packet-in.`` A *max_len* of 0 prevents any of the packet from being sent (thus, only metadata is included). By default, the entire packet is sent, equivalent to a *max_len* of 65535. This option has no effect in Open vSwith 2.7 and later: the entire packet will always be sent. ``reason=``\ *reason* Specify *reason* as the reason for sending the message in the ``packet-in``. The supported reasons are ``no_match``, ``action``, ``invalid_ttl``, ``action_set``, ``group``, and ``packet_out``. The default reason is ``action``. ``id=``\ *controller_id* Specify *controller_id*, a 16-bit integer, as the connection ID of the OpenFlow controller or controllers to which the ``packet-in`` message should be sent. The default is zero. Zero is also the default connection ID for each controller connection, and a given controller connection will only have a nonzero connection ID if its controller uses the ``NXT_SET_CONTROLLER_ID`` Open vSwitch extension to OpenFlow. ``userdata=``\ *hh*\ ``...`` Supplies the bytes represented as hex digits *hh* as additional data to the controller in the ``packet-in`` message. Pairs of hex digits may be separated by periods for readability. ``pause`` Causes the switch to freeze the packet's trip through Open vSwitch flow tables and serializes that state into the packet-in message as a ``continuation,`` an additional property in the ``NXT_PACKET_IN2`` message. The controller can later send the continuation back to the switch in an ``NXT_RESUME`` message, which will restart the packet's traversal from the point where it was interrupted. This permits an OpenFlow controller to interpose on a packet midway through processing in Open vSwitch. **Conformance** All versions of OpenFlow and Open vSwitch support ``controller`` action and its ``max_len`` option. The ``userdata`` and ``pause`` options require the Open vSwitch ``NXAST_CONTROLLER2`` extension action added in Open vSwitch 2.6. In the absence of these options, the ``reason`` (other than ``reason=action``) and ``controller_id`` (option than ``controller_id=0``) options require the Open vSwitch ``NXAST_CONTROLLER`` extension action added in Open vSwitch 1.6. Open vSwitch 2.7 and later is configured to not buffer packets for the packet-in event. As a result, the full packet is always sent to controllers. This means that the ``max_len`` option has no effect on the ``controller`` action, and all values (even 0) are equivalent to the default value of 65535. The ``enqueue`` action ---------------------- .. name: ENQUEUE **Syntax**: | ``enqueue(``\ *port*\ ``,``\ *queue*\ ``)`` | ``enqueue:``\ *port*\ ``:``\ *queue* Enqueues the packet on the specified *queue* within port *port*. *port* must be an OpenFlow port number or name as described under `Port Specifications`_ above. *port* may be ``in_port`` or ``local`` but the other standard OpenFlow ports are not allowed. *queue* must be a number between 0 and 4294967294 (0xfffffffe), inclusive. The number of actually supported queues depends on the switch. Some OpenFlow implementations do not support queuing at all. In Open vSwitch, the supported queues vary depending on the operating system, datapath, and hardware in use. Use the ``QoS`` and ``Queue`` tables in the Open vSwitch database to configure queuing on individual OpenFlow ports (see ``ovs-vswitchd.conf.db(5)`` for more information). **Conformance** Only OpenFlow 1.0 supports ``enqueue``. OpenFlow 1.1 added the ``set_queue`` action to use in its place along with ``output``. Open vSwitch translates ``enqueue`` to a sequence of three actions in OpenFlow 1.1 or later: ``set_queue:``\ *queue*\ ``,output:``\ *port*\ ``,pop_queue``. This is equivalent in behavior as long as the flow table does not otherwise use ``set_queue``, but it relies on the ``pop_queue`` Open vSwitch extension action. The ``bundle`` and ``bundle_load`` actions ------------------------------------------ .. name: BUNDLE, BUNDLE_LOAD **Syntax**: | ``bundle(``\ *fields*\ ``,``\ *basis*\ ``,``\ *algorithm*\ ``,ofport,members:``\ *port*\ ``...)`` | ``bundle_load(``\ *fields*\ ``,``\ *basis*\ ``,``\ *algorithm*\ ``,ofport,``\ *dst*\ ``,members:``\ *port*\ ``...)`` These actions choose a port (a ``member``) from a comma-separated OpenFlow *port* list. After selecting the port, ``bundle`` outputs to it, whereas ``bundle_load`` writes its port number to *dst*, which must be a 16-bit or wider field or subfield in the syntax described under `Field Specifications`_ above. These actions hash a set of *fields* using *basis* as a universal hash parameter, then apply the bundle link selection *algorithm* to choose a *port*. *fields* must be one of the following. For the options with ``symmetric`` in the name, reversing source and destination addresses yields the same hash: ``eth_src`` Ethernet source address. ``nw_src`` IPv4 or IPv6 source address. ``nw_dst`` IPv4 or IPv6 destination address. ``symmetric_l4`` Ethernet source and destination, Ethernet type, VLAN ID or IDs (if any), IPv4 or IPv6 source and destination, IP protocol, TCP or SCTP (but not UDP) source and destination. ``symmetric_l3l4`` IPv4 or IPv6 source and destination, IP protocol, TCP or SCTP (but not UDP) source and destination. ``symmetric_l3l4+udp`` Like ``symmetric_l3l4`` but include UDP ports. *algorithm* must be one of the following: ``active_backup`` Chooses the first live port listed in ``members``. ``hrw`` (Highest Random Weight) Computes the following, considering only the live ports in ``members``:: for i in [1, n_members]: weights[i] = hash(flow, i) member = { i such that weights[i] >= weights[j] for all j != i } This algorithm is specified by RFC 2992. The algorithms take port liveness into account when selecting members. The definition of whether a port is live is subject to change. It currently takes into account carrier status and link monitoring protocols such as BFD and CFM. If none of the members is live, ``bundle`` does not output the packet and ``bundle_load`` stores ``OFPP_NONE`` (65535) in the output field. Example: ``bundle(eth_src,0,hrw,ofport,members:4,8)`` uses an Ethernet source hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest Random Weight algorithm. **Conformance** Open vSwitch 1.2 introduced the ``bundle`` and ``bundle_load`` OpenFlow extension actions. The ``group`` action -------------------- .. name: GROUP **Syntax**: | ``group:``\ *group* Outputs the packet to the OpenFlow group *group*, which must be a number in the range 0 to 4294967040 (0xffffff00). The group must exist or Open vSwitch will refuse to add the flow. When a group is deleted, Open vSwitch also deletes all of the flows that output to it. Groups contain action sets, whose semantics are described above in the section `Action Sets`_. The semantics of action sets can be surprising to users who expect action list semantics, since action sets reorder and sometimes ignore actions. A ``group`` action usually executes the action set or sets in one or more group buckets. Open vSwitch saves the packet and metadata before it executes each bucket, and then restores it afterward. Thus, when a group executes more than one bucket, this means that each bucket executes on the same packet and metadata. Moreover, regardless of the number of buckets executed, the packet and metadata are the same before and after executing the group. Sometimes saving and restoring the packet and metadata can be undesirable. In these situations, workarounds are possible. For example, consider a pipeline design in which a ``select`` group bucket is to communicate to a later stage of processing a value based on which bucket was selected. An obvious design would be for the bucket to communicate the value via ``set_field`` on a register. This does not work because registers are part of the metadata that ``group`` saves and restores. The following alternative bucket designs do work: - Recursively invoke the rest of the pipeline with ``resubmit``. - Use ``resubmit`` into a table that uses ``push`` to put the value on the stack for the caller to ``pop`` off. This works because ``group`` preserves only packet data and metadata, not the stack. (This design requires indirection through ``resubmit`` because actions sets may not contain ``push`` or ``pop`` actions.) An ``exit`` action within a group bucket terminates only execution of that bucket, not other buckets or the overall pipeline. **Conformance** OpenFlow 1.1 introduced ``group``. Open vSwitch 2.6 and later also supports ``group`` as an extension to OpenFlow 1.0. Encapsulation and Decapsulation Actions ======================================= The ``strip_vlan`` and ``pop`` actions -------------------------------------- .. name: STRIP_VLAN **Syntax**: | ``strip_vlan`` | ``pop_vlan`` Removes the outermost VLAN tag, if any, from the packet. The two names for this action are synonyms with no semantic difference. The OpenFlow 1.0 specification uses the name ``strip_vlan`` and later versions use ``pop_vlan``, but OVS accepts either name regardless of version. In OpenFlow 1.1 and later, consistency rules allow ``strip_vlan`` only in a flow that matches only packets with a VLAN tag (or following an action that pushes a VLAN tag, such as ``push_vlan``). See `Inconsistencies`_, above, for more information. **Conformance** All versions of OpenFlow and Open vSwitch support this action. The ``push_vlan`` action ------------------------ .. name: PUSH_VLAN **Syntax**: | ``push_vlan:``\ *ethertype* Pushes a new outermost VLAN onto the packet. Uses TPID *ethertype*, which must be ``0x8100`` for an 802.1Q C-tag or ``0x88a8`` for a 802.1ad S-tag. **Conformance** OpenFlow 1.1 and later supports this action. Open vSwitch 2.8 added support for multiple VLAN tags (with a limit of 2) and 802.1ad S-tags. The ``push_mpls`` action ------------------------ .. name: PUSH_MPLS **Syntax**: | ``push_mpls:``\ *ethertype* Pushes a new outermost MPLS label stack entry (LSE) onto the packet and changes the packet's Ethertype to *ethertype*, which must be either ``B0x8847`` or ``0x8848``. If the packet did not already contain any MPLS labels, initializes the new LSE as: Label 2, if the packet contains IPv6, 0 otherwise. TC The low 3 bits of the packet's DSCP value, or 0 if the packet is not IP. TTL Copied from the IP TTL, or 64 if the packet is not IP. If the packet did already contain an MPLS label, initializes the new outermost label as a copy of the existing outermost label. OVS currently supports at most 3 MPLS labels. This action applies only to Ethernet packets. **Conformance** Open vSwitch 1.11 introduced support for MPLS. OpenFlow 1.1 and later support ``push_mpls``. Open vSwitch implements ``push_mpls`` as an extension to OpenFlow 1.0. The ``pop_mpls`` action ----------------------- .. name: POP_MPLS **Syntax**: | ``pop_mpls:``\ *ethertype* Strips the outermost MPLS label stack entry and changes the packet's Ethertype to *ethertype*. This action applies only to Ethernet packets with at least one MPLS label. If there is more than one MPLS label, then *ethertype* should be an MPLS Ethertype (``B0x8847`` or ``0x8848``). **Conformance** Open vSwitch 1.11 introduced support for MPLS. OpenFlow 1.1 and later support ``pop_mpls``. Open vSwitch implements ``pop_mpls`` as an extension to OpenFlow 1.0. The ``encap`` action -------------------- .. name: ENCAP **Syntax**: | ``encap(nsh([md_type=``\ *md_type*\ ``], [tlv(``\ *class*,\ *type*,\ *value*\ ``)]...))`` | ``encap(ethernet)`` | ``encap(mpls)`` | ``encap(mpls_mc)`` The ``encap`` action encapsulates a packet with a specified header. It has variants for different kinds of encapsulation. The ``encap(nsh(...))`` variant encapsulates an Ethernet frame with NSH. The *md_type* may be ``1`` or ``2`` for metadata type 1 or 2, defaulting to 1. For metadata type 2, TLVs may be specified with *class* as a 16-bit hexadecimal integer beginning with ``0x``, *type* as an 8-bit decimal integer, and *value* a sequence of pairs of hex digits beginning with ``0x``. For example: ``encap(nsh(md_type=1))`` Encapsulates the packet with an NSH header with metadata type 1. ``encap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))`` Encapsulates the packet with an NSH header, NSH metadata type 2, and an NSH TLV with class 0x1000, type 10, and the 4-byte value 0x12345678. The ``encap(ethernet)`` variant encapsulate a bare L3 packet in an Ethernet frame. The Ethernet type is initialized to the L3 packet's type, e.g. 0x0800 if the L3 packet is IPv4. The Ethernet source and destination are initially zeroed. The ``encap(mpls)`` variant adds a MPLS header at the start of the packet. When encap(ethernet) is applied after this action, the ethertype of ethernet header will be populated with MPLS unicast ethertype (``0x8847``). The ``encap(mpls_mc)`` variant adds a MPLS header at the start of the packet. When encap(ethernet) is applied after this action, the ethertype of ethernet header will be populated with MPLS multicast ethertype (``0x8848``). **Conformance** This action is an Open vSwitch extension to OpenFlow 1.3 and later, introduced in Open vSwitch 2.8. The MPLS support for this action is added in Open vSwitch 2.17. The ``decap`` action -------------------- .. name: DECAP **Syntax**: | ``decap`` | ``decap(packet_type(ns=``\ *namespace,*\ ``type=``\ *type*\ ``))`` Removes an outermost encapsulation from the packet: - If the packet is an Ethernet packet, removes the Ethernet header, which changes the packet into a bare L3 packet. If the packet has VLAN tags, raises an unsupported packet type error (see `Error Handling`_, above). - Otherwise, if the packet is an NSH packet, removes the NSH header, revealing the inner packet. Open vSwitch supports Ethernet, IPv4, IPv6, and NSH inner packet types. Other types raise unsupported packet type errors. - Otherwise, if the packet is encapsulated inside a MPLS header, removes the MPLS header and classifies the inner packet as mentioned in the packet type argument of the decap. The *packet_type* field specifies the type of the packet in the format specified in OpenFlow 1.5 chapter `7.2.3.11 Packet Type Match Field`. The inner packet will be incorrectly classified, if the inner packet is different from mentioned in the *packet_type* field. - Otherwise, raises an unsupported packet type error. **Conformance** This action is an Open vSwitch extension to OpenFlow 1.3 and later, introduced in Open vSwitch 2.8. The MPLS support for this action is added in Open vSwitch 2.17. Field Modification Actions ========================== These actions modify packet data and metadata fields. The ``set_field`` and ``load`` actions -------------------------------------- .. name: SET_FIELD **Syntax**: | ``set_field:``\ *value*\ ``[/``\ *mask*\ ``]->``\ *dst* | ``load:``\ *value*\ ``->``\ *dst* These actions loads a literal value into a field or part of a field. The ``set_field`` action takes *value* in the customary syntax for field *dst*, e.g. ``00:11:22:33:44:55`` for an Ethernet address, and *dst* as the field's name. The optional *mask* allows part of a field to be set. The ``load`` action takes *value* as an integer value (in decimal or prefixed by ``0x`` for hexadecimal) and *dst* as a field or subfield in the syntax described under `Field Specifications`_ above. The following all set the Ethernet source address to 00:11:22:33:44:55: - ``set_field:00:11:22:33:44:55->eth_src`` - ``load:0x001122334455->eth_src`` - ``load:0x001122334455->OXM_OF_ETH_SRC[]`` The following all set the multicast bit in the Ethernet destination address: - ``set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst`` - ``load:1->eth_dst[40]`` Open vSwitch prohibits a ``set_field`` or ``load`` action whose *dst* is not guaranteed to be part of the packet; for example, ``set_field`` of ``nw_dst`` is only allowed in a flow that matches on Ethernet type 0x800. In some cases, such as in an action set, Open vSwitch can't statically check that *dst* is part of the packet, and in that case if it is not then Open vSwitch treats the action as a no-op. **Conformance** Open vSwitch 1.1 introduced ``NXAST_REG_LOAD`` as a extension to OpenFlow 1.0 and used ``load`` to express it. Later, OpenFlow 1.2 introduced a standard ``OFPAT_SET_FIELD`` action that was restricted to loading entire fields, so Open vSwitch added the form ``set_field`` with this restriction. OpenFlow 1.5 extended ``OFPAT_SET_FIELD`` to the point that it became a superset of ``NXAST_REG_LOAD``. Open vSwitch translates either syntax as necessary for the OpenFlow version in use: in OpenFlow 1.0 and 1.1, ``NXAST_REG_LOAD``; in OpenFlow 1.2, 1.3, and 1.4, ``NXAST_REG_LOAD`` for ``load`` or for loading a subfield, ``OFPAT_SET_FIELD`` otherwise; and OpenFlow 1.5 and later, ``OFPAT_SET_FIELD``. The ``move`` action ------------------- .. name: REG_MOVE **Syntax**: | ``move:``\ *src*\ ``->``\ *dst* Copies the named bits from field or subfield *src* to field or subfield *dst*. *src* and *dst* should fields or subfields in the syntax described under `Field Specifications`_ above. The two fields or subfields must have the same width. Examples: - ``move:reg0[0..5]->reg1[26..31]`` copies the six bits numbered 0 through 5 in register 0 into bits 26 through 31 of register 1. - ``move:reg0[0..15]->vlan_tci`` copies the least significant 16 bits of register 0 into the VLAN TCI field. **Conformance** In OpenFlow 1.0 through 1.4, ``move`` ordinarily uses an Open vSwitch extension to OpenFlow. In OpenFlow 1.5, ``move`` uses the OpenFlow 1.5 standard ``OFPAT_COPY_FIELD`` action. The ONF has also made ``OFPAT_COPY_FIELD`` available as an extension to OpenFlow 1.3. Open vSwitch 2.4 and later understands this extension and uses it if a controller uses it, but for backward compatibility with older versions of Open vSwitch, ``ovs-ofctl`` does not use it. The ``mod_dl_src`` and ``mod_dl_dst`` actions --------------------------------------------- .. name: SET_ETH_SRC, SET_ETH_DST **Syntax**: | ``mod_dl_src:``\ *mac* | ``mod_dl_dst:``\ *mac* Sets the Ethernet source or destination address, respectively, to *mac*, which should be expressed in the form ``xx:xx:xx:xx:xx:xx``. For L3-only packets, that is, those that lack an Ethernet header, this action has no effect. **Conformance** OpenFlow 1.0 and 1.1 have specialized actions for these purposes. OpenFlow 1.2 and later do not, so Open vSwitch translates them to appropriate ``OFPAT_SET_FIELD`` actions for those versions, The ``mod_nw_src`` and ``mod_nw_dst`` actions --------------------------------------------- .. name: SET_IP_SRC, SET_IP_DST **Syntax**: | ``mod_nw_src:``\ *ip* | ``mod_nw_dst:``\ *ip* Sets the IPv4 source or destination address, respectively, to *ip*, which should be expressed in the form ``w.x.y.z``. In OpenFlow 1.1 and later, consistency rules allow these actions only in a flow that matches only packets that contain an IPv4 header (or following an action that adds an IPv4 header, e.g. ``pop_mpls:0x0800``). See `Inconsistencies`_, above, for more information. **Conformance** OpenFlow 1.0 and 1.1 have specialized actions for these purposes. OpenFlow 1.2 and later do not, so Open vSwitch translates them to appropriate ``OFPAT_SET_FIELD`` actions for those versions, The ``mod_nw_tos`` and ``mod_nw_ecn`` actions --------------------------------------------- .. name: SET_IP_DSCP, SET_IP_ECN **Syntax**: | ``mod_nw_tos:``\ *tos* | ``mod_nw_ecn:``\ *ecn* The ``mod_nw_tos`` action sets the DSCP bits in the IPv4 ToS/DSCP or IPv6 traffic class field to *tos*, which must be a multiple of 4 between 0 and 255. This action does not modify the two least significant bits of the ToS field (the ECN bits). The ``mod_nw_ecn`` action sets the ECN bits in the IPv4 ToS or IPv6 traffic class field to *ecn*, which must be a value between 0 and 3, inclusive. This action does not modify the six most significant bits of the field (the DSCP bits). In OpenFlow 1.1 and later, consistency rules allow these actions only in a flow that matches only packets that contain an IPv4 or IPv6 header (or following an action that adds such a header). See `Inconsistencies`_, above, for more information. **Conformance** OpenFlow 1.0 has a ``mod_nw_tos`` action but not ``mod_nw_ecn``. Open vSwitch implements the latter in OpenFlow 1.0 as an extension using ``NXAST_REG_LOAD``. OpenFlow 1.1 has specialized actions for these purposes. OpenFlow 1.2 and later do not, so Open vSwitch translates them to appropriate ``OFPAT_SET_FIELD`` actions for those versions. The ``mod_tp_src`` and ``mod_tp_dst`` actions --------------------------------------------- .. name: SET_L4_SRC_PORT, SET_L4_DST_PORT **Syntax**: | ``mod_tp_src:``\ *port* | ``mod_tp_dst:``\ *port* Sets the TCP or UDP or SCTP source or destination port, respectively, to *port*. Both IPv4 and IPv6 are supported. In OpenFlow 1.1 and later, consistency rules allow these actions only in a flow that matches only packets that contain a TCP or UDP or SCTP header. See `Inconsistencies`_, above, for more information. **Conformance** OpenFlow 1.0 and 1.1 have specialized actions for these purposes. OpenFlow 1.2 and later do not, so Open vSwitch translates them to appropriate ``OFPAT_SET_FIELD`` actions for those versions, The ``dec_ttl`` action ---------------------- .. name : DEC_TTL **Syntax**: | ``dec_ttl`` | ``dec_ttl(``\ *id1*\ ``[,``\ *id2*\ ``[, ...]])`` Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the TTL or hop limit is initially 0 or 1, no decrement occurs, as packets reaching TTL zero must be rejected. Instead, Open vSwitch sends a ``packet-in`` message with reason code ``OFPR_INVALID_TTL`` to each connected controller that has enabled receiving such messages, and stops processing the current set of actions. (However, if the current set of actions was reached through ``resubmit``, the remaining actions in outer levels resume processing.) As an Open vSwitch extension to OpenFlow, this action supports the ability to specify a list of controller IDs. Open vSwitch will only send the message to controllers with the given ID or IDs. Specifying no list is equivalent to specifying a single controller ID of zero. In OpenFlow 1.1 and later, consistency rules allow these actions only in a flow that matches only packets that contain an IPv4 or IPv6 header. See `Inconsistencies`_, above, for more information. **Conformance** All versions of OpenFlow and Open vSwitch support this action. The ``set_mpls_label``, ``set_mpls_tc``, and ``set_mpls_ttl`` actions --------------------------------------------------------------------- .. name: SET_MPLS_LABEL, SET_MPLS_TC, SET_MPLS_TTL **Syntax**: | ``set_mpls_label:``\ *label* | ``set_mpls_tc:``\ *tc* | ``set_mpls_ttl:``\ *ttl* The ``set_mpls_label`` action sets the label of the packet's outer MPLS label stack entry. *label* should be a 20-bit value that is decimal by default; use a ``0x`` prefix to specify the value in hexadecimal. The ``set_mpls_tc`` action sets the traffic class of the packet's outer MPLS label stack entry. *tc* should be in the range 0 to 7, inclusive. The ``set_mpls_ttl`` action sets the TTL of the packet's outer MPLS label stack entry. *ttl* should be in the range 0 to 255 inclusive. In OpenFlow 1.1 and later, consistency rules allow these actions only in a flow that matches only packets that contain an MPLS label (or following an action that adds an MPLS label, e.g. ``push_mpls:0x8847``). See `Inconsistencies`_, above, for more information. **Conformance** OpenFlow 1.0 does not support MPLS, but Open vSwitch implements these actions as extensions. OpenFlow 1.1 has specialized actions for these purposes. OpenFlow 1.2 and later do not, so Open vSwitch translates them to appropriate ``OFPAT_SET_FIELD`` actions for those versions, The ``dec_mpls_ttl`` and ``dec_nsh_ttl`` actions ------------------------------------------------ .. name: DEC_MPLS_TTL, DEC_NSH_TTL **Syntax**: | ``dec_mpls_ttl`` | ``dec_nsh_ttl`` These actions decrement the TTL of the packet's outer MPLS label stack entry or its NSH header, respectively. If the TTL is initially 0 or 1, no decrement occurs. Instead, Open vSwitch sends a ``packet-in`` message with reason code ``BOFPR_INVALID_TTL`` to OpenFlow controllers with ID 0, if it has enabled receiving them. Processing the current set of actions then stops. (However, if the current set of actions was reached through ``resubmit``, remaining actions in outer levels resume processing.) In OpenFlow 1.1 and later, consistency rules allow this actions only in a flow that matches only packets that contain an MPLS label or an NSH header, respectively. See `Inconsistencies`_, above, for more information. **Conformance** Open vSwitch 1.11 introduced support for MPLS. OpenFlow 1.1 and later support ``dec_mpls_ttl``. Open vSwitch implements ``dec_mpls_ttl`` as an extension to OpenFlow 1.0. Open vSwitch 2.8 introduced support for NSH, although the NSH draft changed after release so that only Open vSwitch 2.9 and later conform to the final protocol specification. The ``dec_nsh_ttl`` action and NSH support in general is an Open vSwitch extension not supported by any version of OpenFlow. The ``check_pkt_larger`` action ------------------------------- .. name: CHECK_PKT_LARGER **Syntax**: | ``check_pkt_larger(``\ *pkt_len*\ ``)->``\ *dst* Checks if the packet is larger than the specified length in *pkt_len*. If so, stores 1 in *dst*, which should be a 1-bit field; if not, stores 0. The packet length to check against the argument *pkt_len* includes the L2 header and L2 payload of the packet, but not the VLAN tag (if present). Examples: - ``check_pkt_larger(1500)->reg0[0]`` - ``check_pkt_larger(8000)->reg9[10]`` This action was added in Open vSwitch 2.12. The ``delete_field`` action --------------------------- .. name: DELETE_FIELD **Syntax**: | ``delete_field:``\ *field* The ``delete_field`` action deletes a *field* in the syntax described under `Field Specifications`_ above. Currently, only the ``tun_metadata`` fields are supported. This action was added in Open vSwitch 2.14. Metadata Actions ================ The ``set_tunnel`` action ------------------------- .. name: SET_TUNNEL **Syntax**: | ``set_tunnel:``\ *id* | ``set_tunnel64:``\ *id* Many kinds of tunnels support a tunnel ID, e.g. VXLAN and Geneve have a 24-bit VNI, and GRE has an optional 32-bit key. This action sets the value used for tunnel ID in such tunneled packets, although whether it is used for a particular tunnel depends on the tunnel's configuration. See the tunnel ID documentation in ``ovs-fields(7)`` for more information. **Conformance** These actions are OpenFlow extensions. ``set_tunnel`` was introduced in Open vSwitch 1.0. ``set_tunnel64``, which is needed if *id* is wider than 32 bits, was added in Open vSwitch 1.1. Both actions always set the entire tunnel ID field. Open vSwitch supports these actions in all versions of OpenFlow, but in OpenFlow 1.2 and later it translates them to an appropriate standardized ``OFPAT_SET_FIELD`` action. The ``set_queue`` and ``pop_queue`` actions ------------------------------------------- .. name: SET_QUEUE, POP_QUEUE **Syntax**: | ``set_queue:``\ *queue* | ``pop_queue`` The ``set_queue`` action sets the queue ID to be used for subsequent output actions to *queue*, which must be a 32-bit integer. The range of meaningful values of *queue*, and their meanings, varies greatly from one OpenFlow implementation to another. Even within a single implementation, there is no guarantee that all OpenFlow ports have the same queues configured or that all OpenFlow ports in an implementation can be configured the same way queue-wise. For more information, see the documentation for the output queue field in ``ovs-fields(7)``. The ``pop_queue`` restores the output queue to the default that was set when the packet entered the switch (generally 0). Four billion queues ought to be enough for anyone: https://mailman.stanford.edu/pipermail/openflow-spec/2009-August/000394.html **Conformance** OpenFlow 1.1 introduced the ``set_queue`` action. Open vSwitch also supports it as an extension in OpenFlow 1.0. The ``pop_queue`` action is an Open vSwitch extension. Firewalling Actions =================== Open vSwitch is often used to implement a firewall. The preferred way to implement a firewall is ``connection tracking,`` that is, to keep track of the connection state of individual TCP sessions. The ``ct`` action described in this section, added in Open vSwitch 2.5, implements connection tracking. For new deployments, it is the recommended way to implement firewalling with Open vSwitch. Before ``ct`` was added, Open vSwitch did not have built-in support for connection tracking. Instead, Open vSwitch supported the ``learn`` action, which allows a received packet to add a flow to an OpenFlow flow table. This could be used to implement a primitive form of connection tracking: packets passing through the firewall in one direction could create flows that allowed response packets back through the firewall in the other direction. The additional ``fin_timeout`` action allowed the learned flows to expire quickly after TCP session termination. The ``ct`` action ----------------- .. name: CT **Syntax**: | ``ct([``\ *argument*\ ``]...)`` | ``ct(commit[,``\ *argument*\ ``]...)`` The action has two modes of operation, distinguished by whether ``commit`` is present. The following arguments may be present in either mode: ``zone=``\ *value* A zone is a 16-bit id that isolates connections into separate domains, allowing overlapping network addresses in different zones. If a zone is not provided, then the default is 0. The *value* may be specified either as a 16-bit integer literal or a field or subfield in the syntax described under `Field Specifications`_ above. Without ``commit``, this action sends the packet through the connection tracker. The connection tracker keeps track of the state of TCP connections for packets passed through it. For each packet through a connection, it checks that it satisfies TCP invariants and signals the connection state to later actions using the ``ct_state`` metadata field, which is documented in ``ovs-fields(7)``. In this form, ``ct`` forks the OpenFlow pipeline: - In one fork, ``ct`` passes the packet to the connection tracker. Afterward, it reinjects the packet into the OpenFlow pipeline with the connection tracking fields initialized. The ``ct_state`` field is initialized with connection state and ``ct_zone`` to the connection tracking zone specified on the ``zone`` argument. If the connection is one that is already tracked, ``ct_mark`` and ``ct_label`` to its existing mark and label, respectively; otherwise they are zeroed. In addition, ``ct_nw_proto``, ``ct_nw_src``, ``ct_nw_dst``, ``ct_ipv6_src``, ``ct_ipv6_dst``, ``ct_tp_src``, and ``ct_tp_dst`` are initialized appropriately for the original direction connection. See the ``resubmit`` action for a way to search the flow table with the connection tracking original direction fields swapped with the packet 5-tuple fields. See ``ovs-fields(7)`` for details on the connection tracking fields. - In the other fork, the original instance of the packet continues independent processing following the ``ct`` action. The ``ct_state`` field and other connection tracking metadata are cleared. Without ``commit``, the ``ct`` action accepts the following arguments: ``table=``\ *table* Sets the OpenFlow table where the packet is reinjected. The *table* must be a number between 0 and 254 inclusive, or a table's name. If *table* is not specified, then the packet is not reinjected. ``nat`` ``nat(``\ *type*\ ``=``\ *addrs*\ ``[:``\ *ports*\ ``][,``\ *flag*\ ``]...)`` Specify address and port translation for the connection being tracked. The *type* must be ``src``, for source address/port translation (SNAT), or ``dst``, for destination address/port translation (DNAT). Setting up address translation for a new connection takes effect only if the connection is later committed with ``ct(commit ...)``. The ``src`` and ``dst`` options take the following arguments: *addrs* The IP address ``addr`` or range ``addr1-addr2`` from which the translated address should be selected. If only one address is given, then that address will always be selected, otherwise the address selection can be informed by the optional persistent flag as described below. Either IPv4 or IPv6 addresses can be provided, but both addresses must be of the same type, and the datapath behavior is undefined in case of providing IPv4 address range for an IPv6 packet, or IPv6 address range for an IPv4 packet. IPv6 addresses must be bracketed with ``[`` and ``]`` if a port range is also given. *ports* The L4 ``port`` or range ``port1-port2`` from which the translated port should be selected. When a port range is specified, fallback to ephemeral ports does not happen, else, it will. The port number selection can be informed by the optional ``random`` and ``hash`` flags described below. The optional *flags* are: ``random`` The selection of the port from the given range should be done using a fresh random number. This flag is mutually exclusive with ``hash``. ``hash`` The selection of the port from the given range should be done using a datapath specific hash of the packet's IP addresses and the other, non-mapped port number. This flag is mutually exclusive with ``random``. ``persistent`` The selection of the IP address from the given range should be done so that the same mapping can be provided after the system restarts. If ``alg`` is specified for the committing ``ct`` action that also includes ``nat`` with a ``src`` or ``dst`` attribute, then the datapath tries to set up the helper to be NAT-aware. This functionality is datapath specific and may not be supported by all datapaths. A ``bare`` ``nat`` argument with no options will only translate the packet being processed in the way the connection has been set up with an earlier, committed ``ct`` action. A ``nat`` action with ``src`` or ``dst``, when applied to a packet belonging to an established (rather than new) connection, will behave the same as a bare ``nat``. For SNAT, there is a special case when the ``src`` IP address is configured as all 0's, i.e., ``nat(src=0.0.0.0)``. In this case, when a source port collision is detected during the commit, the source port will be translated to an ephemeral port. If there is no collision, no SNAT is performed. Open vSwitch 2.6 introduced ``nat``. Linux 4.6 was the earliest upstream kernel that implemented ``ct`` support for ``nat``. With ``commit``, the connection tracker commits the connection to the connection tracking module. The ``commit`` flag should only be used from the pipeline within the first fork of ``ct`` without ``commit``. Information about the connection is stored beyond the lifetime of the packet in the pipeline. Some ``ct_state`` flags are only available for committed connections. The following options are available only with ``commit``: ``force`` A committed connection always has the directionality of the packet that caused the connection to be committed in the first place. This is the ``original direction`` of the connection, and the opposite direction is the ``reply direction``. If a connection is already committed, but it is in the wrong direction, ``force`` effectively terminates the existing connection and starts a new one in the current direction. This flag has no effect if the original direction of the connection is already the same as that of the current packet. ``exec(``\ *action*\ ``...)`` Perform each *action* within the context of connection tracking. Only actions which modify the ``ct_mark`` or ``ct_label`` fields are accepted within ``exec`` action, and these fields may only be modified with this option. For example: ``set_field:``\ *value*\ ``[/``\ *mask*\ ``]->ct_mark`` Store a 32-bit metadata value with the connection. Subsequent lookups for packets in this connection will populate ``ct_mark`` when the packet is sent to the connection tracker with the table specified. ``set_field:``\ *value*\ ``[/``\ *mask*\ ``]->ct_label`` Store a 128-bit metadata value with the connection. Subsequent lookups for packets in this connection will populate ``ct_label`` when the packet is sent to the connection tracker with the table specified. ``alg=``\ *alg* Specify application layer gateway *alg* to track specific connection types. If subsequent related connections are sent through the ``ct`` action, then the ``rel`` flag in the ``ct_state`` field will be set. Supported types include: ``ftp`` Look for negotiation of FTP data connections. Specify this option for FTP control connections to detect related data connections and populate the ``rel`` flag for the data connections. ``tftp`` Look for negotiation of TFTP data connections. Specify this option for TFTP control connections to detect related data connections and populate the ``rel`` flag for the data connections. Related connections inherit ``ct_mark`` from that stored with the original connection (i.e. the connection created by ``ct(alg=...)``. With the Linux datapath, global sysctl options affect ``ct`` behavior. In particular, if ``net.netfilter.nf_conntrack_helper`` is enabled, which it is by default until Linux 4.7, then application layer gateway helpers may be executed even if *alg* is not specified. For security reasons, the netfilter team recommends users disable this option. For further details, please see http://www.netfilter.org/news.html#2012-04-03 . The ``ct`` action may be used as a primitive to construct stateful firewalls by selectively committing some traffic, then matching ``ct_state`` to allow established connections while denying new connections. The following flows provide an example of how to implement a simple firewall that allows new connections from port 1 to port 2, and only allows established connections to send traffic from port 2 to port 1:: table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=100,ip,ct_state=-trk,action=ct(table=1) table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit),2 table=1,in_port=1,ip,ct_state=+trk+est,action=2 table=1,in_port=2,ip,ct_state=+trk+new,action=drop table=1,in_port=2,ip,ct_state=+trk+est,action=1 If ``ct`` is executed on IPv4 (or IPv6) fragments, then the message is implicitly reassembled before sending to the connection tracker and refragmented upon output, to the original maximum received fragment size. Reassembly occurs within the context of the zone, meaning that IP fragments in different zones are not assembled together. Pipeline processing for the initial fragments is halted. When the final fragment is received, the message is assembled and pipeline processing continues for that flow. Packet ordering is not guaranteed by IP protocols, so it is not possible to determine which IP fragment will cause message reassembly (and therefore continue pipeline processing). As such, it is strongly recommended that multiple flows should not execute ``ct`` to reassemble fragments from the same IP message. **Conformance** The ``ct`` action was introduced in Open vSwitch 2.5. Some of its features were introduced later, noted individually above. The ``ct_clear`` action ----------------------- .. name: CT_CLEAR **Syntax**: | ``ct_clear`` Clears connection tracking state from the flow, zeroing ``ct_state``, ``ct_zone``, ``ct_mark``, and ``ct_label``. This action was introduced in Open vSwitch 2.7. The ``learn`` action -------------------- .. name: LEARN **Syntax**: | ``learn(``\ *argument*\ ``...)`` The ``learn`` action adds or modifies a flow in an OpenFlow table, similar to ``ovs-ofctl --strict mod-flows``. The arguments specify the match fields, actions, and other properties of the flow to be added or modified. Match fields for the new flow are specified as follows. At least one match field should ordinarily be specified: *field*\ ``=``\ *value* Specifies that *field*, in the new flow, must match the literal *value*, e.g. ``dl_type=0x800``. Shorthand match syntax, such as ``ip`` in place of ``dl_type=0x800``, is not supported. *field*\ ``=``\ *src* Specifies that *field* in the new flow must match *src* taken from the packet currently being processed. For example, ``udp_dst=udp_src``, applied to a UDP packet with source port 53, creates a flow which matches ``udp_dst=53``. *field* and *src* must have the same width. *field* Shorthand for the previous form when *field* and *src* are the same. For example, ``udp_dst``, applied to a UDP packet with destination port 53, creates a flow which matches ``udp_dst=53``. The *field* and *src* arguments above should be fields or subfields in the syntax described under `Field Specifications`_ above. Match field specifications must honor prerequisites for both the flow with the ``learn`` and the new flow that it creates. Consider the following complete flow, in the syntax accepted by ``ovs-ofctl``. If the flow's match on ``udp`` were omitted, then the flow would not satisfy the prerequisites for the ``learn`` action's use of ``udp_src``. If ``dl_type=0x800`` or ``nw_proto`` were omitted from ``learn``, then the new flow would not satisfy the prerequisite for its match on ``udp_dst``. For more information on prerequisites, please refer to ``ovs-fields(7)``:: udp, actions=learn(dl_type=0x800, nw_proto=17, udp_dst=udp_src) Actions for the new flow are specified as follows. At least one action should ordinarily be specified: ``load:``\ *value*\ ``->``\ *dst* Adds a ``load`` action to the new flow that loads the literal *value* into *dst*. The syntax is the same as the ``load`` action explained in the `Field Modification Actions`_ section. ``load:``\ *src*\ ``->``\ *dst* Adds a ``load`` action to the new flow that loads *src*, a field or subfield from the packet being processed, into *dst*. ``output:``\ *field* Adds an ``output`` action to the new flow's actions that outputs to the OpenFlow port taken from *field*, which must be a field as described above. ``fin_idle_timeout=``\ *seconds* / ``fin_hard_timeout=``\ *seconds* Adds a ``fin_timeout`` action with the specified arguments to the new flow. This feature was added in Open vSwitch 1.6. The following additional arguments are optional: ``idle_timeout=``\ *seconds* ``hard_timeout=``\ *seconds* ``priority=``\ *value* ``cookie=``\ *value* ``send_flow_rem`` These arguments have the same meaning as in the usual flow syntax documented in ``ovs-ofctl(8)``. ``table=``\ *table* The table in which the new flow should be inserted. Specify a decimal number between 0 and 254 inclusive or the name of a table. The default, if table is unspecified, is table 1 (not 0). ``delete_learned`` When this flag is specified, deleting the flow that contains the ``learn`` action will also delete the flows created by ``learn``. Specifically, when the last ``learn`` action with this flag and particular ``table`` and ``cookie`` values is removed, the switch deletes all of the flows in the specified table with the specified cookie. This flag was added in Open vSwitch 2.4. ``limit=``\ *number* If the number of flows in the new flow's table with the same cookie exceeds *number*, the action will not add a new flow. By default, or with ``limit=0``, there is no limit. This flag was added in Open vSwitch 2.8. ``result_dst=``\ *field*\ ``[``\ *bit*\ ``]`` If learn fails (because the number of flows exceeds ``limit``), the action sets *field*\ [*bit*] to 0, otherwise it will be set to 1. *field*\ [*bit*] must be a single bit. This flag was added in Open vSwitch 2.8. By itself, the ``learn`` action can only put two kinds of actions into the flows that it creates: ``load`` and ``output`` actions. If ``learn`` is used in isolation, these are severe limits. However, ``learn`` is not meant to be used in isolation. It is a primitive meant to be used together with other Open vSwitch features to accomplish a task. Its existing features are enough to accomplish most tasks. Here is an outline of a typical pipeline structure that allows for versatile behavior using ``learn``: - Flows in table ``A`` contain a ``learn`` action, that populates flows in table ``L``, that use a ``load`` action to populate register ``R`` with information about what was learned. - Flows in table ``B`` contain two sequential resubmit actions: one to table ``L`` and another one to table ``B + 1``. - Flows in table ``B + 1`` match on register ``R`` and act differently depending on what the flows in table ``L`` loaded into it. This approach can be used to implement many ``learn``-based features. For example: - Resubmit to a table selected based on learned information, e.g. see https://mail.openvswitch.org/pipermail/ovs-discuss/2016-June/021694.html . - MAC learning in the middle of a pipeline, as described in the ``Open vSwitch Advanced Features Tutorial`` in the OVS documentation. - TCP state based firewalling, by learning outgoing connections based on SYN packets and matching them up with incoming packets. (This is usually better implemented using the ``ct`` action.) - At least some of the features described in T. A. Hoff, ``Extending Open vSwitch to Facilitate Creation of Stateful SDN Applications``. **Conformance** The ``learn`` action is an Open vSwitch extension to OpenFlow added in Open vSwitch 1.3. Some features of ``learn`` were added in later versions, as noted individually above. The ``fin_timeout`` action -------------------------- .. name: FIN_TIMEOUT **Syntax**: | ``fin_timeout(``\ *key*\ ``=``\ *value*\ ``...)`` This action changes the idle timeout or hard timeout, or both, of the OpenFlow flow that contains it, when the flow matches a TCP packet with the FIN or RST flag. When such a packet is observed, the action reduces the rule's timeouts to those specified on the action. If the rule's existing timeout is already shorter than the one that the action specifies, then that timeout is unaffected. The timeouts are specified as key-value pairs: ``idle_timeout=``\ *seconds* Causes the flow to expire after the given number of seconds of inactivity. ``hard_timeout=``\ *seconds* Causes the flow to expire after the given number of *seconds*, regardless of activity. (*seconds* specifies time since the flow's creation, not since the receipt of the FIN or RST.) This action is normally added to a learned flow by the ``learn`` action. It is unlikely to be useful otherwise. **Conformance** This Open vSwitch extension action was added in Open vSwitch 1.6. Programming and Control Flow Actions ==================================== The ``resubmit`` action ----------------------- .. name: RESUBMIT **Syntax**: | ``resubmit:``\ *port* | ``resubmit([``\ *port*\ ``],[``\ *table*\ ][,ct])`` Searches an OpenFlow flow table for a matching flow and executes the actions found, if any, before continuing to the following action in the current flow entry. Arguments can customize the search: - If *port* is given as an OpenFlow port number or name, then it specifies a value to use for the input port metadata field as part of the search, in place of the input port currently in the flow. Specifying ``in_port`` as ``port`` is equivalent to omitting it. - If *table* is given as an integer between 0 and 254 or a table name, it specifies the OpenFlow table to search. If it is not specified, the table from the current flow is used. - If ``ct`` is specified, then the search is done with packet 5-tuple fields swapped with the corresponding conntrack original direction tuple fields. See the documentation for ``ct`` above, for more information about connection tracking, or ``ovs-fields(7)`` for details about the connection tracking fields. This flag requires a valid connection tracking state as a match prerequisite in the flow where this action is placed. Examples of valid connection tracking state matches include ``ct_state=+new``, ``ct_state=+est``, ``ct_state=+rel``, and ``ct_state=+trk-inv``. The changes, if any, to the input port and connection tracking fields are just for searching the flow table. The changes are not visible to actions or to later flow table lookups. The most common use of ``resubmit`` is to visit another flow table without *port* or ``ct``, like this: ``resubmit(,``\ *table*\ ``)``. Recursive ``resubmit`` actions are permitted. **Conformance** The ``resubmit`` action is an Open vSwitch extension. However, the ``goto_table`` instruction in OpenFlow 1.1 and later can be viewed as a kind of restricted ``resubmit``. Open vSwitch 1.3 added ``table``. Open vSwitch 2.7 added ``ct``. Open vSwitch imposes a limit on ``resubmit`` recursion that varies among version: - Open vSwitch 1.0.1 and earlier did not support recursion. - Open vSwitch 1.0.2 and 1.0.3 limited recursion to 8 levels. - Open vSwitch 1.1 and 1.2 limited recursion to 16 levels. - Open vSwitch 1.2 through 1.8 limited recursion to 32 levels. - Open vSwitch 1.9 through 2.0 limited recursion to 64 levels. - Open vSwitch 2.1 through 2.5 limited recursion to 64 levels and impose a total limit of 4,096 resubmits per flow translation (earlier versions did not impose any total limit). - Open vSwitch 2.6 and later imposes the same limits as 2.5, with one exception: resubmit from table ``x`` to any table ``y > x`` does not count against the recursion depth limit. The ``clone`` action -------------------- .. name: CLONE **Syntax**: | ``clone(``\ *action*\ ``...)`` Executes each nested *action*, saving much of the packet and pipeline state beforehand and then restoring it afterward. The state that is saved and restored includes all flow data and metadata (including, for example, ``in_port`` and ``ct_state``), the stack accessed by ``push`` and ``pop`` actions, and the OpenFlow action set. This action was added in Open vSwitch 2.7. The ``push`` and ``pop`` actions -------------------------------- .. name: STACK_PUSH, STACK_POP **Syntax**: | ``push:``\ *src* | ``pop:``\ *dst* The ``push`` action pushes *src* on a general-purpose stack. The ``pop`` action pops an entry off the stack into *dst*. *src* and *dst* should be fields or subfields in the syntax described under `Field Specifications`_ above. Controllers can use the stack for saving and restoring data or metadata around ``resubmit`` actions, for swapping or rearranging data and metadata, or for other purposes. Any data or metadata field, or part of one, may be pushed, and any modifiable field or subfield may be popped. The number of bits pushed in a stack entry do not have to match the number of bits later popped from that entry. If more bits are popped from an entry than were pushed, then the entry is conceptually left-padded with 0-bits as needed. If fewer bits are popped than pushed, then bits are conceptually trimmed from the left side of the entry. The stack's size is limited. The limit is intended to be high enough that ``normal`` use will not pose problems. Stack overflow or underflow is an error that stops action execution (see ``Stack too deep`` under `Error Handling`_, above). Examples: - ``push:reg2[0..5]`` or ``push:NXM_NX_REG2[0..5]`` pushes on the stack the 6 bits in register 2 bits 0 through 5. - ``pop:reg2[0..5]`` or ``pop:NXM_NX_REG2[0..5]`` pops the value from top of the stack and copy bits 0 through 5 of that value into bits 0 through 5 of register 2. **Conformance** Open vSwitch 1.2 introduced ``push`` and ``pop`` as OpenFlow extension actions. The ``exit`` action ------------------- .. name: EXIT **Syntax**: | ``exit`` This action causes Open vSwitch to immediately halt execution of further actions. Actions which have already been executed are unaffected. Any further actions, including those which may be in other tables, or different levels of the ``resubmit`` call stack, are ignored. However, an ``exit`` action within a group bucket terminates only execution of that bucket, not other buckets or the overall pipeline. Actions in the action set are still executed (specify ``clear_actions`` before ``exit`` to discard them). The ``multipath`` action ------------------------ .. name: MULTIPATH **Syntax**: | ``multipath(``\ *fields*,\ *basis*,\ *algorithm*,\ *n_links*,\ *arg*,\ *dst*\ ``)`` Hashes *fields* using *basis* as a universal hash parameter, then the applies multipath link selection *algorithm* (with parameter *arg*) to choose one of *n_links* output links numbered 0 through *n_links* minus 1, and stores the link into *dst*, which must be a field or subfield in the syntax described under `Field Specifications`_ above. The ``bundle`` or ``bundle_load`` actions are usually easier to use than ``multipath``. *fields* must be one of the following: ``eth_src`` Hashes Ethernet source address only. ``symmetric_l4`` Hashes Ethernet source, destination, and type, VLAN ID, IPv4/IPv6 source, destination, and protocol, and TCP or SCTP (but not UDP) ports. The hash is computed so that pairs of corresponding flows in each direction hash to the same value, in environments where L2 paths are the same in each direction. UDP ports are not included in the hash to support protocols such as VXLAN that use asymmetric ports in each direction. ``symmetric_l3l4`` Hashes IPv4/IPv6 source, destination, and protocol, and TCP or SCTP (but not UDP) ports. Like ``symmetric_l4``, this is a symmetric hash, but by excluding L2 headers it is more effective in environments with asymmetric L2 paths (e.g. paths involving VRRP IP addresses on a router). Not an effective hash function for protocols other than IPv4 and IPv6, which hash to a constant zero. ``symmetric_l3l4+udp`` Like ``symmetric_l3l4+udp``, but UDP ports are included in the hash. This is a more effective hash when asymmetric UDP protocols such as VXLAN are not a consideration. ``symmetric_l3`` Hashes network source address and network destination address. ``nw_src`` Hashes network source address only. ``nw_dst`` Hashes network destination address only. The *algorithm* used to compute the final result ``link`` must be one of the following: ``modulo_n`` Computes ``link = hash(flow) % n_links``. This algorithm redistributes all traffic when ``n_links`` changes. It has ``O(1)`` performance. Use 65535 for ``max_link`` to get a raw hash value. This algorithm is specified by RFC 2992. ``hash_threshold`` Computes ``link = hash(flow) / (MAX_HASH / n_links)``. Redistributes between one-quarter and one-half of traffic when ``n_links`` changes. It has ``O(1)`` performance. This algorithm is specified by RFC 2992. ``hrw`` (Highest Random Weight) Computes the following:: for i in [0, n_links]: weights[i] = hash(flow, i) link = { i such that weights[i] >= weights[j] for all j != i } Redistributes ``1 / n_links`` of traffic when ``n_links`` changes. It has ``O(n_links)`` performance. If ``n_links`` is greater than a threshold (currently 64, but subject to change), Open vSwitch will substitute another algorithm automatically. This algorithm is specified by RFC 2992. ``iter_hash`` (Iterative Hash) Computes the following:: i = 0 repeat: i = i + 1 link = hash(flow, i) % arg while link > max_link Redistributes ``1 / n_links`` of traffic when ``n_links`` changes. ``O(1)`` performance when ``arg / max_link`` is bounded by a constant. Redistributes all traffic when ``arg`` changes. *arg* must be greater than ``max_link`` and for best performance should be no more than approximately ``max_link * 2``. If *arg* is outside the acceptable range, Open vSwitch will automatically substitute the least power of 2 greater than ``max_link``. This algorithm is specific to Open vSwitch. Only the ``iter_hash`` algorithm uses *arg*. It is an error if ``max_link`` is greater than or equal to ``2**n_bits``. **Conformance** This is an OpenFlow extension added in Open vSwitch 1.1. Other Actions ============= The ``conjunction`` action -------------------------- .. name: CONJUNCTION **Syntax**: | ``conjunction(``\ *id*, *k*/*n*\ ``)`` This action allows for sophisticated ``conjunctive match`` flows. Refer to ``Conjunctive Match Fields`` in ``ovs-fields(7)`` for details. A flow that has one or more ``conjunction`` actions may not have any other actions except for ``note`` actions. **Conformance** Open vSwitch 2.4 introduced the ``conjunction`` action and ``conj_id`` field. They are Open vSwitch extensions to OpenFlow. The ``note`` action ------------------- .. name: NOTE **Syntax**: | ``note:[``\ *hh*\ ``]...`` This action does nothing at all. OpenFlow controllers may use it to annotate flows with more data than can fit in a flow cookie. The action may include any number of bytes represented as hex digits *hh*. Periods may separate pairs of hex digits, for readability. The ``note`` action's format doesn't include an exact length for its payload, so the provided bytes will be padded on the right by enough bytes with value 0 to make the total number 6 more than a multiple of 8. **Conformance** This action is an extension to OpenFlow introduced in Open vSwitch 1.1. The ``sample`` action --------------------- .. name: SAMPLE **Syntax**: | ``sample(``\ *argument*\ ``...)`` Samples packets and sends one sample for every sampled packet. The following *argument* forms are accepted: ``probability=``\ *packets* The number of sampled packets out of 65535. Must be greater or equal to 1. ``collector_set_id=``\ *id* The unsigned 32-bit integer identifier of the set of sample collectors to send sampled packets to. Defaults to 0. ``obs_domain_id=``\ *value* When sending samples to IPFIX collectors, the unsigned 32-bit integer Observation Domain ID sent in every IPFIX flow record. The *value* may be specified as a 32-bit integer or a field or subfield in the syntax described under `Field Specifications`_ above. Defaults to 0. ``obs_point_id=``\ *value* When sending samples to IPFIX collectors, the unsigned 32-bit integer Observation Point ID sent in every IPFIX flow record. The *value* may be specified as a 32-bit integer or a field or subfield in the syntax described under `Field Specifications`_ above. Defaults to 0. ``sampling_port=``\ *port* Sample packets on *port*, which should be the ingress or egress port. This option, which was added in Open vSwitch 2.6, allows the IPFIX implementation to export egress tunnel information. ``ingress`` ``egress`` Specifies explicitly that the packet is being sampled on ingress to or egress from the switch. IPFIX reports sent by Open vSwitch before version 2.6 did not include a direction. From 2.6 until 2.7, IPFIX reports inferred a direction from ``sampling_port``: if it was the packet's output port, then the direction was reported as egress, otherwise as ingress. Open vSwitch 2.7 introduced these options, which allow the inferred direction to be overridden. This is particularly useful when the ingress (or egress) port is not a tunnel. Refer to ``ovs-vswitchd.conf.db(5)`` for more details on configuring sample collector sets. **Conformance** This action is an OpenFlow extension added in Open vSwitch 2.4. Support for subfields in `obs_domain_id` and `obs_point_id` was added in Open vSwitch 3.4. Instructions ============ Every version of OpenFlow includes actions. OpenFlow 1.1 introduced the higher-level, related concept of ``instructions``. In OpenFlow 1.1 and later, actions within a flow are always encapsulated within an instruction. Each flow has at most one instruction of each kind, which are executed in the following fixed order defined in the OpenFlow specification: #. ``Meter`` #. ``Apply-Actions`` #. ``Clear-Actions`` #. ``Write-Actions`` #. ``Write-Metadata`` #. ``Stat-Trigger`` (not supported by Open vSwitch) #. ``Goto-Table`` The most important instruction is ``Apply-Actions``. This instruction encapsulates any number of actions, which the instruction executes. Open vSwitch does not explicitly represent ``Apply-Actions``. Instead, any action by itself is implicitly part of an ``Apply-Actions`` instructions. Open vSwitch syntax requires other instructions, if present, to be in the order listed above. Otherwise it will flag an error. The ``meter`` action and instruction ------------------------------------ .. name: METER **Syntax**: | ``meter:``\ *meter_id* Apply meter *meter_id*. If a meter band rate is exceeded, the packet may be dropped, or modified, depending on the meter band type. **Conformance** OpenFlow 1.3 introduced the ``meter`` instruction. OpenFlow 1.5 changes ``meter`` from an instruction to an action. OpenFlow 1.5 allows implementations to restrict ``meter`` to be the first action in an action list and to exclude ``meter`` from action sets, for better compatibility with OpenFlow 1.3 and 1.4. Open vSwitch restricts the ``meter`` action both ways. Open vSwitch 2.0 introduced OpenFlow protocol support for meters, but it did not include a datapath implementation. Open vSwitch 2.7 added meter support to the userspace datapath. Open vSwitch 2.10 added meter support to the kernel datapath. Open vSwitch 2.12 added support for meter as an action in OpenFlow 1.5. The ``clear_actions`` instruction --------------------------------- .. name: CLEAR_ACTIONS **Syntax**: | ``clear_actions`` Clears the action set. See `Action Sets`_, above, for more information. **Conformance** OpenFlow 1.1 introduced ``clear_actions``. Open vSwitch 2.1 added support for ``clear_actions``. The ``write_actions`` instruction --------------------------------- .. name: WRITE_ACTIONS **Syntax**: | ``write_actions(``\ *action*\ ``...)`` Adds each *action* to the action set. The action set is carried between flow tables and then executed at the end of the pipeline. Only certain actions may be written to the action set. See `Action Sets`_, above, for more information. **Conformance** OpenFlow 1.1 introduced ``write_actions``. Open vSwitch 2.1 added support for ``write_actions``. The ``write_metadata`` instruction ---------------------------------- .. name: WRITE_METADATA **Syntax**: | ``write_metadata:``\ *value*\ ``[/``\ *mask*\ ``]`` Updates the flow's ``metadata`` field. If *mask* is omitted, ``metadata`` is set exactly to *value*; if *mask* is specified, then a 1-bit in *mask* indicates that the corresponding bit in ``metadata`` will be replaced with the corresponding bit from *value*. Both *value* and *mask* are 64-bit values that are decimal by default; use a ``0x`` prefix to specify them in hexadecimal. The ``metadata`` field can also be matched in the flow table and updated with actions such as ``set_field`` and ``move``. **Conformance** OpenFlow 1.1 introduced ``write_metadata``. Open vSwitch 2.1 added support for ``write_metadata``. The ``goto_table`` instruction ------------------------------ .. name: GOTO_TABLE **Syntax**: | ``goto_table:``\ *table* Jumps to *table* as the next table in the process pipeline. The table may be a number between 0 and 254 or a table name. It is an error if *table* is less than or equal to the table of the flow that contains it; that is, ``goto_table`` must move forward in the OpenFlow pipeline. Since ``goto_table`` must be the last instruction in a flow, it never leads to recursion. The ``resubmit`` extension action is more flexible. **Conformance** OpenFlow 1.1 introduced ``goto_table``. Open vSwitch 2.1 added support for ``goto_table``. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/ovs-appctl.8.rst000066400000000000000000000253551514270232600253470ustar00rootroot00000000000000========== ovs-appctl ========== Synopsis ======== ``ovs-appctl`` [``--target=``\ *target* | ``-t`` *target*] [``--timeout=``\ *secs* | ``-T`` *secs*] [``--format=``\ *format* | ``-f`` *format*] [``--pretty``] *command* [*arg* ``...``] ``ovs-appctl --help`` ``ovs-appctl --version`` Description =========== Open vSwitch daemons accept certain commands at runtime to control their behavior and query their settings. Every daemon accepts a common set of commands documented under `Common Commands`_ below. Some daemons support additional commands documented in their own manpages. ``ovs-vswitchd`` in particular accepts a number of additional commands documented in ``ovs-vswitchd(8)``. The ``ovs-appctl`` program provides a simple way to invoke these commands. The command to be sent is specified on ``ovs-appctl``'s command line as non-option arguments. ``ovs-appctl`` sends the command and prints the daemon's response on standard output. In normal use only a single option is accepted: * ``-t`` *target* or ``--target=``\ *target* Tells ``ovs-appctl`` which daemon to contact. If *target* begins with ``/`` it must name a Unix domain socket on which an Open vSwitch daemon is listening for control channel connections. By default, each daemon listens on a Unix domain socket in the rundir (e.g. ``/run``) named ``..ctl``, where is the program's name and is its process ID. For example, if ``ovs-vswitchd`` has PID 123, it would listen on ``ovs-vswitchd.123.ctl``. Otherwise, ``ovs-appctl`` looks in the rundir for a pidfile, that is, a file whose contents are the process ID of a running process as a decimal number, named *target*\ ``.pid``. (The ``--pidfile`` option makes an Open vSwitch daemon create a pidfile.) ``ovs-appctl`` reads the pidfile, then looks in the rundir for a Unix socket named *target*\ ``..ctl``, where is replaced by the process ID read from the pidfile, and uses that file as if it had been specified directly as the target. On Windows, *target* can be an absolute path to a file that contains a localhost TCP port on which an Open vSwitch daemon is listening for control channel connections. By default, each daemon writes the TCP port on which it is listening for control connection into the file ``.ctl`` located inside the rundir. If *target* is not an absolute path, ``ovs-appctl`` looks in the rundir for a file named *target*\ ``.ctl``. The default *target* is ``ovs-vswitchd``. * ``-T`` *secs* or ``--timeout=``\ *secs* By default, or with a *secs* of ``0``, ``ovs-appctl`` waits forever to connect to the daemon and receive a response. This option limits runtime to approximately *secs* seconds. If the timeout expires, ``ovs-appctl`` exits with a ``SIGALRM`` signal. * ``-f`` *format* or ``--format=``\ *format* Tells ``ovs-appctl`` which output format to use. By default, or with a *format* of ``text``, ``ovs-appctl`` will print plain-text for humans. When *format* is ``json``, ``ovs-appctl`` will return a JSON document. When ``json`` is requested, but a command has not implemented JSON output, the plain-text output will be wrapped in a provisional JSON document with the following structure:: {"reply-format":"plain","reply":"$PLAIN_TEXT_HERE"} * ``--pretty`` By default, JSON output is printed as compactly as possible. This option causes JSON in output to be printed in a more readable fashion. For example, members of objects and elements of arrays are printed one per line, with indentation. Requires ``--format=json``. Common Commands =============== Every Open vSwitch daemon supports a common set of commands, which are documented in this section. General Commands ---------------- These commands display daemon-specific commands and the running version. Note that these commands are different from the ``--help`` and ``--version`` options that return information about the ``ovs-appctl`` utility itself. * ``list-commands`` Lists the commands supported by the target. * ``version`` Displays the version and compilation date of the target. Logging Commands ---------------- Open vSwitch has several log levels. The highest-severity log level is: * ``off`` No message is ever logged at this level, so setting a logging destination's log level to ``off`` disables logging to that destination. The following log levels, in order of descending severity, are available: * ``emer`` A major failure forced a process to abort. * ``err`` A high-level operation or a subsystem failed. Attention is warranted. * ``warn`` A low-level operation failed, but higher-level subsystems may be able to recover. * ``info`` Information that may be useful in retrospect when investigating a problem. * ``dbg`` Information useful only to someone with intricate knowledge of the system, or that would commonly cause too-voluminous log output. Log messages at this level are not logged by default. Every Open vSwitch daemon supports the following commands for examining and adjusting log levels: * ``vlog/list`` Lists the known logging modules and their current levels. * ``vlog/list-pattern`` Lists logging pattern used for each destination. * ``vlog/set`` [*spec*] Sets logging levels. Without any *spec*, sets the log level for every module and destination to ``dbg``. Otherwise, *spec* is a list of words separated by spaces or commas or colons, up to one from each category below: * A valid module name, as displayed by the ``vlog/list`` command on ``ovs-appctl(8)``, limits the log level change to the specified module. * ``syslog``, ``console``, or ``file``, to limit the log level change to only to the system log, to the console, or to a file, respectively. On Windows platform, ``syslog`` is only useful if *target* was started with the ``--syslog-target`` option (it has no effect otherwise). * ``off``, ``emer``, ``err``, ``warn``, ``info``, or ``dbg``, to control the log level. Messages of the given severity or higher will be logged, and messages of lower severity will be filtered out. ``off`` filters out all messages. Case is not significant within *spec*. Regardless of the log levels set for ``file``, logging to a file will not take place unless the target application was invoked with the ``--log-file`` option. For compatibility with older versions of OVS, ``any`` is accepted within *spec* but it has no effect. * ``vlog/set PATTERN:``\ *destination*:*pattern* Sets the log pattern for *destination* to *pattern*. Each time a message is logged to *destination*, *pattern* determines the message's formatting. Most characters in *pattern* are copied literally to the log, but special escapes beginning with ``%`` are expanded as follows: * ``%A`` The name of the application logging the message, e.g. ``ovs-vswitchd``. * ``%B`` The RFC5424 syslog PRI of the message. * ``%c`` The name of the module (as shown by ``ovs-appctl --list``) logging the message. * ``%d`` The current date and time in ISO 8601 format (``YYYY-MM-DD HH:MM:SS``). * ``%d{``\ *format*\ ``}`` The current date and time in the specified *format*, which takes the same format as the ``template`` argument to ``strftime(3)``. As an extension, any ``#`` characters in *format* will be replaced by fractional seconds, e.g. use ``%H:%M:%S.###`` for the time to the nearest millisecond. Sub-second times are only approximate and currently decimal places after the third will always be reported as zero. * ``%D`` The current UTC date and time in ISO 8601 format (``YYYY-MM-DD HH:MM:SS``). * ``%D{``\ *format*\ ``}`` The current UTC date and time in the specified *format*, which takes the same format as the ``template`` argument to ``strftime(3)``. Supports the same extension for sub-second resolution as ``%d{...}``. * ``%E`` The hostname of the node running the application. * ``%m`` The message being logged. * ``%N`` A serial number for this message within this run of the program, as a decimal number. The first message a program logs has serial number 1, the second one has serial number 2, and so on. * ``%n`` A new-line. * ``%p`` The level at which the message is logged, e.g. ``DBG``. * ``%P`` The program's process ID (pid), as a decimal number. * ``%r`` The number of milliseconds elapsed from the start of the application to the time the message was logged. * ``%t`` The subprogram name, that is, an identifying name for the process or thread that emitted the log message, such as ``monitor`` for the process used for ``--monitor`` or ``main`` for the primary process or thread in a program. * ``%T`` The subprogram name enclosed in parentheses, e.g. ``(monitor)``, or the empty string for the primary process or thread in a program. * ``%%`` A literal ``%``. A few options may appear between the ``%`` and the format specifier character, in this order: * ``-`` Left justify the escape's expansion within its field width. Right justification is the default. * ``0`` Pad the field to the field width with ``0`` characters. Padding with spaces is the default. * *width* A number specifies the minimum field width. If the escape expands to fewer characters than *width* then it is padded to fill the field width. (A field wider than *width* is not truncated to fit.) The default pattern for console and file output is ``%D{%Y-%m-%dT %H:%M:%SZ}|%05N|%c|%p|%m``; for syslog output, ``%05N|%c|%p|%m``. Daemons written in Python (e.g. ``ovs-monitor-ipsec``) do not allow control over the log pattern. * ``vlog/set FACILITY:``\ *facility* Sets the RFC5424 facility of the log message. *facility* can be one of ``kern``, ``user``, ``mail``, ``daemon``, ``auth``, ``syslog``, ``lpr``, ``news``, ``uucp``, ``clock``, ``ftp``, ``ntp``, ``audit``, ``alert``, ``clock2``, ``local0``, ``local1``, ``local2``, ``local3``, ``local4``, ``local5``, ``local6`` or ``local7``. * ``vlog/close`` Causes the daemon to close its log file, if it is open. (Use ``vlog/reopen`` to reopen it later.) * ``vlog/reopen`` Causes the daemon to close its log file, if it is open, and then reopen it. (This is useful after rotating log files, to cause a new log file to be used.) This has no effect if the target application was not invoked with the ``--log-file`` option. Options ======= .. option:: -h, --help Prints a brief help message to the console. .. option:: -V, --version Prints version information to the console. See Also ======== ``ovs-appctl`` can control all Open vSwitch daemons, including ``ovs-vswitchd(8)`` and ``ovsdb-server(1)``. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/ovs-ctl.8.rst000066400000000000000000000404351514270232600246420ustar00rootroot00000000000000======= ovs-ctl ======= Synopsis ======== ``ovs-ctl --system-id=random| [] start`` ``ovs-ctl stop`` ``ovs-ctl --system-id=random| [] restart`` ``ovs-ctl status`` ``ovs-ctl version`` ``ovs-ctl [] load-kmod`` ``ovs-ctl --system-id=random| [] force-reload-kmod`` ``ovs-ctl [--protocol=] [--sport=] [--dport=] enable-protocol`` ``ovs-ctl delete-transient-ports`` ``ovs-ctl help | -h | --help`` ``ovs-ctl --version`` Description =========== The ``ovs-ctl`` program starts, stops, and checks the status of Open vSwitch daemons. It is not meant to be invoked directly by system administrators but to be called internally by system startup scripts. Each ``ovs-ctl`` command is described separately below. The ``start`` command --------------------- The ``start`` command starts Open vSwitch. It performs the following tasks: 1. Loads the Open vSwitch kernel module. If this fails, and the Linux bridge module is loaded but no bridges exist, it tries to unload the bridge module and tries loading the Open vSwitch kernel module again. (This is because the Open vSwitch kernel module cannot coexist with the Linux bridge module before 2.6.37.) The ``start`` command skips the following steps if ``ovsdb-server`` is already running: 2. If the Open vSwitch database file does not exist, it creates it. If the database does exist, but it has an obsolete version, it upgrades it to the latest schema. 3. Starts ``ovsdb-server``, unless the ``--no-ovsdb-server`` command option is given. 4. Initializes a few values inside the database. 5. If the ``--delete-bridges`` option was used, deletes all of the bridges from the database. 6. If the ``--delete-transient-ports`` option was used, deletes all ports that have ``other_config:transient`` set to true. The ``start`` command skips the following step if ``ovs-vswitchd`` is already running, or if the ``--no-ovs-vswitchd`` command option is given: 7. Starts ``ovs-vswitchd``. Options ~~~~~~~ Several command-line options influence the ``start`` command's behavior. Some form of the following option should ordinarily be specified: * ``--system-id=`` or ``--system-id=random`` This specifies a unique system identifier to store into ``external-ids:system-id`` in the database's ``Open_vSwitch`` table. Remote managers that talk to the Open vSwitch database server over network protocols use this value to identify and distinguish Open vSwitch instances, so it should be unique (at least) within OVS instances that will connect to a single controller. When ``random`` is specified, ``ovs-ctl`` will generate a random ID that persists from one run to another (stored in a file). When another string is specified ``ovs-ctl`` uses it literally. The following options should be specified if the defaults are not suitable: * ``--system-type=`` or ``--system-version=`` Sets the value to store in the ``system-type`` and ``system-version`` columns, respectively, in the database's ``Open_vSwitch`` table. Remote managers may use these values too determine the kind of system to which they are connected (primarily for display to human administrators). When not specified, ``ovs-ctl`` uses values from the optional ``system-type.conf`` and ``system-version.conf`` files (see `Files`_) or it uses the ``lsb_release`` program, if present, to provide reasonable defaults. The following options are also likely to be useful: * ``--external-id="="`` Sets ``external-ids:`` to in the database's ``Open_vSwitch`` table. Specifying this option multiple times adds multiple key-value pairs. * ``--delete-bridges`` Ordinarily Open vSwitch bridges persist from one system boot to the next, as long as the database is preserved. Some environments instead expect to re-create all of the bridges and other configuration state on every boot. This option supports that, by deleting all Open vSwitch bridges after starting ``ovsdb-server`` but before starting ``ovs-vswitchd``. * ``--delete-transient-ports`` Deletes all ports that have ``other_config:transient`` set to ``true``. This is important on certain environments where some ports are going to be recreated after reboot, but other ports need to be persisted in the database. * ``--ovs-user=user[:group]`` Ordinarily Open vSwitch daemons are started as the user invoking the ovs-ctl command. Some system administrators would prefer to have the various daemons spawn as different users in their environments. This option allows passing the ``--user`` option to the ``ovsdb-server`` and ``ovs-vswitchd`` daemons, allowing them to change their privilege levels. The following options are less important: * ``--no-monitor`` By default ``ovs-ctl`` passes ``--monitor`` to ``ovs-vswitchd`` and ``ovsdb-server``, requesting that it spawn a process monitor which will restart the daemon if it crashes. This option suppresses that behavior. * ``--daemon-cwd=`` Specifies the current working directory that the OVS daemons should run from. The default is ``/`` (the root directory) if this option is not specified. (This option is useful because most systems create core files in a process's current working directory and because a file system that is in use as a process's current working directory cannot be unmounted.) * ``--no-force-corefiles`` By default, ``ovs-ctl`` enables core dumps for the OVS daemons. This option disables that behavior. * ``--no-mlockall`` By default ``ovs-ctl`` passes ``--mlockall`` to ``ovs-vswitchd``, requesting that it lock all of its virtual memory on page fault (on allocation, when running on Linux kernel 4.4 and older), preventing it from being paged to disk. This option suppresses that behavior. * ``--no-self-confinement`` Disable self-confinement for ``ovs-vswitchd`` and ``ovsdb-server`` daemons. This flag may be used when, for example, OpenFlow controller creates its Unix Domain Socket outside OVS run directory and OVS needs to connect to it. It is better to stick with the default behavior and not to use this flag, unless: - You have Open vSwitch running under SELinux or AppArmor Mandatory Access Control that would prevent OVS from messing with sockets outside ordinary OVS directories. - You believe that relying on protocol handshakes (e.g. OpenFlow) is enough to prevent OVS to adversely interact with other daemons running on your system. - You don't have much worries of remote OVSDB exploits in the first place, because, perhaps, OVSDB manager is running on the same host as OVS and share similar attack vectors. * ``--oom-score=`` Sets the Linux Out-Of-Memory (OOM) killer score for the OVS daemon after it's been started. * ``--ulimit-core=`` Sets ulimit core file size for the OVS daemon after it's been started. * ``--ovsdb-server-priority=`` or ``--ovs-vswitchd-priority=`` Sets the ``nice(1)`` level used for each daemon. All of them default to ``-10``. * ``--ovsdb-server-wrapper=`` or ``--ovs-vswitchd-wrapper=`` Configures the specified daemon to run under , which is one of the following: * ``valgrind``: Run the daemon under ``valgrind(1)``, if it is installed, logging to ``.valgrind.log.`` in the log directory. * ``strace``: Run the daemon under ``strace(1)``, if it is installed, logging to ``.strace.log.`` in the log directory. * ``glibc``: Enable GNU C library features designed to find memory errors. By default, no wrapper is used. Each of the wrappers can expose bugs in Open vSwitch that lead to incorrect operation, including crashes. The ``valgrind`` and ``strace`` wrappers greatly slow daemon operations so they should not be used in production. They also produce voluminous logs that can quickly fill small disk partitions. The ``glibc`` wrapper is less resource-intensive but still somewhat slows the daemons. The following options control file locations. They should only be used if the default locations cannot be used. See ``FILES``, below, for more information. * ``--db-file=`` Overrides the file name for the OVS database. * ``--db-sock=`` Overrides the file name for the Unix domain socket used to connect to ``ovsdb-server``. * ``--db-schema=`` Overrides the file name for the OVS database schema. * ``--extra-dbs=`` Adds as an extra database for ``ovsdb-server`` to serve out. Multiple space-separated file names may also be specified. should begin with ``/``; if it does not, then it will be taken as relative to . The ``stop`` command -------------------- The ``stop`` command stops the ``ovs-vswitchd`` and ``ovsdb-server`` daemons. It does not unload the Open vSwitch kernel modules. It can take the same ``--no-ovsdb-server`` and ``--no-ovs-vswitchd`` options as that of the ``start`` command. This command does nothing and finishes successfully if the OVS daemons aren't running. The ``restart`` command ----------------------- The ``restart`` command performs a ``stop`` followed by a ``start`` command. The command can take the same options as that of the ``start`` command. In addition, it saves and restores OpenFlow flows for each individual bridge. The ``status`` command ---------------------- The ``status`` command checks whether the OVS daemons ``ovs-vswitchd`` and ``ovsdb-server`` are running and prints messages with that information. It exits with status 0 if the daemons are running, 1 otherwise. The ``version`` command ----------------------- The ``version`` command runs ``ovsdb-server --version`` and ``ovs-vswitchd --version``. The ``force-reload-kmod`` command --------------------------------- The ``force-reload-kmod`` command allows upgrading the Open vSwitch kernel module without rebooting. It performs the following tasks: 1. Gets a list of OVS "internal" interfaces, that is, network devices implemented by Open vSwitch. The most common examples of these are bridge "local ports". 2. Saves the OpenFlow flows of each bridge. 3. Stops the Open vSwitch daemons, as if by a call to ``ovs-ctl stop``. 4. Saves the kernel configuration state of the OVS internal interfaces listed in step 1, including IP and IPv6 addresses and routing table entries. 5. Unloads the Open vSwitch kernel module (including the bridge compatibility module if it is loaded). 6. Starts OVS back up, as if by a call to ``ovs-ctl start``. This reloads the kernel module, restarts the OVS daemons and finally restores the saved OpenFlow flows. 7. Restores the kernel configuration state that was saved in step 4. 8. Checks for daemons that may need to be restarted because they have packet sockets that are listening on old instances of Open vSwitch kernel interfaces and, if it finds any, prints a warning on stdout. DHCP is a common example: if the ISC DHCP client is running on an OVS internal interface, then it will have to be restarted after completing the above procedure. (It would be nice if ``ovs-ctl`` could restart daemons automatically, but the details are far too specific to a particular distribution and installation.) ``force-kmod-reload`` internally stops and starts OVS, so it accepts all of the options accepted by the ``start`` command except for the ``--no-ovs-vswitchd`` option. The ``load-kmod`` command ------------------------- The ``load-kmod`` command loads the openvswitch kernel modules if they are not already loaded. This operation also occurs as part of the ``start`` command. The motivation for providing the ``load-kmod`` command is to allow errors when loading modules to be handled separately from other errors that may occur when running the ``start`` command. By default the ``load-kmod`` command attempts to load the ``openvswitch`` kernel module. The ``enable-protocol`` command ------------------------------- The ``enable-protocol`` command checks for rules related to a specified protocol in the system's ``iptables(8)`` configuration. If there are no rules specifically related to that protocol, then it inserts a rule to accept the specified protocol. More specifically: * If ``iptables`` is not installed or not enabled, this command does nothing, assuming that lack of filtering means that the protocol is enabled. * If the ``INPUT`` chain has a rule that matches the specified protocol, then this command does nothing, assuming that whatever rule is installed reflects the system administrator's decisions. * Otherwise, this command installs a rule that accepts traffic of the specified protocol. This command normally completes successfully, even if it does nothing. Only the failure of an attempt to insert a rule normally causes it to return an exit code other than 0. The following options control the protocol to be enabled: * ``--protocol=`` The name of the IP protocol to be enabled, such as ``gre`` or ``tcp``. The default is ``gre``. * ``--sport=`` or ``--dport=`` TCP or UDP source or destination port to match. These are optional and allowed only with ``--protocol=tcp`` or ``--protocol=udp``. The ``delete-transient-ports`` command -------------------------------------- Deletes all ports that have the ``other_config:transient`` value set to true. The ``help`` command -------------------- Prints a usage message and exits successfully. Options ======= In addition to the options listed for each command above, these options control the behavior of several ``ovs-ctl`` commands. By default, ``ovs-ctl`` controls the ``ovsdb-server`` and ``ovs-vswitchd`` daemons. The following options restrict that control to exclude one or the other: * ``--no-ovsdb-server`` Specifies that the ``ovs-ctl`` commands ``start``, ``stop``, and ``restart`` should not modify the running status of ``ovsdb-server``. * ``--no-ovs-vswitchd`` Specifies that the ``ovs-ctl`` commands ``start``, ``stop``, and ``restart`` should not modify the running status of ``ovs-vswitchd``. It is an error to include this option with the ``force-reload-kmod`` command. Exit Status =========== ``ovs-ctl`` exits with status 0 on success and nonzero on failure. The ``start`` command is considered to succeed if OVS is already started; the ``stop`` command is considered to succeed if OVS is already stopped. Environment =========== The following environment variables affect ``ovs-ctl``: * ``PATH`` ``ovs-ctl`` does not hardcode the location of any of the programs that it runs. ``ovs-ctl`` will add the and that were specified at ``configure`` time to ``PATH``, if they are not already present. * ``OVS_LOGDIR``, ``OVS_RUNDIR``, ``OVS_DBDIR``, ``OVS_SYSCONFDIR``, ``OVS_PKGDATADIR``, ``OVS_BINDIR``, ``OVS_SBINDIR`` Setting one of these variables in the environment overrides the respective ``configure`` option, both for ``ovs-ctl`` itself and for the other Open vSwitch programs that it runs. Files ===== ``ovs-ctl`` uses the following files: * ``ovs-lib`` Shell function library used internally by ``ovs-ctl``. It must be installed in the same directory as ``ovs-ctl``. * ``/.log`` Per-daemon logfiles. * ``/.pid`` Per-daemon pidfiles to track whether a daemon is running and with what process ID. * ``/vswitch.ovsschema`` The OVS database schema used to initialize the database (use ``--db-schema`` to override this location). * ``/conf.db`` The OVS database (use ``--db-file`` to override this location). * ``/openvswitch/db.sock`` The Unix domain socket used for local communication with ``ovsdb-server`` (use ``--db-sock`` to override this location). * ``/openvswitch/system-id.conf`` The persistent system UUID created and read by ``--system-id=random``. * ``/openvswitch/system-type.conf`` and ``/openvswitch/system-version.conf`` The ``system-type`` and ``system-version`` values stored in the database's ``Open_vSwitch`` table when not specified as a command-line option. Example ======= The file ``debian/openvswitch-switch.init`` in the Open vSwitch source distribution is a good example of how to use ``ovs-ctl``. See Also ======== ``README.rst``, ``ovsdb-server(8)``, ``ovs-vswitchd(8)``. openvswitch-3.7.0~git20260211.8c6ebf8/Documentation/ref/ovs-flowviz.8.rst000066400000000000000000000362421514270232600255610ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. =========== ovs-flowviz =========== Synopsis ======== ``ovs-flowviz`` [``-i`` [*alias*,]\ *file* | ``--input`` [*alias*,]\ *file*] [``-c`` *file* | ``--config`` *file*] [``-f`` *filter* | ``--filter`` *filter*] [``-l`` *filter* | ``--highlight`` *filter*] [``--style`` *style*] *flow-type* *format* [*args*...] ``ovs-flowviz --help`` Description =========== ``ovs-flowviz`` helps visualize OpenFlow and datapath flow dumps in different formats in order to make them more easily understood. ``ovs-flowviz`` reads flows from ``stdin`` or from a *file* specified by the ``--input`` option, filters them, highlights them, and finally outputs them in one of the predefined *format*\ s. Options ======= .. program:: ovs-flowviz .. option:: -h, --help Print a brief help message to the console. .. option:: -i [,], --input [,] File to read flows from. If not provided, ``ovs-flowviz`` will read flows from stdin. This option can be specified multiple times. The file path can prepended by an alias that will be shown in the output. For example: ``--input node1,/path/to/file1 --input node2,/path/to/file2`` .. option:: -c , --config Style configuration file to use, overriding the default one. Styles defined in the style configuration file can be selected using the ``--style`` option. For more details on the style configuration file, see the `Style Configuration File`_ section below. .. option:: -f , --filter Flow filter expression. Only those flows matching the expression will be shown (although some formats implement filtering differently, see the `Datapath tree format`_ section below). The filtering syntax is detailed in `Filtering Syntax`_. .. option:: -l , --highlight Highlight the flows that match the provided *filter* expression. The filtering syntax is detailed in `Filtering Syntax`_. .. option:: --style """ STYLE = """ """ # noqa: E501 SCRIPT = """ """ # noqa: E501 def __init__(self, name, flowtree, opts): self.name = name.replace(" ", "_") self.tree = flowtree self.opts = opts self.formatter = HTMLFormatter(opts) @classmethod def head(cls): html = "" html += cls.STYLE html += "" return html @classmethod def begin_body(cls, opts): style = HTMLFormatter(opts).style bg = ( style.get("background").color if style.get("background") else "#f0f0f0" ) fg = style.get("default").color if style.get("default") else "black" return cls.BODY_STYLE.format(bg=bg, fg=fg) @classmethod def end_body(cls): return cls.SCRIPT def format(self): html_obj = f"
" html_obj += '
    ' for in_port in sorted(self.tree.recirc_nodes[0].keys()): node = self.tree.recirc_nodes[0][in_port] if node.visible: html_obj += "
  • " html_obj += self.format_recirc_node(node) html_obj += "
  • " html_obj += "
" html_obj += "
" return html_obj def format_recirc_node(self, node): html_obj = '
' html_obj += "[recirc_id({}) in_port({})]".format( hex(node.recirc), node.in_port ) html_obj += "
" html_obj += '
    ' # nested for block in node.visible_blocks(): html_block = "
  • " html_block += self.format_block(block) html_block += "
  • " html_obj += html_block html_obj += "
" return html_obj def format_single_block(self, block): block_id = "block_{}".format(block.flows[0].flow.id) html_obj = f'
' omit_first = { "actions": "all", } omit_rest = { "actions": "all", "match": [kv.key for _, kv in block.equal_match], } for i, flow in enumerate(filter(lambda x: x.visible, block.flows)): html_obj += '
' omit = omit_rest if i > 0 else omit_first buf = HTMLBuffer() hl = None if self.opts.get("highlight"): result = self.opts.get("highlight").evaluate(flow.flow) if result: hl = result.kv self.formatter.format_flow(buf, flow.flow, hl, omitted=omit) html_obj += buf.text html_obj += "
" html_obj += "
" # Match list. html_obj += '
  • ' html_obj += "
    " # Match list. if block.next_recirc_nodes: html_obj += '
    ' else: html_obj += '
    ' omit = { "match": "all", "info": "all", "ufid": "all", "dp_extra_info": "all", } buf = HTMLBuffer() buf.append_extra("actions: ", None) hl = None if self.opts.get("highlight"): result = self.opts.get("highlight").evaluate(block.flows[0].flow) if result: hl = result.kv self.formatter.format_flow(buf, block.flows[0].flow, hl, omitted=omit) html_obj += buf.text html_obj += "
    " return html_obj def format_block(self, block): html_obj = self.format_single_block(block) html_obj += '
      ' for node in block.next_recirc_nodes: if node.visible: html_obj += "
    • " html_obj += self.format_recirc_node(node) html_obj += "
    • " html_obj += "
    " html_obj += "
  • " html_obj += "
" return html_obj class HTMLTreeProcessor(FileProcessor): def __init__(self, opts): super().__init__(opts, "odp") self.trees = {} self.opts = opts self.tree = None self.curr_file = "" def start_file(self, name, filename): self.tree = FlowTree() self.curr_file = name def start_thread(self, name): if not self.tree: self.tree = FlowTree() def stop_thread(self, name): full_name = self.curr_file + f" ({name})" if self.tree: self.trees[full_name] = self.tree self.tree = None def process_flow(self, flow, name): self.tree.add(flow, self.opts.get("filter")) def process(self): super().process(False) def stop_file(self, name, filename): if self.tree: self.trees[name] = self.tree self.tree = None def print(self): html_obj = "" html_obj += "" html_obj += HTMLTree.head() html_obj += "" html_obj += "" html_obj += HTMLTree.begin_body(self.opts) for name, tree in self.trees.items(): tree.build() html_tree = HTMLTree(name, tree, self.opts) html_obj += "
" html_obj += "

{}

".format(name) html_obj += html_tree.format() html_obj += "
" html_obj += HTMLTree.end_body() html_obj += "" html_obj += "" print(html_obj) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/odp/tree.py000066400000000000000000000371361514270232600247150ustar00rootroot00000000000000# Copyright (c) 2023 Red Hat, Inc. # # 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. import sys from rich.style import Style from rich.console import Group from rich.panel import Panel from rich.text import Text from rich.tree import Tree from ovs.compat.sortedcontainers import SortedList from ovs.flowviz.console import ( ConsoleFormatter, ConsoleBuffer, hash_pallete, heat_pallete, file_header, ) from ovs.flowviz.process import ( FileProcessor, ) class TreeFlow(object): """A flow within a Tree.""" def __init__(self, flow, filter=None): self._flow = flow self._visible = True if filter: self._matches = filter.evaluate(flow) else: self._matches = True @property def flow(self): return self._flow @property def visible(self): return self._visible @visible.setter def visible(self, new_visible): self._visible = new_visible @property def matches(self): return self._matches class FlowBlock(object): """A block of flows in a Tree. Flows are arranged together in a block if they have the same action. """ def __init__(self, tflow): """Create a FlowBlock based on a flow. Args: flow: TreeFlow """ self._flows = SortedList([], self.__key) self._next_recirc_nodes = SortedList([], key=lambda x: -x.pkts) self._actions = tflow.flow.actions_kv self._sum_pkts = tflow.flow.info.get("packets") or 0 self._visible = False self._flows.add(tflow) self._equal_match = [ (i, kv) for i, kv in enumerate(tflow.flow.match_kv) if kv.key not in ["in_port", "recirc_id"] ] in_port = tflow.flow.match.get("in_port") self._next_recirc_inport = [ (recirc, in_port) for recirc in self._get_next_recirc(tflow.flow) ] @property def flows(self): return self._flows @property def pkts(self): return self._sum_pkts @property def visible(self): return self._visible @property def equal_match(self): return self._equal_match @property def next_recirc_nodes(self): return self._next_recirc_nodes def add_if_belongs(self, tflow): """Add TreeFlow to block if it belongs here.""" if not self._belongs(tflow): return False to_del = [] for i, (orig_i, kv) in enumerate(self.equal_match): if orig_i >= len(tflow.flow.match_kv): kv_i = None else: kv_i = tflow.flow.match_kv[orig_i] if kv_i != kv: to_del.append(i) for i in sorted(to_del, reverse=True): del self.equal_match[i] self._sum_pkts += tflow.flow.info.get("packets") or 0 self._flows.add(tflow) return True def build(self, recirc_nodes): """Populates next_recirc_nodes given a dictionary of RecircNode objects indexed by recirc_id and in_port. """ for recirc, in_port in self._next_recirc_inport: try: self._next_recirc_nodes.add(recirc_nodes[recirc][in_port]) except KeyError: print( f"mising [recirc_id {hex(recirc)} inport {in_port}]. " "Flow tree will be incomplete.", file=sys.stderr, ) def compute_visible(self): """Determines if the block should be visible. A FlowBlock is visible if any of its flows is. If any of the nested RecircNodes is visible, all flows should be visible. If not, only the ones that match should. """ nested_recirc_visible = False for recirc in self._next_recirc_nodes: recirc.compute_visible() if recirc.visible: nested_recirc_visible = True for tflow in self._flows: tflow.visible = True if nested_recirc_visible else tflow.matches if tflow.visible: self._visible = True def _belongs(self, tflow): if len(tflow.flow.actions_kv) != len(self._actions): return False return all( [a == b for a, b in zip(tflow.flow.actions_kv, self._actions)] ) def __key(self, f): return -(f.flow.info.get("packets") or 0) def _get_next_recirc(self, flow): """Get the next recirc_ids from a Flow. The recirc_id is obtained from actions such as recirc, but also complex actions such as check_pkt_len and sample Args: flow (ODPFlow): flow to get the recirc_id from. Returns: set of next recirculation ids. """ # Helper function to find a recirc in a dictionary of actions. def find_in_list(actions_list): recircs = [] for item in actions_list: (action, value) = next(iter(item.items())) if action == "recirc": recircs.append(value) elif action == "check_pkt_len": recircs.extend(find_in_list(value.get("gt"))) recircs.extend(find_in_list(value.get("le"))) elif action == "clone": recircs.extend(find_in_list(value)) elif action == "sample": recircs.extend(find_in_list(value.get("actions"))) return recircs recircs = [] recircs.extend(find_in_list(flow.actions)) return set(recircs) class RecircNode(object): def __init__(self, recirc, in_port, heat_map=[]): self._recirc = recirc self._in_port = in_port self._visible = False self._sum_pkts = 0 self._heat_map_fields = heat_map self._min = dict.fromkeys(self._heat_map_fields, -1) self._max = dict.fromkeys(self._heat_map_fields, 0) self._blocks = [] self._sorted_blocks = SortedList([], key=lambda x: -x.pkts) @property def recirc(self): return self._recirc @property def in_port(self): return self._in_port @property def visible(self): return self._visible @property def pkts(self): """Returns the blocks sorted by pkts. Should not be called before running build().""" return self._sum_pkts @property def min(self): return self._min @property def max(self): return self._max def visible_blocks(self): """Returns visible blocks sorted by pkts. Should not be called before running build().""" return filter(lambda x: x.visible, self._sorted_blocks) def add_flow(self, tflow): assert tflow.flow.match.get("recirc_id") == self.recirc assert tflow.flow.match.get("in_port") == self.in_port self._sum_pkts += tflow.flow.info.get("packets") or 0 # Accumulate minimum and maximum values for later use in heat-map. for field in self._heat_map_fields: val = tflow.flow.info.get(field) if self._min[field] == -1 or val < self._min[field]: self._min[field] = val if val > self._max[field]: self._max[field] = val for b in self._blocks: if b.add_if_belongs(tflow): return self._blocks.append(FlowBlock(tflow)) def build(self, recirc_nodes): """Builds the recirculation links of nested blocks. Args: recirc_nodes: Dictionary of RecircNode objects indexed by recirc_id and in_port. """ for block in self._blocks: block.build(recirc_nodes) self._sorted_blocks.add(block) def compute_visible(self): """Determine if the RecircNode should be visible. A RecircNode is visible if any of its blocks is. """ for block in self._blocks: block.compute_visible() if block.visible: self._visible = True class FlowTree: """A Flow tree is a a class that processes datapath flows into a tree based on recirculation ids. Args: flows (list[ODPFlow]): Optional, initial list of flows heat_map_fields (list[str]): Optional, info fields to calculate maximum and minimum values. """ def __init__(self, flows=None, heat_map_fields=[]): self._recirc_nodes = {} self._all_recirc_nodes = [] self._heat_map_fields = heat_map_fields if flows: for flow in flows: self.add(flow) @property def recirc_nodes(self): """Recirculation nodes in a double-dictionary. First-level key: recirc_id. Second-level key: in_port. """ return self._recirc_nodes @property def all_recirc_nodes(self): """All Recirculation nodes in a list.""" return self._all_recirc_nodes def add(self, flow, filter=None): """Add a flow""" rid = flow.match.get("recirc_id") or 0 in_port = flow.match.get("in_port") or 0 if not self._recirc_nodes.get(rid): self._recirc_nodes[rid] = {} if not self._recirc_nodes.get(rid).get(in_port): node = RecircNode(rid, in_port, heat_map=self._heat_map_fields) self._recirc_nodes[rid][in_port] = node self._all_recirc_nodes.append(node) self._recirc_nodes[rid][in_port].add_flow(TreeFlow(flow, filter)) def build(self): """Build the flow tree.""" for node in self._all_recirc_nodes: node.build(self._recirc_nodes) # Once recirculation links have been built. Determine what should stay # visible recursively starting by recirc_id = 0. for _, node in self._recirc_nodes.get(0).items(): node.compute_visible() def min_max(self): """Return a dictionary, indexed by the heat_map_fields, of minimum and maximum values. """ min_vals = {field: [] for field in self._heat_map_fields} max_vals = {field: [] for field in self._heat_map_fields} if not self._heat_map_fields: return None for node in self._all_recirc_nodes: if not node.visible: continue for field in self._heat_map_fields: min_vals[field].append(node.min[field]) max_vals[field].append(node.max[field]) return { field: ( min(min_vals[field]) if min_vals[field] else 0, max(max_vals[field]) if max_vals[field] else 0, ) for field in self._heat_map_fields } class ConsoleTreeProcessor(FileProcessor): def __init__(self, opts, heat_map=[]): super().__init__(opts, "odp") self.trees = {} self.ofconsole = ConsoleFormatter(self.opts) self.style = self.ofconsole.style self.heat_map = heat_map self.tree = None self.curr_file = "" if self.style: # Generate a color pallete for recirc ids. self.recirc_style_gen = hash_pallete( hue=[x / 50 for x in range(0, 50)], saturation=[0.7], value=[0.8], ) self.style.set_default_value_style(Style(color="grey66")) self.style.set_key_style("output", Style(color="green")) self.style.set_value_style("output", Style(color="green")) self.style.set_value_style("recirc", self.recirc_style_gen) self.style.set_value_style("recirc_id", self.recirc_style_gen) def start_file(self, name, filename): self.tree = FlowTree(heat_map_fields=self.heat_map) self.curr_file = name def start_thread(self, name): if not self.tree: self.tree = FlowTree(heat_map_fields=self.heat_map) def stop_thread(self, name): full_name = self.curr_file + f" ({name})" if self.tree: self.trees[full_name] = self.tree self.tree = None def process_flow(self, flow, name): self.tree.add(flow, self.opts.get("filter")) def process(self): super().process(False) def stop_file(self, name, filename): if self.tree: self.trees[name] = self.tree self.tree = None def print(self): for name, tree in self.trees.items(): self.ofconsole.console.print("\n") self.ofconsole.console.print(file_header(name)) tree.build() if self.style: min_max = tree.min_max() for field in self.heat_map: min_val, max_val = min_max[field] self.style.set_value_style( field, heat_pallete(min_val, max_val) ) self.print_tree(tree) def print_tree(self, tree): root = Tree("Datapath Flows (logical)") # Start by shoing recirc_id = 0 for in_port in sorted(tree.recirc_nodes[0].keys()): node = tree.recirc_nodes[0][in_port] if node.visible: self.print_recirc_node(root, node) self.ofconsole.console.print(root) def print_recirc_node(self, parent, node): if self.ofconsole.style: recirc_style = self.recirc_style_gen(hex(node.recirc)) else: recirc_style = None node_text = Text( "[recirc_id({}) in_port({})]".format( hex(node.recirc), node.in_port ), style=recirc_style, ) console_node = parent.add( Panel.fit(node_text), guide_style=recirc_style ) for block in node.visible_blocks(): self.print_block(block, console_node) def print_block(self, block, parent): # Print the flow matches and the statistics. flow_text = [] omit_first = { "actions": "all", } omit_rest = { "actions": "all", "match": [kv.key for _, kv in block.equal_match], } for i, flow in enumerate(filter(lambda x: x.visible, block.flows)): omit = omit_rest if i > 0 else omit_first buf = ConsoleBuffer(Text()) self.ofconsole.format_flow(buf, flow.flow, omitted=omit) flow_text.append(buf.text) # Print the action associated with the block. omit = { "match": "all", "info": "all", "ufid": "all", "dp_extra_info": "all", } act_buf = ConsoleBuffer(Text()) act_buf.append_extra("actions: ", Style(bold=(self.style is not None))) self.ofconsole.format_flow(act_buf, block.flows[0].flow, omitted=omit) flows_node = parent.add( Panel(Group(*flow_text)), guide_style=Style(color="default") ) action_node = flows_node.add( Panel.fit( act_buf.text, border_style="green" if self.style else "default" ), guide_style=Style(color="default"), ) # Nested to the action, print the next recirc nodes. for node in block.next_recirc_nodes: if node.visible: self.print_recirc_node(action_node, node) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ofp/000077500000000000000000000000001514270232600233745ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ofp/__init__.py000066400000000000000000000000001514270232600254730ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ofp/cli.py000066400000000000000000000151111514270232600245140ustar00rootroot00000000000000# Copyright (c) 2023 Red Hat, Inc. # # 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. import os import click from ovs.flowviz.main import maincli from ovs.flowviz.ofp.html import HTMLProcessor from ovs.flowviz.ofp.logic import CookieProcessor, LogicFlowProcessor from ovs.flowviz.process import ( ConsoleProcessor, JSONOpenFlowProcessor, ) @maincli.group(subcommand_metavar="FORMAT") @click.pass_obj def openflow(opts): """Process OpenFlow Flows.""" pass @openflow.command() @click.pass_obj def json(opts): """Print the flows in JSON format.""" proc = JSONOpenFlowProcessor(opts) proc.process() print(proc.json_string()) @openflow.command() @click.option( "-h", "--heat-map", is_flag=True, default=False, show_default=True, help="Create heat-map with packet and byte counters", ) @click.pass_obj def console(opts, heat_map): """Print the flows in the console with some style.""" proc = ConsoleProcessor( opts, "ofp", heat_map=["n_packets", "n_bytes"] if heat_map else [], ) proc.process() proc.print() def ovn_detrace_callback(ctx, param, value): """click callback to add detrace information to config object and set general ovn-detrace flag to True """ ctx.obj[param.name] = value if value != param.default: ctx.obj["ovn_detrace_flag"] = True return value @openflow.command() @click.option( "-d", "--ovn-detrace", "ovn_detrace_flag", is_flag=True, show_default=True, help="Use ovn-detrace to extract cookie information (implies '-c')", ) @click.option( "--ovn-detrace-path", default="/usr/bin", type=click.Path(), help="Use an alternative path to where ovn_detrace.py is located. " "Instead of using this option you can just set PYTHONPATH accordingly.", show_default=True, callback=ovn_detrace_callback, ) @click.option( "--ovnnb-db", default=os.getenv("OVN_NB_DB") or "unix:/var/run/ovn/ovnnb_db.sock", help="Specify the OVN NB database string (implies -d). " "If the OVN_NB_DB environment variable is set, it's used as default. " "Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock", callback=ovn_detrace_callback, ) @click.option( "--ovnsb-db", default=os.getenv("OVN_SB_DB") or "unix:/var/run/ovn/ovnsb_db.sock", help="Specify the OVN NB database string (implies -d). " "If the OVN_NB_DB environment variable is set, it's used as default. " "Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock", callback=ovn_detrace_callback, ) @click.option( "-o", "--ovn-filter", help="Specify a filter to be run on ovn-detrace information (implied -d). " "Format: python regular expression " "(see https://docs.python.org/3/library/re.html)", callback=ovn_detrace_callback, ) @click.option( "-s", "--show-flows", is_flag=True, default=False, show_default=True, help="Show the full flows under each logical flow", ) @click.option( "-c", "--cookie", "cookie_flag", is_flag=True, default=False, show_default=True, help="Consider the cookie in the logical flow", ) @click.option( "-h", "--heat-map", is_flag=True, default=False, show_default=True, help="Create heat-map with packet and byte counters (when -s is used)", ) @click.pass_obj def logic( opts, ovn_detrace_flag, ovn_detrace_path, ovnnb_db, ovnsb_db, ovn_filter, show_flows, cookie_flag, heat_map, ): """ Print the logical structure of the flows. First, sorts the flows based on tables and priorities. Then, deduplicates logically equivalent flows: these a flows that match on the same set of fields (regardless of the values they match against), have the same priority, and actions (regardless of action arguments, except in the case of output and recirculate). Optionally, the cookie can also be considered to be part of the logical flow. """ if ovn_detrace_flag: opts["ovn_detrace_flag"] = True if opts.get("ovn_detrace_flag"): cookie_flag = True processor = LogicFlowProcessor(opts, cookie_flag, heat_map) processor.process() processor.print(show_flows) @openflow.command() @click.option( "-d", "--ovn-detrace", "ovn_detrace_flag", is_flag=True, show_default=True, help="Use ovn-detrace to extract cookie information", ) @click.option( "--ovn-detrace-path", default="/usr/bin", type=click.Path(), help="Use an alternative path to where ovn_detrace.py is located. " "Instead of using this option you can just set PYTHONPATH accordingly", show_default=True, callback=ovn_detrace_callback, ) @click.option( "--ovnnb-db", default=os.getenv("OVN_NB_DB") or "unix:/var/run/ovn/ovnnb_db.sock", help="Specify the OVN NB database string (implies -d). " "If the OVN_NB_DB environment variable is set, it's used as default. " "Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock", callback=ovn_detrace_callback, ) @click.option( "--ovnsb-db", default=os.getenv("OVN_SB_DB") or "unix:/var/run/ovn/ovnsb_db.sock", help="Specify the OVN NB database string (implies -d). " "If the OVN_NB_DB environment variable is set, it's used as default. " "Otherwise, the default is unix:/var/run/ovn/ovnnb_db.sock", callback=ovn_detrace_callback, ) @click.option( "-o", "--ovn-filter", help="Specify a filter to be run on ovn-detrace information (implied -d). " "Format: python regular expression " "(see https://docs.python.org/3/library/re.html)", callback=ovn_detrace_callback, ) @click.pass_obj def cookie( opts, ovn_detrace_flag, ovn_detrace_path, ovnnb_db, ovnsb_db, ovn_filter ): """Print the flow tables sorted by cookie.""" if ovn_detrace_flag: opts["ovn_detrace_flag"] = True processor = CookieProcessor(opts) processor.process() processor.print() @openflow.command() @click.pass_obj def html(opts): """Print the flows in an linked HTML list arranged by tables.""" processor = HTMLProcessor(opts) processor.process() print(processor.html()) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ofp/html.py000066400000000000000000000066251514270232600247230ustar00rootroot00000000000000# Copyright (c) 2023 Red Hat, Inc. # # 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. from ovs.flowviz.html_format import HTMLBuffer, HTMLFormatter, HTMLStyle from ovs.flowviz.process import ( FileProcessor, ) class HTMLProcessor(FileProcessor): """File processor that prints OpenFlow tables in HTML.""" def __init__(self, opts): super().__init__(opts, "ofp") self.formatter = HTMLFormatter(self.opts) self.data = dict() def start_file(self, name, filename): self.tables = dict() def stop_file(self, name, filename): self.data[name] = self.tables def process_flow(self, flow, name): table = flow.info.get("table") or 0 if not self.tables.get(table): self.tables[table] = list() self.tables[table].append(flow) def html(self): bg = ( self.formatter.style.get("background").color if self.formatter.style.get("background") else "white" ) fg = ( self.formatter.style.get("default").color if self.formatter.style.get("default") else "black" ) html_obj = """ """.format( bg=bg, fg=fg ) for name, tables in self.data.items(): name = name.replace(" ", "_") html_obj += "

{}

".format(name) html_obj += "
" for table, flows in tables.items(): def anchor(x): return "#table_%s_%s" % (name, x.value["table"]) resubmit_style = self.formatter.style.get("value.resubmit") resubmit_color = resubmit_style.color if resubmit_style else fg self.formatter.style.set_value_style( "resubmit", HTMLStyle( resubmit_color, anchor_gen=anchor, ), ) html_obj += ( "

Table {table}

".format( name=name, table=table ) ) html_obj += "
    ".format(table) for flow in flows: html_obj += "
  • ".format(flow.id) highlighted = None if self.opts.get("highlight"): result = self.opts.get("highlight").evaluate(flow) if result: highlighted = result.kv buf = HTMLBuffer() self.formatter.format_flow(buf, flow, highlighted) html_obj += buf.text html_obj += "
  • " html_obj += "
" html_obj += "
" return html_obj openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ofp/logic.py000066400000000000000000000310411514270232600250420ustar00rootroot00000000000000# Copyright (c) 2023 Red Hat, Inc. # # 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. import sys import io import re from rich.tree import Tree from rich.text import Text from ovs.flowviz.process import FileProcessor from ovs.flowviz.console import ( ConsoleFormatter, ConsoleBuffer, hash_pallete, file_header, heat_pallete, ) class LFlow: """A Logical Flow represents the skeleton of a flow. Two logical flows have the same logical representation if they match against the same fields (regardless of the matching value) and have the same set of actions (regardless of the actions' arguments, except for those in the exact_actions list). Attributes: flow (OFPFlow): The flow exact_actions(list): Optional; list of action keys that are considered unique if the value is also the same. match_cookie (bool): Optional; if cookies are part of the logical flow """ def __init__(self, flow, exact_actions=[], match_cookie=False): self.cookie = flow.info.get("cookie") or 0 if match_cookie else None self.priority = flow.match.get("priority") or 0 self.match_keys = tuple([kv.key for kv in flow.match_kv]) self.action_keys = tuple( [ kv.key for kv in flow.actions_kv if kv.key not in exact_actions ] ) self.match_action_kvs = [ kv for kv in flow.actions_kv if kv.key in exact_actions ] def __eq__(self, other): return ( (self.cookie == other.cookie if self.cookie else True) and self.priority == other.priority and self.action_keys == other.action_keys and self.equal_match_action_kvs(other) and self.match_keys == other.match_keys ) def equal_match_action_kvs(self, other): """ Compares the logical flow's match action key-values with the others. Args: other (LFlow): The other LFlow to compare against Returns true if both LFlow have the same action k-v. """ if len(other.match_action_kvs) != len(self.match_action_kvs): return False for kv in self.match_action_kvs: found = False for other_kv in other.match_action_kvs: if self.match_kv(kv, other_kv): found = True break if not found: return False return True def match_kv(self, one, other): """Compares a KeyValue. Args: one, other (KeyValue): The objects to compare Returns true if both KeyValue objects have the same key and value """ return one.key == other.key and one.value == other.value def __hash__(self): hash_data = [ self.cookie, self.priority, self.action_keys, tuple((kv.key, str(kv.value)) for kv in self.match_action_kvs), self.match_keys, ] if self.cookie: hash_data.append(self.cookie) return tuple(hash_data).__hash__() def format(self, buf, formatter): """Format the Logical Flow into a Buffer.""" if self.cookie: buf.append_extra( "cookie={} ".format(hex(self.cookie)).ljust(18), style=cookie_style_gen(str(self.cookie)), ) buf.append_extra( "priority={} ".format(self.priority), style="steel_blue" ) buf.append_extra(",".join(self.match_keys), style="steel_blue") buf.append_extra(" ---> ", style="bold magenta") buf.append_extra(",".join(self.action_keys), style="steel_blue") if len(self.match_action_kvs) > 0: buf.append_extra(" ", style=None) for kv in self.match_action_kvs: formatter.format_kv(buf, kv, formatter.style) buf.append_extra(",", style=None) class LogicFlowProcessor(FileProcessor): def __init__(self, opts, match_cookie, heat_map): super().__init__(opts, "ofp") self.data = dict() self.min_max = dict() self.match_cookie = match_cookie self.heat_map = ["n_packets", "n_bytes"] if heat_map else [] self.ovn_detrace = ( OVNDetrace(opts) if opts.get("ovn_detrace_flag") else None ) def start_file(self, name, filename): if len(self.heat_map) > 0: self.min = [-1] * len(self.heat_map) self.max = [0] * len(self.heat_map) self.tables = dict() def stop_file(self, name, filename): if len(self.heat_map) > 0: self.min_max[name] = (self.min, self.max) self.data[name] = self.tables def process_flow(self, flow, name): """Sort the flows by table and logical flow.""" # Running calculation of min and max values for all the fields that # take place in the heatmap. for i, field in enumerate(self.heat_map): val = flow.info.get(field) if self.min[i] == -1 or val < self.min[i]: self.min[i] = val if val > self.max[i]: self.max[i] = val table = flow.info.get("table") or 0 if not self.tables.get(table): self.tables[table] = dict() # Group flows by logical hash lflow = LFlow( flow, exact_actions=["output", "resubmit", "drop"], match_cookie=self.match_cookie, ) if not self.tables[table].get(lflow): self.tables[table][lflow] = list() self.tables[table][lflow].append(flow) def print(self, show_flows): formatter = ConsoleFormatter(opts=self.opts) console = formatter.console for name, tables in self.data.items(): console.print("\n") console.print(file_header(name)) tree = Tree("Ofproto Flows (logical)") for table_num in sorted(tables.keys()): table = tables[table_num] table_tree = tree.add("** TABLE {} **".format(table_num)) if len(self.heat_map) > 0 and len(table.values()) > 0: for i, field in enumerate(self.heat_map): (min_val, max_val) = self.min_max[name][i] formatter.style.set_value_style( field, heat_pallete(min_val, max_val) ) for lflow in sorted( table.keys(), key=(lambda x: x.priority), reverse=True, ): flows = table[lflow] ovn_info = None if self.ovn_detrace: ovn_info = self.ovn_detrace.get_ovn_info(lflow.cookie) if self.opts.get("ovn_filter"): ovn_regexp = re.compile( self.opts.get("ovn_filter") ) if not ovn_regexp.search(ovn_info): continue buf = ConsoleBuffer(Text()) lflow.format(buf, formatter) buf.append_extra( " ( x {} )".format(len(flows)), style="dark_olive_green3", ) lflow_tree = table_tree.add(buf.text) if ovn_info: ovn = lflow_tree.add("OVN Info") for part in ovn_info.split("\n"): if part.strip(): ovn.add(part.strip()) if show_flows: for flow in flows: buf = ConsoleBuffer(Text()) highlighted = None if self.opts.get("highlight"): result = self.opts.get("highlight").evaluate( flow ) if result: highlighted = result.kv formatter.format_flow(buf, flow, highlighted) lflow_tree.add(buf.text) console.print(tree) class OVNDetrace(object): def __init__(self, opts): if not opts.get("ovn_detrace_flag"): raise Exception("Cannot initialize OVN Detrace connection") if opts.get("ovn_detrace_path"): sys.path.append(opts.get("ovn_detrace_path")) import ovn_detrace class FakePrinter(ovn_detrace.Printer): def __init__(self): self.buff = io.StringIO() def print_p(self, msg): print(" * ", msg, file=self.buff) def print_h(self, msg): print(" * ", msg, file=self.buff) def clear(self): self.buff = io.StringIO() self.ovn_detrace = ovn_detrace self.ovnnb_conn = ovn_detrace.OVSDB( opts.get("ovnnb_db"), "OVN_Northbound" ) self.ovnsb_conn = ovn_detrace.OVSDB( opts.get("ovnsb_db"), "OVN_Southbound" ) self.ovn_printer = FakePrinter() self.cookie_handlers = ovn_detrace.get_cookie_handlers( self.ovnnb_conn, self.ovnsb_conn, self.ovn_printer ) def get_ovn_info(self, cookie): self.ovn_printer.clear() self.ovn_detrace.print_record_from_cookie( self.ovnsb_conn, self.cookie_handlers, "{:x}".format(cookie) ) return self.ovn_printer.buff.getvalue() # Try to make it easy to spot same cookies by printing them in different # colors cookie_style_gen = hash_pallete( hue=[x / 10 for x in range(0, 10)], saturation=[0.5], value=[0.5 + x / 10 * (0.85 - 0.5) for x in range(0, 10)], ) class CookieProcessor(FileProcessor): """Processor that sorts flows into cookies and tables.""" def __init__(self, opts): super().__init__(opts, "ofp") self.data = dict() self.ovn_detrace = ( OVNDetrace(opts) if opts.get("ovn_detrace_flag") else None ) def start_file(self, name, filename): self.cookies = dict() def stop_file(self, name, filename): self.data[name] = self.cookies def process_flow(self, flow, name): """Sort the flows by table and logical flow.""" cookie = flow.info.get("cookie") or 0 if not self.cookies.get(cookie): self.cookies[cookie] = dict() table = flow.info.get("table") or 0 if not self.cookies[cookie].get(table): self.cookies[cookie][table] = list() self.cookies[cookie][table].append(flow) def print(self): ofconsole = ConsoleFormatter(opts=self.opts) console = ofconsole.console for name, cookies in self.data.items(): console.print("\n") console.print(file_header(name)) tree = Tree("Ofproto Cookie Tree") for cookie, tables in cookies.items(): ovn_info = None if self.ovn_detrace: ovn_info = self.ovn_detrace.get_ovn_info(cookie) if self.opts.get("ovn_filter"): ovn_regexp = re.compile(self.opts.get("ovn_filter")) if not ovn_regexp.search(ovn_info): continue cookie_tree = tree.add("** Cookie {} **".format(hex(cookie))) if ovn_info: ovn = cookie_tree.add("OVN Info") for part in ovn_info.split("\n"): if part.strip(): ovn.add(part.strip()) tables_tree = cookie_tree.add("Tables") for table, flows in tables.items(): table_tree = tables_tree.add("* Table {} * ".format(table)) for flow in flows: buf = ConsoleBuffer(Text()) ofconsole.format_flow(buf, flow) table_tree.add(buf.text) console.print(tree) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ovs-flowviz000077500000000000000000000012561514270232600250470ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022,2023 Red Hat, Inc. # # 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. from ovs.flowviz import main if __name__ == '__main__': main.main() openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/ovs-flowviz.conf000066400000000000000000000072601514270232600257710ustar00rootroot00000000000000# Create any number of styles.{style_name} sections with a defined style. # # Syntax: # # [FORMAT].[PORTION].[SELECTOR].[ELEMENT] = [VALUE] # # * FORMAT: console or html # * PORTION: The portion of the flow that the style applies to # - key: Selects how to print the key of a KeyValue pair # - key: Selects how to print the value of a KeyValue pair # - flag: Selects how to print the a flag # - delim: Selects how to print the delimiters around key and values # # * SELECTOR: # - highlighted: to apply when the key is highlighted # - type.{TYPE}: to apply when the value matches a type # (special types such as IPAddress or EthMask can be used) # (only aplicable to 'value') # - {key_name}: to apply when the key matches the key_name # # Console Styles # ============== # * ELEMENT: # - color: defines the color in hex or a color rich starndard ones [1] # - underline: if set to "true", the selected portion will be underlined # #[1] https://rich.readthedocs.io/en/stable/appendix/colors.html#standard-colors # # HTML Styles # ============== # * PORTION: An extra portion is supported: "background" which defines the # background color of the page. # * ELEMENT: # - color: defines the color in hex format [styles.dark] # defaults for key-values console.key.color = #5D86BA console.value.color= #B0C4DE console.delim.color= #B0C4DE console.default.color= #FFFFFF # defaults for special types console.value.type.IPAddress.color = #008700 console.value.type.IPMask.color = #008700 console.value.type.EthMask.color = #008700 # dim some long arguments console.value.ct.color = grey66 console.value.ufid.color = grey66 console.value.clone.color = grey66 console.value.controller.color = grey66 # highlight flags console.flag.color = #875fff # show drop and recirculations console.key.drop.color = red console.key.resubmit.color = #00d700 console.key.output.color = #00d700 console.value.output.color = #00d700 # highlights console.key.highlighted.color = red console.key.highlighted.underline = true console.value.highlighted.underline = true console.delim.highlighted.underline = true # html html.background.color = #23282e html.default.color = white html.key.color = #5D86BA html.value.color = #B0C4DE html.delim.color = #B0C4DE html.key.resubmit.color = #005f00 html.key.recirc.color = #005f00 html.value.resubmit.color = #005f00 html.value.recirc.color = #005f00 html.key.output.color = #00d700 html.value.output.color = #00d700 html.key.highlighted.color = #FF00FF html.value.highlighted.color = #FF00FF html.key.drop.color = red [styles.light] # If a color is omitted, the default terminal color will be used # highlight keys console.key.color = blue # special types console.value.type.IPAddress.color = #008700 console.value.type.IPMask.color = #008700 console.value.type.EthMask.color = #008700 # dim long arguments console.value.ct.color = bright_black console.value.ufid.color = #870000 console.value.clone.color = bright_black console.value.controller.color = bright_black # highlight flags console.flag.color = #00005F # show drop and recirculations console.key.drop.color = red console.key.resubmit.color = #00d700 console.key.output.color = #005f00 console.value.output.color = #00d700 # highlights console.key.highlighted.color = #f20905 console.value.highlighted.color = #f20905 console.key.highlighted.underline = true console.value.highlighted.underline = true console.delim.highlighted.underline = true # html html.background.color = white html.key.color = #00005f html.value.color = #870000 html.key.resubmit.color = #00d700 html.key.output.color = #005f00 html.value.output.color = #00d700 html.key.highlighted.color = #FF00FF html.value.highlighted.color = #FF00FF openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/flowviz/process.py000066400000000000000000000266151514270232600246520ustar00rootroot00000000000000# Copyright (c) 2023 Red Hat, Inc. # # 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. import sys import json import click from ovs.flow.decoders import FlowEncoder from ovs.flow.odp import ODPFlow from ovs.flow.ofp import OFPFlow from ovs.flowviz.console import ( ConsoleFormatter, default_highlight, file_header, heat_pallete, ) from ovs.flowviz.format import FlowStyle class FileProcessor(object): """Base class for file-based Flow processing. It is able to create flows from strings found in a file (or stdin). The process of parsing the flows is extendable in many ways by deriving this class. When process() is called, the base class will: - call self.start_file() for each new file that get's processed - call self.start_thread() for each thread (Datapath flow only) - apply the filter defined in opts if provided (can be optionally disabled) - call self.process_flow() for after the flow has been filtered - call self.stop_thread() after the thread block has been processed (Datapath flow only) - call self.stop_file() after the file has been processed entirely In the case of stdin, the filename and file alias is 'stdin'. Args: opts (dict): Options dictionary flow_type (str): ["ofp", "odp"] """ def __init__(self, opts, flow_type): self.opts = opts assert flow_type in ["ofp", "odp"] self.flow_type = flow_type self.current_thread = None # Methods that must be implemented by derived classes. def init(self): """Called before the flow processing begins.""" pass def start_file(self, alias, filename): """Called before the processing of a file begins. Args: alias(str): The alias name of the filename filename(str): The filename string """ pass def start_thread(self, thread): """Called before the processing of a thread block begins. Args: thread(str): The thread name ("main" or "pmd at cpu core $N") """ raise NotImplementedError def process_flow(self, flow, name): """Called for built flow (after filtering). Args: flow(Flow): The OpenFlow or Datapath flow. name(str): The name of the file from which the flow comes """ raise NotImplementedError def stop_thread(self, thread): """Called after the processing of a thread ends. Args: thread(str): The thread name ("main" or "pmd at cpu core $N") """ raise NotImplementedError def stop_file(self, alias, filename): """Called after the processing of a file ends. Args: alias(str): The alias name of the filename filename(str): The filename string """ pass def end(self): """Called after the processing ends.""" pass def process_line(self, line, idx): if self.flow_type == "odp": next_thread = self.current_thread if line.startswith("flow-dump from the main thread"): next_thread = "main" elif line.startswith("flow-dump from pmd on cpu core"): next_thread = line.removeprefix("flow-dump from ").strip("\n") if next_thread != self.current_thread: if self.current_thread: self.stop_thread(self.current_thread) self.start_thread(next_thread) self.current_thread = next_thread return None return ODPFlow(line, idx) elif self.flow_type == "ofp": # Skip strings commonly found in OpenFlow flow dumps. if " reply " in line: return None return OFPFlow(line, idx) def process(self, do_filter=True): idx = 0 filenames = self.opts.get("filename") filt = self.opts.get("filter") if do_filter else None self.init() if filenames: for alias, filename in filenames: try: with open(filename) as f: self.start_file(alias, filename) for line in f: flow = self.process_line(line, idx) idx += 1 if not flow or (filt and not filt.evaluate(flow)): continue self.process_flow(flow, alias) if self.current_thread: self.stop_thread(self.current_thread) self.stop_file(alias, filename) except IOError as e: raise click.BadParameter( "Failed to read from file {} ({}): {}".format( filename, e.errno, e.strerror ) ) else: data = sys.stdin.read() self.start_file("stdin", "stdin") for line in data.split("\n"): line = line.strip() if line: flow = self.process_line(line, idx) idx += 1 if ( not flow or not getattr(flow, "_sections", None) or (filt and not filt.evaluate(flow)) ): continue self.process_flow(flow, "stdin") if self.current_thread: self.stop_thread(self.current_thread) self.stop_file("stdin", "stdin") self.end() class JSONOpenFlowProcessor(FileProcessor): """A FileProcessor that prints OpenFlow flows in JSON format.""" def __init__(self, opts): super().__init__(opts, "ofp") self.flows = dict() def start_file(self, name, filename): self.flows_list = list() def stop_file(self, name, filename): self.flows[name] = self.flows_list def process_flow(self, flow, name): self.flows_list.append(flow) def json_string(self): if len(self.flows.keys()) > 1: return json.dumps( [ {"name": name, "flows": [flow.dict() for flow in flows]} for name, flows in self.flows.items() ], indent=4, cls=FlowEncoder, ) return json.dumps( [flow.dict() for flow in self.flows_list], indent=4, cls=FlowEncoder, ) class JSONDatapathProcessor(FileProcessor): """A FileProcessor that prints Datapath flows in JSON format.""" def __init__(self, opts): super().__init__(opts, "odp") self.data = {} self.thread = None self.file = None def start_file(self, name, filename): self.per_thread_flows = None self.flows_list = [] def start_thread(self, name): if not self.per_thread_flows: self.per_thread_flows = {} def stop_thread(self, name): self.per_thread_flows[name] = self.flows_list def stop_file(self, name, filename): if self.per_thread_flows: self.data[name] = self.per_thread_flows else: self.data[name] = self.flows_list def process_flow(self, flow, name): self.flows_list.append(flow) def json_string(self): opts = { "indent": 4, "cls": FlowEncoder, } def thread_data(data): if isinstance(data, dict): return { thread: [flow.dict() for flow in flows] for thread, flows in data.items() } return [flow.dict() for flow in data] if len(self.data.keys()) > 1: jsondata = {} for file, file_data in self.data.items(): jsondata[file] = thread_data(file_data) return json.dumps(jsondata, **opts) else: return json.dumps( thread_data(next(iter(self.data.values()))), **opts ) class ConsoleProcessor(FileProcessor): """A generic Console Processor that prints flows into the console""" def __init__(self, opts, flow_type, heat_map=[]): super().__init__(opts, flow_type) self.heat_map = heat_map self.console = ConsoleFormatter(opts) if not self.console.style and self.opts.get("highlight"): # Add some style to highlights or else they won't be seen. self.console.style = FlowStyle() self.console.style.set_default_value_style( default_highlight(), True ) self.console.style.set_default_key_style(default_highlight(), True) self.flows = dict() # Dict of flow-lists, one per file and thread. self.min_max = dict() # Used for heat-map calculation. self.curr_file = None self.flows_list = None def _init_list(self): self.flows_list = list() if len(self.heat_map) > 0: self.min = [-1] * len(self.heat_map) self.max = [0] * len(self.heat_map) def _save_list(self, name): if self.flows_list: self.flows[name] = self.flows_list self.flows_list = None if len(self.heat_map) > 0: self.min_max[name] = (self.min, self.max) def start_file(self, name, filename): self._init_list() self.curr_file = name def start_thread(self, name): if not self.flows_list: self._init_list() def stop_thread(self, name): full_name = self.curr_file + f" ({name})" self._save_list(full_name) def stop_file(self, name, filename): self._save_list(name) def process_flow(self, flow, name): # Running calculation of min and max values for all the fields that # take place in the heatmap. for i, field in enumerate(self.heat_map): val = flow.info.get(field) if self.min[i] == -1 or val < self.min[i]: self.min[i] = val if val > self.max[i]: self.max[i] = val self.flows_list.append(flow) def print(self): for name, flows in self.flows.items(): self.console.console.print("\n") self.console.console.print(file_header(name)) if len(self.heat_map) > 0 and len(self.flows) > 0: for i, field in enumerate(self.heat_map): (min_val, max_val) = self.min_max[name][i] self.console.style.set_value_style( field, heat_pallete(min_val, max_val) ) for flow in flows: high = None if self.opts.get("highlight"): result = self.opts.get("highlight").evaluate(flow) if result: high = result.kv self.console.print_flow(flow, high) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/json.py000066400000000000000000000407131514270232600224400ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # 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. from __future__ import absolute_import import functools import json import re import sys PARSER_C = 'C' PARSER_PY = 'PYTHON' try: import ovs._json PARSER = PARSER_C except ImportError: PARSER = PARSER_PY __pychecker__ = 'no-stringiter' SPACES_PER_LEVEL = 2 dumper = functools.partial(json.dumps, separators=(",", ":")) def to_stream(obj, stream, pretty=False, sort_keys=True): stream.write(dumper(obj, indent=SPACES_PER_LEVEL if pretty else None, sort_keys=sort_keys)) def to_file(obj, name, pretty=False, sort_keys=True): with open(name, "w") as stream: to_stream(obj, stream, pretty, sort_keys) def to_string(obj, pretty=False, sort_keys=True): return dumper(obj, indent=SPACES_PER_LEVEL if pretty else None, sort_keys=sort_keys) def from_stream(stream): p = Parser(check_trailer=True) while True: buf = stream.read(4096) if buf == "" or p.feed(buf) != len(buf): break return p.finish() def from_file(name): stream = open(name, "r") try: return from_stream(stream) finally: stream.close() def from_string(s): if not isinstance(s, str): # We assume the input is a string. We will only hit this case for a # str in Python 2 which is not unicode, so we need to go ahead and # decode it. try: s = str(s, 'utf-8') except UnicodeDecodeError as e: seq = ' '.join(["0x%2x" % ord(c) for c in e.object[e.start:e.end] if ord(c) >= 0x80]) return "not a valid UTF-8 string: invalid UTF-8 sequence %s" % seq p = Parser(check_trailer=True) p.feed(s) return p.finish() class Parser(object): # Maximum height of parsing stack. # MAX_HEIGHT = 1000 def __new__(cls, *args, **kwargs): if PARSER == PARSER_C: return ovs._json.Parser(*args, **kwargs) return super(Parser, cls).__new__(cls) def __init__(self, check_trailer=False): self.check_trailer = check_trailer # Lexical analysis. self.lex_state = Parser.__lex_start self.buffer = "" self.line_number = 0 self.column_number = 0 self.byte_number = 0 # Parsing. self.parse_state = Parser.__parse_start self.stack = [] self.member_name = None # Parse status. self.done = False self.error = None def __lex_start_space(self, c): pass def __lex_start_alpha(self, c): self.buffer = c self.lex_state = Parser.__lex_keyword def __lex_start_token(self, c): self.__parser_input(c) def __lex_start_number(self, c): self.buffer = c self.lex_state = Parser.__lex_number def __lex_start_string(self, _): self.lex_state = Parser.__lex_string def __lex_start_error(self, c): if ord(c) >= 32 and ord(c) < 128: self.__error("invalid character '%s'" % c) else: self.__error("invalid character U+%04x" % ord(c)) __lex_start_actions = {} for c in " \t\n\r": __lex_start_actions[c] = __lex_start_space for c in "abcdefghijklmnopqrstuvwxyz": __lex_start_actions[c] = __lex_start_alpha for c in "[{]}:,": __lex_start_actions[c] = __lex_start_token for c in "-0123456789": __lex_start_actions[c] = __lex_start_number __lex_start_actions['"'] = __lex_start_string def __lex_start(self, c): Parser.__lex_start_actions.get( c, Parser.__lex_start_error)(self, c) return True __lex_alpha = {} for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": __lex_alpha[c] = True def __lex_finish_keyword(self): if self.buffer == "false": self.__parser_input(False) elif self.buffer == "true": self.__parser_input(True) elif self.buffer == "null": self.__parser_input(None) else: self.__error("invalid keyword '%s'" % self.buffer) def __lex_keyword(self, c): if c in Parser.__lex_alpha: self.buffer += c return True else: self.__lex_finish_keyword() return False __number_re = re.compile("(-)?(0|[1-9][0-9]*)" r"(?:\.([0-9]+))?(?:[eE]([-+]?[0-9]+))?$") def __lex_finish_number(self): s = self.buffer m = Parser.__number_re.match(s) if m: sign, integer, fraction, exp = m.groups() if (exp is not None and (int(exp) > sys.maxsize or int(exp) < -sys.maxsize - 1)): self.__error("exponent outside valid range") return if fraction is not None and len(fraction.lstrip('0')) == 0: fraction = None sig_string = integer if fraction is not None: sig_string += fraction significand = int(sig_string) pow10 = 0 if fraction is not None: pow10 -= len(fraction) if exp is not None: pow10 += int(exp) if significand == 0: self.__parser_input(0) return elif significand <= 2 ** 63: while pow10 > 0 and significand <= 2 ** 63: significand *= 10 pow10 -= 1 while pow10 < 0 and significand % 10 == 0: significand //= 10 pow10 += 1 if (pow10 == 0 and ((not sign and significand < 2 ** 63) or (sign and significand <= 2 ** 63))): if sign: self.__parser_input(-significand) else: self.__parser_input(significand) return value = float(s) if value == float("inf") or value == float("-inf"): self.__error("number outside valid range") return if value == 0: # Suppress negative zero. value = 0 self.__parser_input(value) elif re.match("-?0[0-9]", s): self.__error("leading zeros not allowed") elif re.match("-([^0-9]|$)", s): self.__error("'-' must be followed by digit") elif re.match(r"-?(0|[1-9][0-9]*)\.([^0-9]|$)", s): self.__error("decimal point must be followed by digit") elif re.search("e[-+]?([^0-9]|$)", s): self.__error("exponent must contain at least one digit") else: self.__error("syntax error in number") def __lex_number(self, c): if c in ".0123456789eE-+": self.buffer += c return True else: self.__lex_finish_number() return False __4hex_re = re.compile("[0-9a-fA-F]{4}") def __lex_4hex(self, s): if len(s) < 4: self.__error("quoted string ends within \\u escape") elif not Parser.__4hex_re.match(s): self.__error("malformed \\u escape") elif s == "0000": self.__error("null bytes not supported in quoted strings") else: return int(s, 16) @staticmethod def __is_leading_surrogate(c): """Returns true if 'c' is a Unicode code point for a leading surrogate.""" return c >= 0xd800 and c <= 0xdbff @staticmethod def __is_trailing_surrogate(c): """Returns true if 'c' is a Unicode code point for a trailing surrogate.""" return c >= 0xdc00 and c <= 0xdfff @staticmethod def __utf16_decode_surrogate_pair(leading, trailing): """Returns the unicode code point corresponding to leading surrogate 'leading' and trailing surrogate 'trailing'. The return value will not make any sense if 'leading' or 'trailing' are not in the correct ranges for leading or trailing surrogates.""" # Leading surrogate: 110110wwwwxxxxxx # Trailing surrogate: 110111xxxxxxxxxx # Code point: 000uuuuuxxxxxxxxxxxxxxxx w = (leading >> 6) & 0xf u = w + 1 x0 = leading & 0x3f x1 = trailing & 0x3ff return (u << 16) | (x0 << 10) | x1 __unescape = {'"': u'"', "\\": u"\\", "/": u"/", "b": u"\b", "f": u"\f", "n": u"\n", "r": u"\r", "t": u"\t"} def __lex_finish_string(self): inp = self.buffer out = u"" while len(inp): backslash = inp.find('\\') if backslash == -1: out += inp break out += inp[:backslash] inp = inp[backslash + 1:] if inp == "": self.__error("quoted string may not end with backslash") return replacement = Parser.__unescape.get(inp[0]) if replacement is not None: out += replacement inp = inp[1:] continue elif inp[0] != u'u': self.__error("bad escape \\%s" % inp[0]) return c0 = self.__lex_4hex(inp[1:5]) if c0 is None: return inp = inp[5:] if Parser.__is_leading_surrogate(c0): if inp[:2] != u'\\u': self.__error("malformed escaped surrogate pair") return c1 = self.__lex_4hex(inp[2:6]) if c1 is None: return if not Parser.__is_trailing_surrogate(c1): self.__error("second half of escaped surrogate pair is " "not trailing surrogate") return code_point = Parser.__utf16_decode_surrogate_pair(c0, c1) inp = inp[6:] else: code_point = c0 out += chr(code_point) self.__parser_input('string', out) def __lex_string_escape(self, c): self.buffer += c self.lex_state = Parser.__lex_string return True def __lex_string(self, c): if c == '\\': self.buffer += c self.lex_state = Parser.__lex_string_escape elif c == '"': self.__lex_finish_string() elif ord(c) >= 0x20: self.buffer += c else: self.__error("U+%04X must be escaped in quoted string" % ord(c)) return True def __lex_input(self, c): eat = self.lex_state(self, c) assert eat is True or eat is False return eat def __parse_start(self, token, unused_string): if token == '{': self.__push_object() elif token == '[': self.__push_array() else: self.__error("syntax error at beginning of input") def __parse_end(self, unused_token, unused_string): self.__error("trailing garbage at end of input") def __parse_object_init(self, token, string): if token == '}': self.__parser_pop() else: self.__parse_object_name(token, string) def __parse_object_name(self, token, string): if token == 'string': self.member_name = string self.parse_state = Parser.__parse_object_colon else: self.__error("syntax error parsing object expecting string") def __parse_object_colon(self, token, unused_string): if token == ":": self.parse_state = Parser.__parse_object_value else: self.__error("syntax error parsing object expecting ':'") def __parse_object_value(self, token, string): self.__parse_value(token, string, Parser.__parse_object_next) def __parse_object_next(self, token, unused_string): if token == ",": self.parse_state = Parser.__parse_object_name elif token == "}": self.__parser_pop() else: self.__error("syntax error expecting '}' or ','") def __parse_array_init(self, token, string): if token == ']': self.__parser_pop() else: self.__parse_array_value(token, string) def __parse_array_value(self, token, string): self.__parse_value(token, string, Parser.__parse_array_next) def __parse_array_next(self, token, unused_string): if token == ",": self.parse_state = Parser.__parse_array_value elif token == "]": self.__parser_pop() else: self.__error("syntax error expecting ']' or ','") def __parser_input(self, token, string=None): self.lex_state = Parser.__lex_start self.buffer = "" self.parse_state(self, token, string) def __put_value(self, value): top = self.stack[-1] if isinstance(top, dict): top[self.member_name] = value else: top.append(value) def __parser_push(self, new_json, next_state): if len(self.stack) < Parser.MAX_HEIGHT: if len(self.stack) > 0: self.__put_value(new_json) self.stack.append(new_json) self.parse_state = next_state else: self.__error("input exceeds maximum nesting depth %d" % Parser.MAX_HEIGHT) def __push_object(self): self.__parser_push({}, Parser.__parse_object_init) def __push_array(self): self.__parser_push([], Parser.__parse_array_init) def __parser_pop(self): if len(self.stack) == 1: self.parse_state = Parser.__parse_end if not self.check_trailer: self.done = True else: self.stack.pop() top = self.stack[-1] if isinstance(top, list): self.parse_state = Parser.__parse_array_next else: self.parse_state = Parser.__parse_object_next def __parse_value(self, token, string, next_state): number_types = [int] number_types.extend([float]) number_types = tuple(number_types) if token in [False, None, True] or isinstance(token, number_types): self.__put_value(token) elif token == 'string': self.__put_value(string) else: if token == '{': self.__push_object() elif token == '[': self.__push_array() else: self.__error("syntax error expecting value") return self.parse_state = next_state def __error(self, message): if self.error is None: self.error = ("line %d, column %d, byte %d: %s" % (self.line_number, self.column_number, self.byte_number, message)) self.done = True def feed(self, s): i = 0 while True: if self.done or i >= len(s): return i c = s[i] if self.__lex_input(c): self.byte_number += 1 if c == '\n': self.column_number = 0 self.line_number += 1 else: self.column_number += 1 i += 1 def is_done(self): return self.done def finish(self): if self.lex_state == Parser.__lex_start: pass elif self.lex_state in (Parser.__lex_string, Parser.__lex_string_escape): self.__error("unexpected end of input in quoted string") else: self.__lex_input(" ") if self.parse_state == Parser.__parse_start: self.__error("empty input stream") elif self.parse_state != Parser.__parse_end: self.__error("unexpected end of input") if self.error is None: assert len(self.stack) == 1 return self.stack.pop() else: return self.error openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/jsonrpc.py000066400000000000000000000511431514270232600231440ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. # # 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. import codecs import errno import os import random import sys import ovs.json import ovs.poller import ovs.reconnect import ovs.stream import ovs.timeval import ovs.util import ovs.vlog EOF = ovs.util.EOF vlog = ovs.vlog.Vlog("jsonrpc") class Message(object): T_REQUEST = 0 # Request. T_NOTIFY = 1 # Notification. T_REPLY = 2 # Successful reply. T_ERROR = 3 # Error reply. __types = {T_REQUEST: "request", T_NOTIFY: "notification", T_REPLY: "reply", T_ERROR: "error"} def __init__(self, type_, method, params, result, error, id): self.type = type_ self.method = method self.params = params self.result = result self.error = error self.id = id _next_id = 0 @staticmethod def _create_id(): this_id = Message._next_id Message._next_id += 1 return this_id @staticmethod def create_request(method, params): return Message(Message.T_REQUEST, method, params, None, None, Message._create_id()) @staticmethod def create_notify(method, params): return Message(Message.T_NOTIFY, method, params, None, None, None) @staticmethod def create_reply(result, id): return Message(Message.T_REPLY, None, None, result, None, id) @staticmethod def create_error(error, id): return Message(Message.T_ERROR, None, None, None, error, id) @staticmethod def type_to_string(type_): return Message.__types[type_] def __validate_arg(self, value, name, must_have): if (value is not None) == (must_have != 0): return None else: type_name = Message.type_to_string(self.type) if must_have: verb = "must" else: verb = "must not" return "%s %s have \"%s\"" % (type_name, verb, name) def is_valid(self): if self.params is not None and not isinstance(self.params, list): return "\"params\" must be JSON array" pattern = {Message.T_REQUEST: 0x11001, Message.T_NOTIFY: 0x11000, Message.T_REPLY: 0x00101, Message.T_ERROR: 0x00011}.get(self.type) if pattern is None: return "invalid JSON-RPC message type %s" % self.type return ( self.__validate_arg(self.method, "method", pattern & 0x10000) or self.__validate_arg(self.params, "params", pattern & 0x1000) or self.__validate_arg(self.result, "result", pattern & 0x100) or self.__validate_arg(self.error, "error", pattern & 0x10) or self.__validate_arg(self.id, "id", pattern & 0x1)) @staticmethod def from_json(json): if not isinstance(json, dict): return "message is not a JSON object" # Make a copy to avoid modifying the caller's dict. json = dict(json) if "method" in json: method = json.pop("method") if not isinstance(method, str): return "method is not a JSON string" else: method = None params = json.pop("params", None) result = json.pop("result", None) error = json.pop("error", None) id_ = json.pop("id", None) if len(json): return "message has unexpected member \"%s\"" % json.popitem()[0] if result is not None: msg_type = Message.T_REPLY elif error is not None: msg_type = Message.T_ERROR elif id_ is not None: msg_type = Message.T_REQUEST else: msg_type = Message.T_NOTIFY msg = Message(msg_type, method, params, result, error, id_) validation_error = msg.is_valid() if validation_error is not None: return validation_error else: return msg def to_json(self): json = {} if self.method is not None: json["method"] = self.method if self.params is not None: json["params"] = self.params if self.result is not None or self.type == Message.T_ERROR: json["result"] = self.result if self.error is not None or self.type == Message.T_REPLY: json["error"] = self.error if self.id is not None or self.type == Message.T_NOTIFY: json["id"] = self.id return json def __str__(self): s = [Message.type_to_string(self.type)] if self.method is not None: s.append("method=\"%s\"" % self.method) if self.params is not None: s.append("params=" + ovs.json.to_string(self.params)) if self.result is not None: s.append("result=" + ovs.json.to_string(self.result)) if self.error is not None: s.append("error=" + ovs.json.to_string(self.error)) if self.id is not None: s.append("id=" + ovs.json.to_string(self.id)) return ", ".join(s) class Connection(object): def __init__(self, stream): self.name = stream.name self.stream = stream self.status = 0 self.input = "" self.output = "" self.parser = None self.received_bytes = 0 def close(self): self.stream.close() self.stream = None def run(self): if self.status: return while len(self.output): retval = self.stream.send(self.output) if retval >= 0: self.output = self.output[retval:] else: if retval != -errno.EAGAIN: vlog.warn("%s: send error: %s" % (self.name, os.strerror(-retval))) self.error(-retval) break def wait(self, poller): if not self.status: self.stream.run_wait(poller) if len(self.output): self.stream.send_wait(poller) def get_status(self): return self.status def get_backlog(self): if self.status != 0: return 0 else: return len(self.output) def get_received_bytes(self): return self.received_bytes def __log_msg(self, title, msg): if vlog.dbg_is_enabled(): vlog.dbg("%s: %s %s" % (self.name, title, msg)) def send(self, msg): if self.status: return self.status self.__log_msg("send", msg) was_empty = len(self.output) == 0 self.output += ovs.json.to_string(msg.to_json()) if was_empty: self.run() return self.status def send_block(self, msg): error = self.send(msg) if error: return error while True: self.run() if not self.get_backlog() or self.get_status(): return self.status poller = ovs.poller.Poller() self.wait(poller) poller.block() def recv(self): if self.status: return self.status, None decoder = codecs.getincrementaldecoder('utf-8')() while True: if not self.input: error, data = self.stream.recv(4096) # Python 3 has separate types for strings and bytes. We # received bytes from a socket. We expect it to be string # data, so we convert it here as soon as possible. if data and not error: try: data = decoder.decode(data) except UnicodeError: error = errno.EILSEQ if error: if (sys.platform == "win32" and error == errno.WSAEWOULDBLOCK): # WSAEWOULDBLOCK would be the equivalent on Windows # for EAGAIN on Unix. error = errno.EAGAIN if error == errno.EAGAIN: return error, None else: # XXX rate-limit vlog.warn("%s: receive error: %s" % (self.name, os.strerror(error))) self.error(error) return self.status, None elif not data: self.error(EOF) return EOF, None else: self.input += data self.received_bytes += len(data) else: if self.parser is None: self.parser = ovs.json.Parser() if ovs.json.PARSER == ovs.json.PARSER_C: self.input = self.input.encode('utf-8')[ self.parser.feed(self.input):].decode() else: self.input = self.input[self.parser.feed(self.input):] if self.parser.is_done(): msg = self.__process_msg() if msg: return 0, msg else: return self.status, None def recv_block(self): while True: error, msg = self.recv() if error != errno.EAGAIN: return error, msg self.run() poller = ovs.poller.Poller() self.wait(poller) self.recv_wait(poller) poller.block() def transact_block(self, request): id_ = request.id error = self.send(request) reply = None while not error: error, reply = self.recv_block() if (reply and (reply.type == Message.T_REPLY or reply.type == Message.T_ERROR) and reply.id == id_): break return error, reply def __process_msg(self): json = self.parser.finish() self.parser = None if isinstance(json, str): # XXX rate-limit vlog.warn("%s: error parsing stream: %s" % (self.name, json)) self.error(errno.EPROTO) return msg = Message.from_json(json) if not isinstance(msg, Message): # XXX rate-limit vlog.warn("%s: received bad JSON-RPC message: %s" % (self.name, msg)) self.error(errno.EPROTO) return self.__log_msg("received", msg) return msg def recv_wait(self, poller): if self.status or self.input: poller.immediate_wake() else: self.stream.recv_wait(poller) def error(self, error): if self.status == 0: self.status = error self.stream.close() self.output = "" class Session(object): """A JSON-RPC session with reconnection.""" def __init__(self, reconnect, rpc, remotes): self.reconnect = reconnect self.rpc = rpc self.stream = None self.pstream = None self.seqno = 0 if type(remotes) is not list: remotes = [remotes] self.remotes = remotes random.shuffle(self.remotes) self.next_remote = 0 @staticmethod def open(name, probe_interval=None): """Creates and returns a Session that maintains a JSON-RPC session to 'name', which should be a string acceptable to ovs.stream.Stream or ovs.stream.PassiveStream's initializer. If 'name' is an active connection method, e.g. "tcp:127.1.2.3", the new session connects and reconnects, with back-off, to 'name'. If 'name' is a passive connection method, e.g. "ptcp:", the new session listens for connections to 'name'. It maintains at most one connection at any given time. Any new connection causes the previous one (if any) to be dropped. If "probe_interval" is zero it disables the connection keepalive feature. If non-zero the value will be forced to at least 1000 milliseconds. If None it will just use the default value in OVS. """ return Session.open_multiple([name], probe_interval=probe_interval) @staticmethod def open_multiple(remotes, probe_interval=None): reconnect = ovs.reconnect.Reconnect(ovs.timeval.msec()) session = Session(reconnect, None, remotes) session.pick_remote() reconnect.enable(ovs.timeval.msec()) reconnect.set_backoff_free_tries(len(remotes)) if ovs.stream.PassiveStream.is_valid_name(reconnect.get_name()): reconnect.set_passive(True, ovs.timeval.msec()) if not ovs.stream.stream_or_pstream_needs_probes(reconnect.get_name()): reconnect.set_probe_interval(0) elif probe_interval is not None: reconnect.set_probe_interval(probe_interval) return session @staticmethod def open_unreliably(jsonrpc): reconnect = ovs.reconnect.Reconnect(ovs.timeval.msec()) session = Session(reconnect, None, [jsonrpc.name]) reconnect.set_quiet(True) session.pick_remote() reconnect.set_max_tries(0) reconnect.connected(ovs.timeval.msec()) return session def pick_remote(self): self.reconnect.set_name(self.remotes[self.next_remote]) self.next_remote = (self.next_remote + 1) % len(self.remotes) def close(self): if self.rpc is not None: self.rpc.close() self.rpc = None if self.stream is not None: self.stream.close() self.stream = None if self.pstream is not None: self.pstream.close() self.pstream = None def __disconnect(self): if self.rpc is not None: self.rpc.error(EOF) self.rpc.close() self.rpc = None elif self.stream is not None: self.stream.close() self.stream = None else: return self.seqno += 1 self.pick_remote() def __connect(self): self.__disconnect() name = self.reconnect.get_name() if not self.reconnect.is_passive(): error, self.stream = ovs.stream.Stream.open(name) if not error: self.reconnect.connecting(ovs.timeval.msec()) else: self.reconnect.connect_failed(ovs.timeval.msec(), error) self.stream = None self.pick_remote() elif self.pstream is None: error, self.pstream = ovs.stream.PassiveStream.open(name) if not error: self.reconnect.listening(ovs.timeval.msec()) else: self.reconnect.connect_failed(ovs.timeval.msec(), error) self.pick_remote() self.seqno += 1 def run(self): if self.pstream is not None: error, stream = self.pstream.accept() if error == 0: if self.rpc or self.stream: # XXX rate-limit vlog.info("%s: new connection replacing active " "connection" % self.reconnect.get_name()) self.__disconnect() self.reconnect.connected(ovs.timeval.msec()) self.rpc = Connection(stream) elif error != errno.EAGAIN: self.reconnect.listen_error(ovs.timeval.msec(), error) self.pstream.close() self.pstream = None if self.rpc: backlog = self.rpc.get_backlog() self.rpc.run() if self.rpc.get_backlog() < backlog: # Data previously caught in a queue was successfully sent (or # there's an error, which we'll catch below). # # We don't count data that is successfully sent immediately as # activity, because there's a lot of queuing downstream from # us, which means that we can push a lot of data into a # connection that has stalled and won't ever recover. self.reconnect.activity(ovs.timeval.msec()) error = self.rpc.get_status() if error != 0: self.reconnect.disconnected(ovs.timeval.msec(), error) self.__disconnect() elif self.stream is not None: self.stream.run() error = self.stream.connect() if error == 0: self.reconnect.connected(ovs.timeval.msec()) self.rpc = Connection(self.stream) self.stream = None elif error != errno.EAGAIN: self.reconnect.connect_failed(ovs.timeval.msec(), error) self.pick_remote() self.stream.close() self.stream = None action = self.reconnect.run(ovs.timeval.msec()) if action == ovs.reconnect.CONNECT: self.__connect() elif action == ovs.reconnect.DISCONNECT: self.reconnect.disconnected(ovs.timeval.msec(), 0) self.__disconnect() elif action == ovs.reconnect.PROBE: if self.rpc: request = Message.create_request("echo", []) request.id = "echo" self.rpc.send(request) else: assert action is None def wait(self, poller): if self.rpc is not None: self.rpc.wait(poller) elif self.stream is not None: self.stream.run_wait(poller) self.stream.connect_wait(poller) if self.pstream is not None: self.pstream.wait(poller) self.reconnect.wait(poller, ovs.timeval.msec()) def get_backlog(self): if self.rpc is not None: return self.rpc.get_backlog() else: return 0 def get_name(self): return self.reconnect.get_name() def send(self, msg): if self.rpc is not None: return self.rpc.send(msg) else: return errno.ENOTCONN def recv(self): if self.rpc is not None: received_bytes = self.rpc.get_received_bytes() error, msg = self.rpc.recv() now = ovs.timeval.msec() self.reconnect.receive_attempted(now) if received_bytes != self.rpc.get_received_bytes(): # Data was successfully received. # # Previously we only counted receiving a full message as # activity, but with large messages or a slow connection that # policy could time out the session mid-message. self.reconnect.activity(now) if not error: if msg.type == Message.T_REQUEST and msg.method == "echo": # Echo request. Send reply. self.send(Message.create_reply(msg.params, msg.id)) elif msg.type == Message.T_REPLY and msg.id == "echo": # It's a reply to our echo request. Suppress it. pass else: return msg return None def recv_wait(self, poller): if self.rpc is not None: self.rpc.recv_wait(poller) def is_alive(self): if self.rpc is not None or self.stream is not None: return True else: max_tries = self.reconnect.get_max_tries() return max_tries is None or max_tries > 0 def is_connected(self): return self.rpc is not None def get_seqno(self): return self.seqno def force_reconnect(self): self.reconnect.force_reconnect(ovs.timeval.msec()) def reset_backoff(self): """ Resets the reconnect backoff by allowing as many free tries as the number of configured remotes. This is to be used by upper layers before calling force_reconnect() if backoff is undesirable.""" free_tries = len(self.remotes) if self.is_connected(): # The extra free try will be consumed when the current remote # is disconnected. free_tries += 1 self.reconnect.set_backoff_free_tries(free_tries) def get_num_of_remotes(self): return len(self.remotes) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/ovsuuid.py000066400000000000000000000035151514270232600231640ustar00rootroot00000000000000# Copyright (c) 2009, 2010, 2011, 2016 Nicira, Inc. # # 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. import re import uuid import ovs.db.parser from ovs.db import error uuidRE = re.compile("^xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx$" .replace('x', '[0-9a-fA-F]')) def zero(): return uuid.UUID(int=0) def is_valid_string(s): return uuidRE.match(s) is not None def from_string(s): if not is_valid_string(s): raise error.Error("%s is not a valid UUID" % s) return uuid.UUID(s) def from_json(json, symtab=None): try: s = ovs.db.parser.unwrap_json(json, "uuid", (str,), "string") if not uuidRE.match(s): raise error.Error("\"%s\" is not a valid UUID" % s, json) return uuid.UUID(s) except error.Error as e: if not symtab: raise e try: name = ovs.db.parser.unwrap_json(json, "named-uuid", (str,), "string") except error.Error: raise e if name not in symtab: symtab[name] = uuid.uuid4() return symtab[name] def to_json(uuid_): return ["uuid", str(uuid_)] def to_c_initializer(uuid_, var): hex_string = uuid_.hex parts = ["0x%s" % (hex_string[x * 8:(x + 1) * 8]) for x in range(4)] return "{ %s }," % ", ".join(parts) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/poller.py000066400000000000000000000240521514270232600227620ustar00rootroot00000000000000# Copyright (c) 2010, 2015 Nicira, Inc. # # 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. import errno import os import select import socket import sys import ovs.timeval import ovs.vlog if sys.platform == "win32": import ovs.winutils as winutils try: import ssl except ImportError: ssl = None try: from eventlet import patcher as eventlet_patcher def _using_eventlet_green_select(): return eventlet_patcher.is_monkey_patched(select) except: eventlet_patcher = None def _using_eventlet_green_select(): return False try: from gevent import monkey as gevent_monkey except: gevent_monkey = None vlog = ovs.vlog.Vlog("poller") POLLIN = 0x001 POLLOUT = 0x004 POLLERR = 0x008 POLLHUP = 0x010 POLLNVAL = 0x020 # eventlet/gevent doesn't support select.poll. If select.poll is used, # python interpreter is blocked as a whole instead of switching from the # current thread that is about to block to other runnable thread. # So emulate select.poll by select.select because using python means that # performance isn't so important. class _SelectSelect(object): """ select.poll emulation by using select.select. Only register and poll are needed at the moment. """ def __init__(self): self.rlist = [] self.wlist = [] self.xlist = [] def register(self, fd, events): if isinstance(fd, socket.socket): fd = fd.fileno() if ssl and isinstance(fd, ssl.SSLSocket): fd = fd.fileno() if sys.platform != 'win32': # Skip this on Windows, it also register events assert isinstance(fd, int) if events & POLLIN: self.rlist.append(fd) events &= ~POLLIN if events & POLLOUT: self.wlist.append(fd) events &= ~POLLOUT if events: self.xlist.append(fd) def poll(self, timeout): # XXX workaround a bug in eventlet # see https://github.com/eventlet/eventlet/pull/25 if timeout == 0 and _using_eventlet_green_select(): timeout = 0.1 if sys.platform == 'win32': events = self.rlist + self.wlist + self.xlist if not events: return [] if len(events) > winutils.win32event.MAXIMUM_WAIT_OBJECTS: raise WindowsError("Cannot handle more than maximum wait" "objects\n") # win32event.INFINITE timeout is -1 # timeout must be an int number, expressed in ms if timeout == 0.1: timeout = 100 else: timeout = int(timeout) # Wait until any of the events is set to signaled try: retval = winutils.win32event.WaitForMultipleObjects( events, False, # Wait all timeout) except winutils.pywintypes.error: return [(0, POLLERR)] if retval == winutils.winerror.WAIT_TIMEOUT: return [] if events[retval] in self.rlist: revent = POLLIN elif events[retval] in self.wlist: revent = POLLOUT else: revent = POLLERR return [(events[retval], revent)] else: if timeout == -1: # epoll uses -1 for infinite timeout, select uses None. timeout = None else: timeout = float(timeout) / 1000 rlist, wlist, xlist = select.select(self.rlist, self.wlist, self.xlist, timeout) events_dict = {} for fd in rlist: events_dict[fd] = events_dict.get(fd, 0) | POLLIN for fd in wlist: events_dict[fd] = events_dict.get(fd, 0) | POLLOUT for fd in xlist: events_dict[fd] = events_dict.get(fd, 0) | (POLLERR | POLLHUP | POLLNVAL) return list(events_dict.items()) SelectPoll = _SelectSelect # If eventlet/gevent isn't used, we can use select.poll by replacing # _SelectPoll with select.poll class # _SelectPoll = select.poll class Poller(object): """High-level wrapper around the "poll" system call. Intended usage is for the program's main loop to go about its business servicing whatever events it needs to. Then, when it runs out of immediate tasks, it calls each subordinate module or object's "wait" function, which in turn calls one (or more) of the functions Poller.fd_wait(), Poller.immediate_wake(), and Poller.timer_wait() to register to be awakened when the appropriate event occurs. Then the main loop calls Poller.block(), which blocks until one of the registered events happens.""" def __init__(self): self.__reset() def fd_wait(self, fd, events): """Registers 'fd' as waiting for the specified 'events' (which should be select.POLLIN or select.POLLOUT or their bitwise-OR). The following call to self.block() will wake up when 'fd' becomes ready for one or more of the requested events. The event registration is one-shot: only the following call to self.block() is affected. The event will need to be re-registered after self.block() is called if it is to persist. 'fd' may be an integer file descriptor or an object with a fileno() method that returns an integer file descriptor.""" self.poll.register(fd, events) def __timer_wait(self, msec): if self.timeout < 0 or msec < self.timeout: self.timeout = msec def timer_wait(self, msec): """Causes the following call to self.block() to block for no more than 'msec' milliseconds. If 'msec' is nonpositive, the following call to self.block() will not block at all. The timer registration is one-shot: only the following call to self.block() is affected. The timer will need to be re-registered after self.block() is called if it is to persist.""" if msec <= 0: self.immediate_wake() else: self.__timer_wait(msec) def timer_wait_until(self, msec): """Causes the following call to self.block() to wake up when the current time, as returned by ovs.timeval.msec(), reaches 'msec' or later. If 'msec' is earlier than the current time, the following call to self.block() will not block at all. The timer registration is one-shot: only the following call to self.block() is affected. The timer will need to be re-registered after self.block() is called if it is to persist.""" now = ovs.timeval.msec() if msec <= now: self.immediate_wake() else: self.__timer_wait(msec - now) def immediate_wake(self): """Causes the following call to self.block() to wake up immediately, without blocking.""" self.timeout = 0 def block(self): """Blocks until one or more of the events registered with self.fd_wait() occurs, or until the minimum duration registered with self.timer_wait() elapses, or not at all if self.immediate_wake() has been called.""" try: try: events = self.poll.poll(self.timeout) self.__log_wakeup(events) except OSError as e: """ On Windows, the select function from poll raises OSError exception if the polled array is empty.""" if e.errno != errno.EINTR: vlog.err("poll: %s" % os.strerror(e.errno)) except select.error as e: # XXX rate-limit error, msg = e if error != errno.EINTR: vlog.err("poll: %s" % e[1]) finally: self.__reset() def __log_wakeup(self, events): if not events: vlog.dbg("%d-ms timeout" % self.timeout) else: for fd, revents in events: if revents != 0: s = "" if revents & POLLIN: s += "[POLLIN]" if revents & POLLOUT: s += "[POLLOUT]" if revents & POLLERR: s += "[POLLERR]" if revents & POLLHUP: s += "[POLLHUP]" if revents & POLLNVAL: s += "[POLLNVAL]" vlog.dbg("%s on fd %d" % (s, fd)) def __reset(self): self.poll = SelectPoll() self.timeout = -1 def get_system_poll(): """Returns the original select.poll() object. If select.poll is monkey patched by eventlet or gevent library, it gets the original select.poll and returns an object of it using the eventlet.patcher.original/gevent.monkey.get_original functions. As a last resort, if there is any exception it returns the SelectPoll() object. """ try: if _using_eventlet_green_select(): _system_poll = eventlet_patcher.original("select").poll elif gevent_monkey and gevent_monkey.is_object_patched( 'select', 'poll'): _system_poll = gevent_monkey.get_original('select', 'poll') else: _system_poll = select.poll except: _system_poll = SelectPoll return _system_poll() openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/process.py000066400000000000000000000026731514270232600231500ustar00rootroot00000000000000# Copyright (c) 2010, 2011 Nicira, Inc. # # 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. import os import signal def _signal_status_msg(type_, signr): s = "%s by signal %d" % (type_, signr) for name in signal.__dict__: if name.startswith("SIG") and getattr(signal, name) == signr: return "%s (%s)" % (s, name) return s def status_msg(status): """Given 'status', which is a process status in the form reported by waitpid(2) and returned by process_status(), returns a string describing how the process terminated.""" if os.WIFEXITED(status): s = "exit status %d" % os.WEXITSTATUS(status) elif os.WIFSIGNALED(status): s = _signal_status_msg("killed", os.WTERMSIG(status)) elif os.WIFSTOPPED(status): s = _signal_status_msg("stopped", os.WSTOPSIG(status)) else: s = "terminated abnormally (%x)" % status if os.WCOREDUMP(status): s += ", core dumped" return s openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/reconnect.py000066400000000000000000000626331514270232600234540ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # 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. import os import ovs.util import ovs.vlog # Values returned by Reconnect.run() CONNECT = 'connect' DISCONNECT = 'disconnect' PROBE = 'probe' EOF = ovs.util.EOF vlog = ovs.vlog.Vlog("reconnect") class Reconnect(object): """A finite-state machine for connecting and reconnecting to a network resource with exponential backoff. It also provides optional support for detecting a connection on which the peer is no longer responding. The library does not implement anything networking related, only an FSM for networking code to use. Many Reconnect methods take a "now" argument. This makes testing easier since there is no hidden state. When not testing, just pass the return value of ovs.time.msec(). (Perhaps this design should be revisited later.)""" class Void(object): name = "VOID" is_connected = False @staticmethod def deadline(fsm, now): return None @staticmethod def run(fsm, now): return None class Listening(object): name = "LISTENING" is_connected = False @staticmethod def deadline(fsm, now): return None @staticmethod def run(fsm, now): return None class Backoff(object): name = "BACKOFF" is_connected = False @staticmethod def deadline(fsm, now): return fsm.state_entered + fsm.backoff @staticmethod def run(fsm, now): return CONNECT class ConnectInProgress(object): name = "CONNECTING" is_connected = False @staticmethod def deadline(fsm, now): return fsm.state_entered + max(1000, fsm.backoff) @staticmethod def run(fsm, now): return DISCONNECT class Active(object): name = "ACTIVE" is_connected = True @staticmethod def deadline(fsm, now): if fsm.probe_interval: base = max(fsm.last_activity, fsm.state_entered) expiration = base + fsm.probe_interval if (now < expiration or fsm.last_receive_attempt is None or fsm.last_receive_attempt >= expiration): # We still have time before the expiration or the time has # already passed and there was no activity. In the first # case we need to wait for the expiration, in the second - # we're already past the deadline. */ return expiration else: # Time has already passed, but we didn't attempt to receive # anything. We need to wake up and try to receive even if # nothing is pending, so we can update the expiration time # or transition to a different state. return now + 1 return None @staticmethod def run(fsm, now): vlog.dbg("%s: idle %d ms, sending inactivity probe" % (fsm.name, now - max(fsm.last_activity, fsm.state_entered))) fsm._transition(now, Reconnect.Idle) return PROBE class Idle(object): name = "IDLE" is_connected = True @staticmethod def deadline(fsm, now): if fsm.probe_interval: expiration = fsm.state_entered + fsm.probe_interval if (now < expiration or fsm.last_receive_attempt is None or fsm.last_receive_attempt >= expiration): return expiration else: return now + 1 return None @staticmethod def run(fsm, now): vlog.err("%s: no response to inactivity probe after %.3g " "seconds, disconnecting" % (fsm.name, (now - fsm.state_entered) / 1000.0)) return DISCONNECT class Reconnect(object): name = "RECONNECT" is_connected = False @staticmethod def deadline(fsm, now): return fsm.state_entered @staticmethod def run(fsm, now): return DISCONNECT def __init__(self, now): """Creates and returns a new reconnect FSM with default settings. The FSM is initially disabled. The caller will likely want to call self.enable() and self.set_name() on the returned object.""" self.name = "void" self.min_backoff = 1000 self.max_backoff = 8000 self.probe_interval = 5000 self.passive = False self.info_level = vlog.info self.state = Reconnect.Void self.state_entered = now self.backoff = 0 self.last_activity = now self.last_connected = None self.last_disconnected = None self.last_receive_attempt = now self.max_tries = None self.backoff_free_tries = 0 self.creation_time = now self.n_attempted_connections = 0 self.n_successful_connections = 0 self.total_connected_duration = 0 self.seqno = 0 def set_quiet(self, quiet): """If 'quiet' is true, this object will log informational messages at debug level, by default keeping them out of log files. This is appropriate if the connection is one that is expected to be short-lived, so that the log messages are merely distracting. If 'quiet' is false, this object logs informational messages at info level. This is the default. This setting has no effect on the log level of debugging, warning, or error messages.""" if quiet: self.info_level = vlog.dbg else: self.info_level = vlog.info def get_name(self): return self.name def set_name(self, name): """Sets this object's name to 'name'. If 'name' is None, then "void" is used instead. The name is used in log messages.""" if name is None: self.name = "void" else: self.name = name def get_min_backoff(self): """Return the minimum number of milliseconds to back off between consecutive connection attempts. The default is 1000 ms.""" return self.min_backoff def get_max_backoff(self): """Return the maximum number of milliseconds to back off between consecutive connection attempts. The default is 8000 ms.""" return self.max_backoff def get_probe_interval(self): """Returns the "probe interval" in milliseconds. If this is zero, it disables the connection keepalive feature. If it is nonzero, then if the interval passes while the FSM is connected and without self.activity() being called, self.run() returns ovs.reconnect.PROBE. If the interval passes again without self.activity() being called, self.run() returns ovs.reconnect.DISCONNECT.""" return self.probe_interval def set_max_tries(self, max_tries): """Limits the maximum number of times that this object will ask the client to try to reconnect to 'max_tries'. None (the default) means an unlimited number of tries. After the number of tries has expired, the FSM will disable itself instead of backing off and retrying.""" self.max_tries = max_tries def get_max_tries(self): """Returns the current remaining number of connection attempts, None if the number is unlimited.""" return self.max_tries def set_backoff(self, min_backoff, max_backoff): """Configures the backoff parameters for this FSM. 'min_backoff' is the minimum number of milliseconds, and 'max_backoff' is the maximum, between connection attempts. 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than or equal to 'min_backoff'.""" self.min_backoff = max(min_backoff, 1000) if self.max_backoff: self.max_backoff = max(max_backoff, 1000) else: self.max_backoff = 8000 if self.min_backoff > self.max_backoff: self.max_backoff = self.min_backoff if (self.state == Reconnect.Backoff and self.backoff > self.max_backoff): self.backoff = self.max_backoff def set_backoff_free_tries(self, backoff_free_tries): """Sets the number of connection attempts that will be made without backoff to 'backoff_free_tries'. Values 0 and 1 both represent a single attempt.""" self.backoff_free_tries = backoff_free_tries def set_probe_interval(self, probe_interval): """Sets the "probe interval" to 'probe_interval', in milliseconds. If this is zero, it disables the connection keepalive feature. If it is nonzero, then if the interval passes while this FSM is connected and without self.activity() being called, self.run() returns ovs.reconnect.PROBE. If the interval passes again without self.activity() being called, self.run() returns ovs.reconnect.DISCONNECT. If 'probe_interval' is nonzero, then it will be forced to a value of at least 1000 ms.""" if probe_interval: self.probe_interval = max(1000, probe_interval) else: self.probe_interval = 0 def is_passive(self): """Returns true if 'fsm' is in passive mode, false if 'fsm' is in active mode (the default).""" return self.passive def set_passive(self, passive, now): """Configures this FSM for active or passive mode. In active mode (the default), the FSM is attempting to connect to a remote host. In passive mode, the FSM is listening for connections from a remote host.""" if self.passive != passive: self.passive = passive if ((passive and self.state in (Reconnect.ConnectInProgress, Reconnect.Reconnect)) or (not passive and self.state == Reconnect.Listening and self.__may_retry())): self._transition(now, Reconnect.Backoff) self.backoff = 0 def is_enabled(self): """Returns true if this FSM has been enabled with self.enable(). Calling another function that indicates a change in connection state, such as self.disconnected() or self.force_reconnect(), will also enable a reconnect FSM.""" return self.state != Reconnect.Void def enable(self, now): """If this FSM is disabled (the default for newly created FSMs), enables it, so that the next call to reconnect_run() for 'fsm' will return ovs.reconnect.CONNECT. If this FSM is not disabled, this function has no effect.""" if self.state == Reconnect.Void and self.__may_retry(): self._transition(now, Reconnect.Backoff) self.backoff = 0 def disable(self, now): """Disables this FSM. Until 'fsm' is enabled again, self.run() will always return 0.""" if self.state != Reconnect.Void: self._transition(now, Reconnect.Void) def force_reconnect(self, now): """If this FSM is enabled and currently connected (or attempting to connect), forces self.run() to return ovs.reconnect.DISCONNECT the next time it is called, which should cause the client to drop the connection (or attempt), back off, and then reconnect.""" if self.state in (Reconnect.ConnectInProgress, Reconnect.Active, Reconnect.Idle): self._transition(now, Reconnect.Reconnect) def disconnected(self, now, error): """Tell this FSM that the connection dropped or that a connection attempt failed. 'error' specifies the reason: a positive value represents an errno value, EOF indicates that the connection was closed by the peer (e.g. read() returned 0), and 0 indicates no specific error. The FSM will back off, then reconnect.""" if self.state not in (Reconnect.Backoff, Reconnect.Void): # Report what happened if self.state in (Reconnect.Active, Reconnect.Idle): if error > 0: vlog.warn("%s: connection dropped (%s)" % (self.name, os.strerror(error))) elif error == EOF: self.info_level("%s: connection closed by peer" % self.name) else: self.info_level("%s: connection dropped" % self.name) elif self.state == Reconnect.Listening: if error > 0: vlog.warn("%s: error listening for connections (%s)" % (self.name, os.strerror(error))) else: self.info_level("%s: error listening for connections" % self.name) elif self.state == Reconnect.Reconnect: self.info_level("%s: connection closed by client" % self.name) elif self.backoff < self.max_backoff: if self.passive: type_ = "listen" else: type_ = "connection" if error > 0: vlog.warn("%s: %s attempt failed (%s)" % (self.name, type_, os.strerror(error))) else: self.info_level("%s: %s attempt timed out" % (self.name, type_)) if (self.state in (Reconnect.Active, Reconnect.Idle)): self.last_disconnected = now if not self.__may_retry(): self._transition(now, Reconnect.Void) return # Back off if self.backoff_free_tries > 1: self.backoff_free_tries -= 1 self.backoff = 0 elif (self.state in (Reconnect.Active, Reconnect.Idle) and (self.last_activity - self.last_connected >= self.backoff or self.passive)): if self.passive: self.backoff = 0 else: self.backoff = self.min_backoff else: if self.backoff < self.min_backoff: self.backoff = self.min_backoff elif self.backoff < self.max_backoff / 2: self.backoff *= 2 if self.passive: action = "trying to listen again" else: action = "reconnect" self.info_level("%s: waiting %.3g seconds before %s" % (self.name, self.backoff / 1000.0, action)) else: if self.backoff < self.max_backoff: if self.passive: action = "try to listen" else: action = "reconnect" self.info_level("%s: continuing to %s in the " "background but suppressing further " "logging" % (self.name, action)) self.backoff = self.max_backoff self._transition(now, Reconnect.Backoff) def connecting(self, now): """Tell this FSM that a connection or listening attempt is in progress. The FSM will start a timer, after which the connection or listening attempt will be aborted (by returning ovs.reconnect.DISCONNECT from self.run()).""" if self.state != Reconnect.ConnectInProgress: if self.passive: self.info_level("%s: listening..." % self.name) elif self.backoff < self.max_backoff: self.info_level("%s: connecting..." % self.name) self._transition(now, Reconnect.ConnectInProgress) def listening(self, now): """Tell this FSM that the client is listening for connection attempts. This state last indefinitely until the client reports some change. The natural progression from this state is for the client to report that a connection has been accepted or is in progress of being accepted, by calling self.connecting() or self.connected(). The client may also report that listening failed (e.g. accept() returned an unexpected error such as ENOMEM) by calling self.listen_error(), in which case the FSM will back off and eventually return ovs.reconnect.CONNECT from self.run() to tell the client to try listening again.""" if self.state != Reconnect.Listening: self.info_level("%s: listening..." % self.name) self._transition(now, Reconnect.Listening) def listen_error(self, now, error): """Tell this FSM that the client's attempt to accept a connection failed (e.g. accept() returned an unexpected error such as ENOMEM). If the FSM is currently listening (self.listening() was called), it will back off and eventually return ovs.reconnect.CONNECT from self.run() to tell the client to try listening again. If there is an active connection, this will be delayed until that connection drops.""" if self.state == Reconnect.Listening: self.disconnected(now, error) def connected(self, now): """Tell this FSM that the connection was successful. The FSM will start the probe interval timer, which is reset by self.activity(). If the timer expires, a probe will be sent (by returning ovs.reconnect.PROBE from self.run(). If the timer expires again without being reset, the connection will be aborted (by returning ovs.reconnect.DISCONNECT from self.run().""" if not self.state.is_connected: self.connecting(now) self.info_level("%s: connected" % self.name) self._transition(now, Reconnect.Active) self.last_connected = now def connect_failed(self, now, error): """Tell this FSM that the connection attempt failed. The FSM will back off and attempt to reconnect.""" self.connecting(now) self.disconnected(now, error) def activity(self, now): """Tell this FSM that some activity occurred on the connection. This resets the probe interval timer, so that the connection is known not to be idle.""" if self.state != Reconnect.Active: self._transition(now, Reconnect.Active) self.last_activity = now def receive_attempted(self, now): """Tell 'fsm' that some attempt to receive data on the connection was made at 'now'. The FSM only allows probe interval timer to expire when some attempt to receive data on the connection was received after the time when it should have expired. This helps in the case where there's a long delay in the poll loop and then reconnect_run() executes before the code to try to receive anything from the remote runs. (To disable this feature, pass None for 'now'.)""" self.last_receive_attempt = now def _transition(self, now, state): if self.state == Reconnect.ConnectInProgress: self.n_attempted_connections += 1 if state == Reconnect.Active: self.n_successful_connections += 1 connected_before = self.state.is_connected connected_now = state.is_connected if connected_before != connected_now: if connected_before: self.total_connected_duration += now - self.last_connected self.seqno += 1 vlog.dbg("%s: entering %s" % (self.name, state.name)) self.state = state self.state_entered = now def run(self, now): """Assesses whether any action should be taken on this FSM. The return value is one of: - None: The client need not take any action. - Active client, ovs.reconnect.CONNECT: The client should start a connection attempt and indicate this by calling self.connecting(). If the connection attempt has definitely succeeded, it should call self.connected(). If the connection attempt has definitely failed, it should call self.connect_failed(). The FSM is smart enough to back off correctly after successful connections that quickly abort, so it is OK to call self.connected() after a low-level successful connection (e.g. connect()) even if the connection might soon abort due to a failure at a high-level (e.g. SSL/TLS negotiation failure). - Passive client, ovs.reconnect.CONNECT: The client should try to listen for a connection, if it is not already listening. It should call self.listening() if successful, otherwise self.connecting() or reconnected_connect_failed() if the attempt is in progress or definitely failed, respectively. A listening passive client should constantly attempt to accept a new connection and report an accepted connection with self.connected(). - ovs.reconnect.DISCONNECT: The client should abort the current connection or connection attempt or listen attempt and call self.disconnected() or self.connect_failed() to indicate it. - ovs.reconnect.PROBE: The client should send some kind of request to the peer that will elicit a response, to ensure that the connection is indeed in working order. (This will only be returned if the "probe interval" is nonzero--see self.set_probe_interval()).""" deadline = self.state.deadline(self, now) if deadline is not None and now >= deadline: return self.state.run(self, now) else: return None def wait(self, poller, now): """Causes the next call to poller.block() to wake up when self.run() should be called.""" timeout = self.timeout(now) if timeout is not None and timeout >= 0: poller.timer_wait(timeout) def timeout(self, now): """Returns the number of milliseconds after which self.run() should be called if nothing else notable happens in the meantime, or None if this is currently unnecessary.""" deadline = self.state.deadline(self, now) if deadline is not None: remaining = deadline - now return max(0, remaining) else: return None def is_connected(self): """Returns True if this FSM is currently believed to be connected, that is, if self.connected() was called more recently than any call to self.connect_failed() or self.disconnected() or self.disable(), and False otherwise.""" return self.state.is_connected def get_last_connect_elapsed(self, now): """Returns the number of milliseconds since 'fsm' was last connected to its peer. Returns None if never connected.""" if self.last_connected: return now - self.last_connected else: return None def get_last_disconnect_elapsed(self, now): """Returns the number of milliseconds since 'fsm' was last disconnected from its peer. Returns None if never disconnected.""" if self.last_disconnected: return now - self.last_disconnected else: return None def get_stats(self, now): class Stats(object): pass stats = Stats() stats.creation_time = self.creation_time stats.last_connected = self.last_connected stats.last_disconnected = self.last_disconnected stats.last_activity = self.last_activity stats.backoff = self.backoff stats.seqno = self.seqno stats.is_connected = self.is_connected() stats.msec_since_connect = self.get_last_connect_elapsed(now) stats.msec_since_disconnect = self.get_last_disconnect_elapsed(now) stats.total_connected_duration = self.total_connected_duration if self.is_connected(): stats.total_connected_duration += ( self.get_last_connect_elapsed(now)) stats.n_attempted_connections = self.n_attempted_connections stats.n_successful_connections = self.n_successful_connections stats.state = self.state.name stats.state_elapsed = now - self.state_entered return stats def __may_retry(self): if self.max_tries is None: return True elif self.max_tries > 0: self.max_tries -= 1 return True else: return False openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/socket_util.py000066400000000000000000000302771514270232600240200ustar00rootroot00000000000000# Copyright (c) 2010, 2012, 2014, 2015 Nicira, Inc. # # 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. import errno import ipaddress import os import os.path import random import socket import sys from ovs import dns_resolve import ovs.fatal_signal import ovs.poller import ovs.vlog try: import ssl except ImportError: ssl = None if sys.platform == 'win32': import ovs.winutils as winutils import win32file vlog = ovs.vlog.Vlog("socket_util") def make_short_name(long_name): if long_name is None: return None long_name = os.path.abspath(long_name) long_dirname = os.path.dirname(long_name) tmpdir = os.getenv('TMPDIR', '/tmp') for x in range(0, 1000): link_name = \ '%s/ovs-un-py-%d-%d' % (tmpdir, random.randint(0, 10000), x) try: os.symlink(long_dirname, link_name) ovs.fatal_signal.add_file_to_unlink(link_name) return os.path.join(link_name, os.path.basename(long_name)) except OSError as e: if e.errno != errno.EEXIST: break raise Exception("Failed to create temporary symlink") def free_short_name(short_name): if short_name is None: return link_name = os.path.dirname(short_name) ovs.fatal_signal.unlink_file_now(link_name) def make_unix_socket(style, nonblock, bind_path, connect_path, short=False): """Creates a Unix domain socket in the given 'style' (either socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if 'bind_path' is not None) and connected to 'connect_path' (if 'connect_path' is not None). If 'nonblock' is true, the socket is made non-blocking. Returns (error, socket): on success 'error' is 0 and 'socket' is a new socket object, on failure 'error' is a positive errno value and 'socket' is None.""" try: sock = socket.socket(socket.AF_UNIX, style) except socket.error as e: return get_exception_errno(e), None try: if nonblock: set_nonblocking(sock) if bind_path is not None: # Delete bind_path but ignore ENOENT. try: os.unlink(bind_path) except OSError as e: if e.errno != errno.ENOENT: return e.errno, None ovs.fatal_signal.add_file_to_unlink(bind_path) sock.bind(bind_path) try: os.fchmod(sock.fileno(), 0o700) except OSError: pass if connect_path is not None: try: sock.connect(connect_path) except socket.error as e: if get_exception_errno(e) != errno.EINPROGRESS: raise return 0, sock except socket.error as e: sock.close() if (bind_path is not None and os.path.exists(bind_path)): ovs.fatal_signal.unlink_file_now(bind_path) eno = ovs.socket_util.get_exception_errno(e) if (eno == "AF_UNIX path too long" and os.uname()[0] == "Linux"): short_connect_path = None short_bind_path = None connect_dirfd = None bind_dirfd = None # Try workaround using /proc/self/fd if connect_path is not None: dirname = os.path.dirname(connect_path) basename = os.path.basename(connect_path) try: connect_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) except OSError as err: return get_exception_errno(err), None short_connect_path = "/proc/self/fd/%d/%s" % (connect_dirfd, basename) if bind_path is not None: dirname = os.path.dirname(bind_path) basename = os.path.basename(bind_path) try: bind_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) except OSError as err: return get_exception_errno(err), None short_bind_path = "/proc/self/fd/%d/%s" % (bind_dirfd, basename) try: return make_unix_socket(style, nonblock, short_bind_path, short_connect_path) finally: if connect_dirfd is not None: os.close(connect_dirfd) if bind_dirfd is not None: os.close(bind_dirfd) elif (eno == "AF_UNIX path too long"): if short: return get_exception_errno(e), None short_bind_path = None try: short_bind_path = make_short_name(bind_path) short_connect_path = make_short_name(connect_path) except: free_short_name(short_bind_path) return errno.ENAMETOOLONG, None try: return make_unix_socket(style, nonblock, short_bind_path, short_connect_path, short=True) finally: free_short_name(short_bind_path) free_short_name(short_connect_path) else: return get_exception_errno(e), None def check_connection_completion(sock): if sys.platform == "win32": p = ovs.poller.SelectPoll() event = winutils.get_new_event(None, False, True, None) # Receive notification of readiness for writing, of completed # connection or multipoint join operation, and of socket closure. win32file.WSAEventSelect(sock, event, win32file.FD_WRITE | win32file.FD_CONNECT | win32file.FD_CLOSE) p.register(event, ovs.poller.POLLOUT) else: p = ovs.poller.get_system_poll() p.register(sock, ovs.poller.POLLOUT) pfds = p.poll(0) if len(pfds) == 1: revents = pfds[0][1] if revents & ovs.poller.POLLERR or revents & ovs.poller.POLLHUP: try: # The following should raise an exception. if ssl and isinstance(sock, ssl.SSLSocket): # SSL wrapped socket does not allow # non-zero optional flag. sock.send("\0".encode()) else: sock.send("\0".encode(), socket.MSG_DONTWAIT) # (Here's where we end up if it didn't.) # XXX rate-limit vlog.err("poll return POLLERR but send succeeded") return errno.EPROTO except socket.error as e: return get_exception_errno(e) else: return 0 else: return errno.EAGAIN def is_valid_ipv4_address(address): try: socket.inet_pton(socket.AF_INET, address) except AttributeError: try: socket.inet_aton(address) except socket.error: return False except socket.error: return False return True def _inet_parse_active(target, default_port): address = target.split(":") if len(address) >= 2: host_name = ":".join(address[0:-1]).lstrip('[').rstrip(']') port = int(address[-1]) else: if default_port: port = default_port else: raise ValueError("%s: port number must be specified" % target) host_name = address[0] if not host_name: raise ValueError("%s: bad peer name format" % target) try: host_name = str(ipaddress.ip_address(host_name)) except ValueError: host_name = dns_resolve.resolve(host_name) if not host_name: raise ValueError("%s: bad peer name format" % target) return (host_name, port) def inet_parse_active(target, default_port, raises=True): try: return _inet_parse_active(target, default_port) except ValueError: if raises: raise return ("", default_port) def inet_create_socket_active(style, address): try: is_addr_inet = is_valid_ipv4_address(address[0]) if is_addr_inet: sock = socket.socket(socket.AF_INET, style, 0) family = socket.AF_INET else: sock = socket.socket(socket.AF_INET6, style, 0) family = socket.AF_INET6 except socket.error as e: return get_exception_errno(e), None return family, sock def inet_connect_active(sock, address, family, dscp): try: set_nonblocking(sock) set_dscp(sock, family, dscp) error = sock.connect_ex(address) if error not in (0, errno.EINPROGRESS, errno.EWOULDBLOCK): sock.close() return error return 0 except socket.error as e: sock.close() return get_exception_errno(e) def inet_open_active(style, target, default_port, dscp): address = inet_parse_active(target, default_port, raises=False) family, sock = inet_create_socket_active(style, address) if sock is None: return family, sock error = inet_connect_active(sock, address, family, dscp) if error: return error, None return 0, sock def get_exception_errno(e): """A lot of methods on Python socket objects raise socket.error, but that exception is documented as having two completely different forms of arguments: either a string or a (errno, string) tuple. We only want the errno.""" if isinstance(e.args, tuple): return e.args[0] else: return errno.EPROTO null_fd = -1 def get_null_fd(): """Returns a readable and writable fd for /dev/null, if successful, otherwise a negative errno value. The caller must not close the returned fd (because the same fd will be handed out to subsequent callers).""" global null_fd if null_fd < 0: try: # os.devnull ensures compatibility with Windows, returns # '/dev/null' for Unix and 'nul' for Windows null_fd = os.open(os.devnull, os.O_RDWR) except OSError as e: vlog.err("could not open %s: %s" % (os.devnull, os.strerror(e.errno))) return -e.errno return null_fd def write_fully(fd, buf): """Returns an (error, bytes_written) tuple where 'error' is 0 on success, otherwise a positive errno value, and 'bytes_written' is the number of bytes that were written before the error occurred. 'error' is 0 if and only if 'bytes_written' is len(buf).""" bytes_written = 0 if len(buf) == 0: return 0, 0 if not isinstance(buf, bytes): buf = bytes(buf, 'utf-8') while True: try: retval = os.write(fd, buf) assert retval >= 0 if retval == len(buf): return 0, bytes_written + len(buf) elif retval == 0: vlog.warn("write returned 0") return errno.EPROTO, bytes_written else: bytes_written += retval buf = buf[:retval] except OSError as e: return e.errno, bytes_written def set_nonblocking(sock): try: sock.setblocking(0) except socket.error as e: vlog.err("could not set nonblocking mode on socket: %s" % os.strerror(get_exception_errno(e))) def set_dscp(sock, family, dscp): if dscp > 63: raise ValueError("Invalid dscp %d" % dscp) val = dscp << 2 if family == socket.AF_INET: sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val) elif family == socket.AF_INET6: sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val) else: raise ValueError('Invalid family %d' % family) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/stream.py000066400000000000000000000770771514270232600227770ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # 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. import errno import os import socket import sys import ovs.poller import ovs.socket_util import ovs.vlog try: import ssl except ImportError: ssl = None if sys.platform == 'win32': import ovs.winutils as winutils import pywintypes import win32event import win32file import win32pipe vlog = ovs.vlog.Vlog("stream") def stream_or_pstream_needs_probes(name): """ True if the stream or pstream specified by 'name' needs periodic probes to verify connectivity. For [p]streams which need probes, it can take a long time to notice the connection was dropped. Returns False if probes aren't needed, and None if 'name' is invalid""" cls = Stream._find_method(name) if cls: return cls.needs_probes() elif PassiveStream.is_valid_name(name): return PassiveStream.needs_probes(name) else: return None class Stream(object): """Bidirectional byte stream. Unix domain sockets, tcp and ssl are implemented.""" # States. __S_CONNECTING = 0 __S_CONNECTED = 1 __S_DISCONNECTED = 2 # Kinds of events that one might wait for. W_CONNECT = 0 # Connect complete (success or failure). W_RECV = 1 # Data received. W_SEND = 2 # Send buffer room available. _SOCKET_METHODS = {} _SSL_private_key_file = None _SSL_certificate_file = None _SSL_ca_cert_file = None # Windows only _write = None # overlapped for write operation _read = None # overlapped for read operation _write_pending = False _read_pending = False _retry_connect = False @staticmethod def register_method(method, cls): Stream._SOCKET_METHODS[method + ":"] = cls @staticmethod def _find_method(name): for method, cls in Stream._SOCKET_METHODS.items(): if name.startswith(method): return cls return None @staticmethod def is_valid_name(name): """Returns True if 'name' is a stream name in the form "TYPE:ARGS" and TYPE is a supported stream type ("unix:", "tcp:" and "ssl:"), otherwise False.""" return bool(Stream._find_method(name)) def __init__(self, socket, name, status, pipe=None, is_server=False): self.socket = socket self.pipe = pipe if sys.platform == 'win32': if pipe is not None: # Flag to check if fd is a server HANDLE. In the case of a # server handle we have to issue a disconnect before closing # the actual handle. self._server = is_server suffix = name.split(":", 1)[1] suffix = ovs.util.abs_file_name(ovs.dirs.RUNDIR, suffix) self._pipename = winutils.get_pipe_name(suffix) self._read = pywintypes.OVERLAPPED() self._read.hEvent = winutils.get_new_event() self._write = pywintypes.OVERLAPPED() self._write.hEvent = winutils.get_new_event() else: self._wevent = winutils.get_new_event(bManualReset=False, bInitialState=False) self.name = name if status == errno.EAGAIN: self.state = Stream.__S_CONNECTING elif status == 0: self.state = Stream.__S_CONNECTED else: self.state = Stream.__S_DISCONNECTED self.error = 0 # Default value of dscp bits for connection between controller and manager. # Value of IPTOS_PREC_INTERNETCONTROL = 0xc0 which is defined # in is used. IPTOS_PREC_INTERNETCONTROL = 0xc0 DSCP_DEFAULT = IPTOS_PREC_INTERNETCONTROL >> 2 @staticmethod def check_connection_completion(sock): return ovs.socket_util.check_connection_completion(sock) @staticmethod def open(name, dscp=DSCP_DEFAULT): """Attempts to connect a stream to a remote peer. 'name' is a connection name in the form "TYPE:ARGS", where TYPE is an active stream class's name and ARGS are stream class-specific. The supported TYPEs include "unix", "tcp", and "ssl". Returns (error, stream): on success 'error' is 0 and 'stream' is the new Stream, on failure 'error' is a positive errno value and 'stream' is None. Never returns errno.EAGAIN or errno.EINPROGRESS. Instead, returns 0 and a new Stream. The connect() method can be used to check for successful connection completion.""" cls = Stream._find_method(name) if not cls: return errno.EAFNOSUPPORT, None suffix = name.split(":", 1)[1] if name.startswith("unix:"): suffix = ovs.util.abs_file_name(ovs.dirs.RUNDIR, suffix) if sys.platform == 'win32': pipename = winutils.get_pipe_name(suffix) if len(suffix) > 255: # Return invalid argument if the name is too long return errno.ENOENT, None try: # In case of "unix:" argument, the assumption is that # there is a file created in the path (suffix). open(suffix, 'r').close() except: return errno.ENOENT, None try: npipe = winutils.create_file(pipename) try: winutils.set_pipe_mode(npipe, win32pipe.PIPE_READMODE_BYTE) except pywintypes.error: return errno.ENOENT, None except pywintypes.error as e: if e.winerror == winutils.winerror.ERROR_PIPE_BUSY: # Pipe is busy, set the retry flag to true and retry # again during the connect function. Stream.retry_connect = True return 0, cls(None, name, errno.EAGAIN, pipe=win32file.INVALID_HANDLE_VALUE, is_server=False) return errno.ENOENT, None return 0, cls(None, name, 0, pipe=npipe, is_server=False) error, sock = cls._open(suffix, dscp) if error: return error, None else: err = cls.check_connection_completion(sock) if err == errno.EAGAIN or err == errno.EINPROGRESS: status = errno.EAGAIN err = 0 elif err == 0: status = 0 else: status = err return err, cls(sock, name, status) @staticmethod def _open(suffix, dscp): raise NotImplementedError("This method must be overrided by subclass") @staticmethod def open_block(error_stream, timeout=None): """Blocks until a Stream completes its connection attempt, either succeeding or failing, but no more than 'timeout' milliseconds. (error, stream) should be the tuple returned by Stream.open(). Negative value of 'timeout' means infinite waiting. Returns a tuple of the same form. Typical usage: error, stream = Stream.open_block(Stream.open("unix:/tmp/socket"))""" # Py3 doesn't support tuple parameter unpacking - PEP 3113 error, stream = error_stream if not error: deadline = None if timeout is not None and timeout >= 0: deadline = ovs.timeval.msec() + timeout while True: error = stream.connect() if sys.platform == 'win32' and error == errno.WSAEWOULDBLOCK: # WSAEWOULDBLOCK would be the equivalent on Windows # for EAGAIN on Unix. error = errno.EAGAIN if error != errno.EAGAIN: break if deadline is not None and ovs.timeval.msec() > deadline: error = errno.ETIMEDOUT break stream.run() poller = ovs.poller.Poller() stream.run_wait(poller) stream.connect_wait(poller) if deadline is not None: poller.timer_wait_until(deadline) poller.block() if stream.socket is not None: assert error != errno.EINPROGRESS if error and stream: stream.close() stream = None return error, stream def close(self): if self.socket is not None: self.socket.close() if self.pipe is not None: if self._server: # Flush the pipe to allow the client to read the pipe # before disconnecting. win32pipe.FlushFileBuffers(self.pipe) win32pipe.DisconnectNamedPipe(self.pipe) winutils.close_handle(self.pipe, vlog.warn) winutils.close_handle(self._read.hEvent, vlog.warn) winutils.close_handle(self._write.hEvent, vlog.warn) def __scs_connecting(self): if self.socket is not None: retval = self.check_connection_completion(self.socket) assert retval != errno.EINPROGRESS elif sys.platform == 'win32': if self.retry_connect: try: self.pipe = winutils.create_file(self._pipename) self._retry_connect = False retval = 0 except pywintypes.error as e: if e.winerror == winutils.winerror.ERROR_PIPE_BUSY: retval = errno.EAGAIN else: self._retry_connect = False retval = errno.ENOENT else: # If retry_connect is false, it means it's already # connected so we can set the value of retval to 0 retval = 0 if retval == 0: self.state = Stream.__S_CONNECTED elif retval != errno.EAGAIN: self.state = Stream.__S_DISCONNECTED self.error = retval def connect(self): """Tries to complete the connection on this stream. If the connection is complete, returns 0 if the connection was successful or a positive errno value if it failed. If the connection is still in progress, returns errno.EAGAIN.""" if self.state == Stream.__S_CONNECTING: self.__scs_connecting() if self.state == Stream.__S_CONNECTING: return errno.EAGAIN elif self.state == Stream.__S_CONNECTED: return 0 else: assert self.state == Stream.__S_DISCONNECTED return self.error def recv(self, n): """Tries to receive up to 'n' bytes from this stream. Returns a (error, string) tuple: - If successful, 'error' is zero and 'string' contains between 1 and 'n' bytes of data. - On error, 'error' is a positive errno value. - If the connection has been closed in the normal fashion or if 'n' is 0, the tuple is (0, ""). The recv function will not block waiting for data to arrive. If no data have been received, it returns (errno.EAGAIN, "") immediately.""" try: return self._recv(n) except socket.error as e: return (ovs.socket_util.get_exception_errno(e), "") def _recv(self, n): retval = self.connect() if retval != 0: return (retval, "") elif n == 0: return (0, "") if sys.platform == 'win32' and self.socket is None: return self.__recv_windows(n) return (0, self.socket.recv(n)) def __recv_windows(self, n): if self._read_pending: try: nBytesRead = winutils.get_overlapped_result(self.pipe, self._read, False) self._read_pending = False except pywintypes.error as e: if e.winerror == winutils.winerror.ERROR_IO_INCOMPLETE: # The operation is still pending, try again self._read_pending = True return (errno.EAGAIN, "") elif e.winerror in winutils.pipe_disconnected_errors: # If the pipe was disconnected, return 0. return (0, "") else: return (errno.EINVAL, "") else: (errCode, self._read_buffer) = winutils.read_file(self.pipe, n, self._read) if errCode: if errCode == winutils.winerror.ERROR_IO_PENDING: self._read_pending = True return (errno.EAGAIN, "") elif errCode in winutils.pipe_disconnected_errors: # If the pipe was disconnected, return 0. return (0, "") else: return (errCode, "") try: nBytesRead = winutils.get_overlapped_result(self.pipe, self._read, False) winutils.win32event.SetEvent(self._read.hEvent) except pywintypes.error as e: if e.winerror in winutils.pipe_disconnected_errors: # If the pipe was disconnected, return 0. return (0, "") else: return (e.winerror, "") recvBuffer = self._read_buffer[:nBytesRead] # recvBuffer will have the type memoryview in Python3. # We can use bytes to convert it to type bytes which works on # both Python2 and Python3. return (0, bytes(recvBuffer)) def send(self, buf): """Tries to send 'buf' on this stream. If successful, returns the number of bytes sent, between 1 and len(buf). 0 is only a valid return value if len(buf) is 0. On error, returns a negative errno value. Will not block. If no bytes can be immediately accepted for transmission, returns -errno.EAGAIN immediately.""" try: return self._send(buf) except socket.error as e: return -ovs.socket_util.get_exception_errno(e) def _send(self, buf): retval = self.connect() if retval != 0: return -retval elif len(buf) == 0: return 0 # We must have bytes for sending. if isinstance(buf, str): buf = buf.encode('utf-8') if sys.platform == 'win32' and self.socket is None: return self.__send_windows(buf) return self.socket.send(buf) def __send_windows(self, buf): if self._write_pending: try: nBytesWritten = winutils.get_overlapped_result(self.pipe, self._write, False) self._write_pending = False except pywintypes.error as e: if e.winerror == winutils.winerror.ERROR_IO_INCOMPLETE: # The operation is still pending, try again self._read_pending = True return -errno.EAGAIN elif e.winerror in winutils.pipe_disconnected_errors: # If the pipe was disconnected, return connection reset. return -errno.ECONNRESET else: return -errno.EINVAL else: (errCode, nBytesWritten) = winutils.write_file(self.pipe, buf, self._write) if errCode: if errCode == winutils.winerror.ERROR_IO_PENDING: self._write_pending = True return -errno.EAGAIN if (not nBytesWritten and errCode in winutils.pipe_disconnected_errors): # If the pipe was disconnected, return connection reset. return -errno.ECONNRESET return nBytesWritten def run(self): pass def run_wait(self, poller): pass def wait(self, poller, wait): assert wait in (Stream.W_CONNECT, Stream.W_RECV, Stream.W_SEND) if self.state == Stream.__S_DISCONNECTED: poller.immediate_wake() return if self.state == Stream.__S_CONNECTING: wait = Stream.W_CONNECT if sys.platform == 'win32': self.__wait_windows(poller, wait) return if wait == Stream.W_RECV: poller.fd_wait(self.socket, ovs.poller.POLLIN) else: poller.fd_wait(self.socket, ovs.poller.POLLOUT) def __wait_windows(self, poller, wait): if self.socket is not None: if wait == Stream.W_RECV: mask = (win32file.FD_READ | win32file.FD_ACCEPT | win32file.FD_CLOSE) event = ovs.poller.POLLIN else: mask = (win32file.FD_WRITE | win32file.FD_CONNECT | win32file.FD_CLOSE) event = ovs.poller.POLLOUT try: win32file.WSAEventSelect(self.socket, self._wevent, mask) except pywintypes.error as e: vlog.err("failed to associate events with socket: %s" % e.strerror) poller.fd_wait(self._wevent, event) else: if wait == Stream.W_RECV: if self._read: poller.fd_wait(self._read.hEvent, ovs.poller.POLLIN) elif wait == Stream.W_SEND: if self._write: poller.fd_wait(self._write.hEvent, ovs.poller.POLLOUT) elif wait == Stream.W_CONNECT: return def connect_wait(self, poller): self.wait(poller, Stream.W_CONNECT) def recv_wait(self, poller): self.wait(poller, Stream.W_RECV) def send_wait(self, poller): self.wait(poller, Stream.W_SEND) def __del__(self): # Don't delete the file: we might have forked. if self.socket is not None: self.socket.close() if self.pipe is not None: # Check if there are any remaining valid handles and close them if self.pipe: winutils.close_handle(self.pipe) if self._read.hEvent: winutils.close_handle(self._read.hEvent) if self._write.hEvent: winutils.close_handle(self._write.hEvent) @staticmethod def ssl_set_private_key_file(file_name): Stream._SSL_private_key_file = file_name @staticmethod def ssl_set_certificate_file(file_name): Stream._SSL_certificate_file = file_name @staticmethod def ssl_set_ca_cert_file(file_name): Stream._SSL_ca_cert_file = file_name class PassiveStream(object): # Windows only connect = None # overlapped for read operation connect_pending = False @staticmethod def needs_probes(name): return False if name.startswith("punix:") else True @staticmethod def is_valid_name(name): """Returns True if 'name' is a passive stream name in the form "TYPE:ARGS" and TYPE is a supported passive stream type (currently "punix:" or "ptcp"), otherwise False.""" return name.startswith("punix:") | name.startswith("ptcp:") def __init__(self, sock, name, bind_path, pipe=None): self.name = name self.pipe = pipe self.socket = sock if pipe is not None: self.connect = pywintypes.OVERLAPPED() self.connect.hEvent = winutils.get_new_event() self.connect_pending = False suffix = name.split(":", 1)[1] suffix = ovs.util.abs_file_name(ovs.dirs.RUNDIR, suffix) self._pipename = winutils.get_pipe_name(suffix) self.bind_path = bind_path @staticmethod def open(name): """Attempts to start listening for remote stream connections. 'name' is a connection name in the form "TYPE:ARGS", where TYPE is an passive stream class's name and ARGS are stream class-specific. Currently the supported values for TYPE are "punix" and "ptcp". Returns (error, pstream): on success 'error' is 0 and 'pstream' is the new PassiveStream, on failure 'error' is a positive errno value and 'pstream' is None.""" if not PassiveStream.is_valid_name(name): return errno.EAFNOSUPPORT, None bind_path = None if name.startswith("punix:"): bind_path = ovs.util.abs_file_name(ovs.dirs.RUNDIR, name[6:]) if sys.platform != 'win32': error, sock = ovs.socket_util.make_unix_socket( socket.SOCK_STREAM, True, bind_path, None) if error: return error, None else: # Branch used only on Windows try: open(bind_path, 'w').close() except: return errno.ENOENT, None pipename = winutils.get_pipe_name(bind_path) if len(pipename) > 255: # Return invalid argument if the name is too long return errno.ENOENT, None npipe = winutils.create_named_pipe(pipename) if not npipe: return errno.ENOENT, None return 0, PassiveStream(None, name, bind_path, pipe=npipe) elif name.startswith("ptcp:"): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) remote = name.split(':') sock.bind((remote[1], int(remote[2]))) else: raise Exception('Unknown connection string') try: sock.listen(64) except socket.error as e: vlog.err("%s: listen: %s" % (name, os.strerror(e.error))) sock.close() return e.error, None return 0, PassiveStream(sock, name, bind_path) def close(self): """Closes this PassiveStream.""" if self.socket is not None: self.socket.close() if self.pipe is not None: winutils.close_handle(self.pipe, vlog.warn) winutils.close_handle(self.connect.hEvent, vlog.warn) if self.bind_path is not None: ovs.fatal_signal.unlink_file_now(self.bind_path) self.bind_path = None def accept(self): """Tries to accept a new connection on this passive stream. Returns (error, stream): if successful, 'error' is 0 and 'stream' is the new Stream object, and on failure 'error' is a positive errno value and 'stream' is None. Will not block waiting for a connection. If no connection is ready to be accepted, returns (errno.EAGAIN, None) immediately.""" if sys.platform == 'win32' and self.socket is None: return self.__accept_windows() while True: try: sock, addr = self.socket.accept() ovs.socket_util.set_nonblocking(sock) if (sys.platform != 'win32' and sock.family == socket.AF_UNIX): return 0, Stream(sock, "unix:%s" % addr, 0) return 0, Stream(sock, 'ptcp:%s:%s' % (addr[0], str(addr[1])), 0) except socket.error as e: error = ovs.socket_util.get_exception_errno(e) if sys.platform == 'win32' and error == errno.WSAEWOULDBLOCK: # WSAEWOULDBLOCK would be the equivalent on Windows # for EAGAIN on Unix. error = errno.EAGAIN if error != errno.EAGAIN: # XXX rate-limit vlog.dbg("accept: %s" % os.strerror(error)) return error, None def __accept_windows(self): if self.connect_pending: try: winutils.get_overlapped_result(self.pipe, self.connect, False) except pywintypes.error as e: if e.winerror == winutils.winerror.ERROR_IO_INCOMPLETE: # The operation is still pending, try again self.connect_pending = True return errno.EAGAIN, None else: if self.pipe: win32pipe.DisconnectNamedPipe(self.pipe) return errno.EINVAL, None self.connect_pending = False error = winutils.connect_named_pipe(self.pipe, self.connect) if error: if error == winutils.winerror.ERROR_IO_PENDING: self.connect_pending = True return errno.EAGAIN, None elif error != winutils.winerror.ERROR_PIPE_CONNECTED: if self.pipe: win32pipe.DisconnectNamedPipe(self.pipe) self.connect_pending = False return errno.EINVAL, None else: win32event.SetEvent(self.connect.hEvent) npipe = winutils.create_named_pipe(self._pipename) if not npipe: return errno.ENOENT, None old_pipe = self.pipe self.pipe = npipe winutils.win32event.ResetEvent(self.connect.hEvent) return 0, Stream(None, self.name, 0, pipe=old_pipe) def wait(self, poller): if sys.platform != 'win32' or self.socket is not None: poller.fd_wait(self.socket, ovs.poller.POLLIN) else: poller.fd_wait(self.connect.hEvent, ovs.poller.POLLIN) def __del__(self): # Don't delete the file: we might have forked. if self.socket is not None: self.socket.close() if self.pipe is not None: # Check if there are any remaining valid handles and close them if self.pipe: winutils.close_handle(self.pipe) if self._connect.hEvent: winutils.close_handle(self._read.hEvent) def usage(name): return """ Active %s connection methods: unix:FILE Unix domain socket named FILE tcp:HOST:PORT TCP socket to HOST with port no of PORT ssl:HOST:PORT SSL/TLS socket to HOST with port no of PORT Passive %s connection methods: punix:FILE Listen on Unix domain socket FILE""" % (name, name) class UnixStream(Stream): @staticmethod def needs_probes(): return False @staticmethod def _open(suffix, dscp): connect_path = suffix return ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, True, None, connect_path) Stream.register_method("unix", UnixStream) class TCPStream(Stream): @staticmethod def needs_probes(): return True @staticmethod def _open(suffix, dscp): error, sock = ovs.socket_util.inet_open_active(socket.SOCK_STREAM, suffix, 0, dscp) if not error: try: sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except socket.error as e: sock.close() return ovs.socket_util.get_exception_errno(e), None return error, sock Stream.register_method("tcp", TCPStream) class SSLStream(Stream): @staticmethod def check_connection_completion(sock): try: return Stream.check_connection_completion(sock) except ssl.SSLSyscallError as e: return ovs.socket_util.get_exception_errno(e) @staticmethod def needs_probes(): return True @staticmethod def _open(suffix, dscp): address = ovs.socket_util.inet_parse_active(suffix, 0, raises=False) family, sock = ovs.socket_util.inet_create_socket_active( socket.SOCK_STREAM, address) if sock is None: return family, sock # Create an SSL context. ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.verify_mode = ssl.CERT_REQUIRED ctx.check_hostname = False # Only allow TLSv1.2 or later. ctx.minimum_version = ssl.TLSVersion.TLSv1_2 ctx.maximum_version = ssl.TLSVersion.MAXIMUM_SUPPORTED # If the client has not set the SSL/TLS configuration files # exception would be raised. ctx.load_verify_locations(Stream._SSL_ca_cert_file) ctx.load_cert_chain(Stream._SSL_certificate_file, Stream._SSL_private_key_file) ssl_sock = ctx.wrap_socket(sock, do_handshake_on_connect=False) # Connect error = ovs.socket_util.inet_connect_active(ssl_sock, address, family, dscp) if not error: try: ssl_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except socket.error as e: ssl_sock.close() return ovs.socket_util.get_exception_errno(e), None return error, ssl_sock def connect(self): retval = super(SSLStream, self).connect() if retval: return retval # TCP Connection is successful. Now do the SSL/TLS handshake. try: self.socket.do_handshake() except ssl.SSLWantReadError: return errno.EAGAIN except (ssl.SSLSyscallError, ssl.SSLZeroReturnError, ssl.SSLEOFError, OSError) as e: return ovs.socket_util.get_exception_errno(e) return 0 def recv(self, n): try: return super(SSLStream, self)._recv(n) except ssl.SSLWantReadError: return (errno.EAGAIN, "") except ssl.SSLSyscallError as e: return (ovs.socket_util.get_exception_errno(e), "") except ssl.SSLZeroReturnError: return (0, "") except socket.error as e: return (ovs.socket_util.get_exception_errno(e), "") def send(self, buf): try: return super(SSLStream, self)._send(buf) except ssl.SSLWantWriteError: return -errno.EAGAIN except ssl.SSLSyscallError as e: return -ovs.socket_util.get_exception_errno(e) except socket.error as e: return -ovs.socket_util.get_exception_errno(e) def close(self): if self.socket: try: self.socket.shutdown(socket.SHUT_RDWR) except socket.error: pass return super(SSLStream, self).close() if ssl: # Register SSL/TLS only if the OpenSSL module is available. Stream.register_method("ssl", SSLStream) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/000077500000000000000000000000001514270232600222525ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_decoders.py000066400000000000000000000074571514270232600254700ustar00rootroot00000000000000from netaddr import IPAddress import pytest from ovs.flow.decoders import decode_ip_port_range @pytest.mark.parametrize( "input_string,expected", [ ( "192.168.0.0-192.168.0.200:1000-2000", { "addrs": { "start": IPAddress("192.168.0.0"), "end": IPAddress("192.168.0.200"), }, "ports": { "start": 1000, "end": 2000, }, }, ), ( "192.168.0.0-192.168.0.200", { "addrs": { "start": IPAddress("192.168.0.0"), "end": IPAddress("192.168.0.200"), }, }, ), ( "192.168.0.0-192.168.0.200:2000", { "addrs": { "start": IPAddress("192.168.0.0"), "end": IPAddress("192.168.0.200"), }, "ports": { "start": 2000, "end": 2000, }, }, ), ( "192.168.0.1:1000-2000", { "addrs": { "start": IPAddress("192.168.0.1"), "end": IPAddress("192.168.0.1"), }, "ports": { "start": 1000, "end": 2000, }, }, ), ( "[fe80:0000:0000:0000:0204:61ff:fe9d:f150]-[fe80:0000:0000:0000:0204:61ff:fe9d:f15f]:255", # noqa: E501 { "addrs": { "start": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f150" ), "end": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f15f" ), }, "ports": { "start": 255, "end": 255, }, }, ), ( "[fe80::204:61ff:254.157.241.86]-[fe80::204:61ff:254.157.241.100]:255-300", # noqa: E501 { "addrs": { "start": IPAddress("fe80::204:61ff:254.157.241.86"), "end": IPAddress("fe80::204:61ff:254.157.241.100"), }, "ports": { "start": 255, "end": 300, }, }, ), ( "[fe80::f150]-[fe80::f15f]:255-300", { "addrs": { "start": IPAddress("fe80::f150"), "end": IPAddress("fe80::f15f"), }, "ports": { "start": 255, "end": 300, }, }, ), ( "fe80:0000:0000:0000:0204:61ff:fe9d:f150-fe80:0000:0000:0000:0204:61ff:fe9d:f15f", # noqa: E501 { "addrs": { "start": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f150" ), "end": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f15f" ), }, }, ), ( "fe80:0000:0000:0000:0204:61ff:fe9d:f156", { "addrs": { "start": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f156" ), "end": IPAddress( "fe80:0000:0000:0000:0204:61ff:fe9d:f156" ), }, }, ), ], ) def test_decode_ip_port_range(input_string, expected): assert expected == decode_ip_port_range(input_string) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_dns_resolve.py000066400000000000000000000221101514270232600262020ustar00rootroot00000000000000import contextlib import ipaddress import sys import time from unittest import mock import pytest from ovs import dns_resolve from ovs import socket_util skip_no_unbound = pytest.mark.skipif("unbound" not in dns_resolve.__dict__, reason="Unbound not installed") HOSTS = [("192.0.2.1", "fake.ip4.domain", "192.0.2.1"), ("2001:db8:2::1", "fake.ip6.domain", "2001:db8:2::1"), ("192.0.2.2", "fake.both.domain", "192.0.2.2"), ("2001:db8:2::2", "fake.both.domain", "192.0.2.2")] def _tmp_file(path, content): path.write_text(content) assert content == path.read_text() return path @pytest.fixture(params=[False, True], ids=["not_daemon", "daemon"]) def resolver_factory(monkeypatch, tmp_path, hosts_file, request): # Allow delaying the instantiation of the DNSResolver def resolver_factory(): with monkeypatch.context() as m: m.setenv("OVS_HOSTS_FILE", str(hosts_file)) # Test with both is_daemon False and True resolver = dns_resolve.init(request.param) assert resolver._is_daemon == request.param return resolver return resolver_factory @contextlib.contextmanager def DNSResolver(*args, **kwargs): """Clean up after returning a dns_resolver.DNSResolver""" resolver = dns_resolve.init(*args, **kwargs) try: yield resolver finally: dns_resolve.destroy() assert dns_resolve._global_resolver is None @pytest.fixture def unbound_conf(tmp_path): path = tmp_path / "unbound.conf" content = """ server: verbosity: 1 """ return _tmp_file(path, content) @pytest.fixture def resolv_conf(tmp_path): path = tmp_path / "resolv.conf" content = "nameserver 127.0.0.1" return _tmp_file(path, content) @pytest.fixture def hosts_file(tmp_path): path = tmp_path / "hosts" content = "\n".join(f"{ip}\t{host}" for ip, host, _ in HOSTS) return _tmp_file(path, content) @pytest.fixture def missing_file(tmp_path): f = tmp_path / "missing_file" assert not f.exists() return f @pytest.fixture(params=[False, True], ids=["with unbound", "without unbound"]) def missing_unbound(monkeypatch, request): if request.param: if "unbound" in dns_resolve.__dict__: monkeypatch.setitem(sys.modules, 'unbound', None) monkeypatch.delitem(dns_resolve.__dict__, "unbound") elif "unbound" not in dns_resolve.__dict__: pytest.skip("Unbound not installed") return request.param def test_missing_unbound(missing_unbound, resolver_factory): resolver = resolver_factory() # Dont fail even w/o unbound assert resolver.dns_enabled == (not missing_unbound) def test_DNSRequest_defaults(): req = dns_resolve.DNSRequest(HOSTS[0][1]) assert HOSTS[0][1] == req.name assert req.state == dns_resolve.ReqState.INVALID assert req.time == req.result == req.ttl is None assert str(req) def _resolve(resolver, host, fn=dns_resolve.resolve): """Handle sync/async lookups, giving up if more than 1 second has passed""" timeout = 1 start = time.time() name = fn(host) if resolver and resolver._is_daemon: while name is None: name = fn(host) if name: break time.sleep(0.01) end = time.time() if end - start > timeout: break if name: return name raise LookupError(f"{host} not found") @pytest.mark.parametrize("ip,host,expected", HOSTS) def test_resolve_addresses(missing_unbound, resolver_factory, ip, host, expected): resolver = resolver_factory() if missing_unbound: with pytest.raises(LookupError): _resolve(resolver, host) else: result = _resolve(resolver, host) assert ipaddress.ip_address(expected) == ipaddress.ip_address(result) @pytest.mark.parametrize("ip,host,expected", HOSTS) def test_resolve_without_init(monkeypatch, missing_unbound, ip, host, expected, hosts_file): # make sure we don't have a global resolver dns_resolve.destroy() with monkeypatch.context() as m: m.setenv("OVS_HOSTS_FILE", str(hosts_file)) if missing_unbound: with pytest.raises(LookupError): _resolve(None, host) else: res = _resolve(None, host) assert dns_resolve._global_resolver is not None assert dns_resolve._global_resolver._is_daemon is False assert ipaddress.ip_address(expected) == ipaddress.ip_address(res) def test_resolve_unknown_host(missing_unbound, resolver_factory): resolver = resolver_factory() with pytest.raises(LookupError): _resolve(resolver, "fake.notadomain") @skip_no_unbound def test_resolve_process_error(): with DNSResolver(True) as resolver: with mock.patch.object(resolver._ctx, "process", return_value=-1): assert resolver.resolve("fake.domain") is None @skip_no_unbound def test_resolve_resolve_error(): with DNSResolver(False) as resolver: with mock.patch.object(resolver._ctx, "resolve", return_value=(-1, None)): assert resolver.resolve("fake.domain") is None @skip_no_unbound def test_resolve_resolve_async_error(): with DNSResolver(True) as resolver: with mock.patch.object(resolver._ctx, "resolve_async", return_value=(-1, None)): with pytest.raises(LookupError): _resolve(resolver, "fake.domain") @pytest.mark.parametrize("file,raises", [(None, False), ("missing_file", dns_resolve.UnboundException), ("unbound_conf", False)]) def test_set_unbound_conf(monkeypatch, missing_unbound, resolver_factory, request, file, raises): if file: file = str(request.getfixturevalue(file)) monkeypatch.setenv("OVS_UNBOUND_CONF", file) resolver = resolver_factory() # Doesn't raise if missing_unbound: assert resolver._set_unbound_conf() is None return with mock.patch.object(resolver._ctx, "config", side_effect=resolver._ctx.config) as c: if raises: with pytest.raises(raises): resolver._set_unbound_conf() else: resolver._set_unbound_conf() if file: c.assert_called_once_with(file) else: c.assert_not_called() @pytest.mark.parametrize("file,raises", [(None, False), ("missing_file", dns_resolve.UnboundException), ("resolv_conf", False)]) def test_resolv_conf(monkeypatch, missing_unbound, resolver_factory, request, file, raises): if file: file = str(request.getfixturevalue(file)) monkeypatch.setenv("OVS_RESOLV_CONF", file) resolver = resolver_factory() # Doesn't raise if missing_unbound: assert resolver._set_resolv_conf() is None return with mock.patch.object(resolver._ctx, "resolvconf", side_effect=resolver._ctx.resolvconf) as c: if raises: with pytest.raises(raises): resolver._set_resolv_conf() else: resolver._set_resolv_conf() c.assert_called_once_with(file) @pytest.mark.parametrize("file,raises", [(None, False), ("missing_file", dns_resolve.UnboundException), ("hosts_file", False)]) def test_hosts(monkeypatch, missing_unbound, resolver_factory, request, file, raises): if file: file = str(request.getfixturevalue(file)) monkeypatch.setenv("OVS_HOSTS_FILE", file) resolver = resolver_factory() # Doesn't raise if missing_unbound: assert resolver._set_hosts_file() is None return with mock.patch.object(resolver._ctx, "hosts", side_effect=resolver._ctx.hosts) as c: if raises: with pytest.raises(raises): resolver._set_hosts_file() else: resolver._set_hosts_file() c.assert_called_once_with(file) def test_UnboundException(missing_unbound): with pytest.raises(dns_resolve.UnboundException): raise dns_resolve.UnboundException("Fake exception", -1) @skip_no_unbound @pytest.mark.parametrize("ip,host,expected", HOSTS) def test_inet_parse_active(resolver_factory, ip, host, expected): resolver = resolver_factory() def fn(name): # Return the same thing _resolve() would so we can call # this multiple times for the is_daemon=True case return socket_util.inet_parse_active(f"{name}:6640", 6640, raises=False)[0] or None # parsing IPs still works IP = _resolve(resolver, ip, fn) assert ipaddress.ip_address(ip) == ipaddress.ip_address(IP) # parsing hosts works IP = _resolve(resolver, host, fn) assert ipaddress.ip_address(IP) == ipaddress.ip_address(expected) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_filter.py000066400000000000000000000146201514270232600251530ustar00rootroot00000000000000import pytest from ovs.flow.filter import OFFilter from ovs.flow.ofp import OFPFlow from ovs.flow.odp import ODPFlow @pytest.mark.parametrize( "expr,flow,expected,match", [ ( "nw_src=192.168.1.1 && tcp_dst=80", OFPFlow( "nw_src=192.168.1.1,tcp_dst=80 actions=drop" ), True, ["nw_src", "tcp_dst"], ), ( "nw_src=192.168.1.2 || tcp_dst=80", OFPFlow( "nw_src=192.168.1.1,tcp_dst=80 actions=drop" ), True, ["nw_src", "tcp_dst"], ), ( "nw_src=192.168.1.1 || tcp_dst=90", OFPFlow( "nw_src=192.168.1.1,tcp_dst=80 actions=drop" ), True, ["nw_src", "tcp_dst"], ), ( "nw_src=192.168.1.2 && tcp_dst=90", OFPFlow( "nw_src=192.168.1.1,tcp_dst=80 actions=drop" ), False, ["nw_src", "tcp_dst"], ), ( "nw_src=192.168.1.1", OFPFlow( "nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" ), False, ["nw_src"], ), ( "nw_src~=192.168.1.1", OFPFlow( "nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" ), True, ["nw_src"], ), ( "nw_src~=192.168.1.1/30", OFPFlow( "nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" ), True, ["nw_src"], ), ( "nw_src~=192.168.1.0/16", OFPFlow( "nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" ), False, ["nw_src"], ), ( "nw_src~=192.168.1.0/16", OFPFlow( "nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" ), False, ["nw_src"], ), ( "n_bytes=100", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), True, ["n_bytes"], ), ( "n_bytes>10", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), True, ["n_bytes"], ), ( "n_bytes>100", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), False, ["n_bytes"], ), ( "n_bytes<100", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), False, ["n_bytes"], ), ( "n_bytes<1000", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), True, ["n_bytes"], ), ( "n_bytes>0 && drop=true", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=drop" # noqa: E501 ), True, ["n_bytes", "drop"], ), ( "n_bytes>0 && drop=true", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=2" # noqa: E501 ), False, ["n_bytes"], ), ( "n_bytes>10 && !output.port=3", OFPFlow( "n_bytes=100 priority=100,nw_src=192.168.1.0/24,tcp_dst=80 actions=2" # noqa: E501 ), True, ["n_bytes", "output"], ), ( "dl_src=00:11:22:33:44:55", OFPFlow( "n_bytes=100 priority=100,dl_src=00:11:22:33:44:55,nw_src=192.168.1.0/24,tcp_dst=80 actions=2" # noqa: E501 ), True, ["dl_src"], ), ( "dl_src~=00:11:22:33:44:55", OFPFlow( "n_bytes=100 priority=100,dl_src=00:11:22:33:44:55/ff:ff:ff:ff:ff:00,nw_src=192.168.1.0/24,tcp_dst=80 actions=2" # noqa: E501 ), True, ["dl_src"], ), ( "dl_src~=00:11:22:33:44:66", OFPFlow( "n_bytes=100 priority=100,dl_src=00:11:22:33:44:55/ff:ff:ff:ff:ff:00,nw_src=192.168.1.0/24,tcp_dst=80 actions=2" # noqa: E501 ), True, ["dl_src"], ), ( "dl_src~=00:11:22:33:44:66 && tp_dst=1000", OFPFlow( "n_bytes=100 priority=100,dl_src=00:11:22:33:44:55/ff:ff:ff:ff:ff:00,nw_src=192.168.1.0/24,tp_dst=0x03e8/0xfff8 actions=2" # noqa: E501 ), False, ["dl_src", "tp_dst"], ), ( "dl_src~=00:11:22:33:44:66 && tp_dst~=1000", OFPFlow( "n_bytes=100 priority=100,dl_src=00:11:22:33:44:55/ff:ff:ff:ff:ff:00,nw_src=192.168.1.0/24,tp_dst=0x03e8/0xfff8 actions=2" # noqa: E501 ), True, ["dl_src", "tp_dst"], ), ( "encap", ODPFlow( "encap(eth_type(0x0800),ipv4(src=10.76.23.240/255.255.255.248,dst=10.76.23.106,proto=17,tos=0/0,ttl=64,frag=no)) actions:drop" # noqa: E501 ), True, ["encap"], ), ( "encap.ipv4.src=10.76.23.240", ODPFlow( "encap(eth_type(0x0800),ipv4(src=10.76.23.240/255.255.255.248,dst=10.76.23.106,proto=17,tos=0/0,ttl=64,frag=no)) actions:drop" # noqa: E501 ), False, ["encap"], ), ( "encap.ipv4.src~=10.76.23.240", ODPFlow( "encap(eth_type(0x0800),ipv4(src=10.76.23.240/255.255.255.248,dst=10.76.23.106,proto=17,tos=0/0,ttl=64,frag=no)) actions:drop" # noqa: E501 ), True, ["encap"], ), ], ) def test_filter(expr, flow, expected, match): ffilter = OFFilter(expr) result = ffilter.evaluate(flow) if expected: assert result else: assert not result assert [kv.key for kv in result.kv] == match openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_kv.py000066400000000000000000000052651514270232600243130ustar00rootroot00000000000000import pytest from ovs.flow.kv import KVParser, KVDecoders, KeyValue from ovs.flow.decoders import decode_default decoders = KVDecoders(default=lambda k, v: (k, decode_default(v))) @pytest.mark.parametrize( "input_data,expected", [ ( ( "cookie=0x0, duration=147566.365s, table=0, n_packets=39, n_bytes=2574, idle_age=65534, hard_age=65534", # noqa: E501 decoders, ), [ KeyValue("cookie", 0), KeyValue("duration", "147566.365s"), KeyValue("table", 0), KeyValue("n_packets", 39), KeyValue("n_bytes", 2574), KeyValue("idle_age", 65534), KeyValue("hard_age", 65534), ], ), ( ( "load:0x4->NXM_NX_REG13[],load:0x9->NXM_NX_REG11[],load:0x8->NXM_NX_REG12[],load:0x1->OXM_OF_METADATA[],load:0x1->NXM_NX_REG14[],mod_dl_src:0a:58:a9:fe:00:02,resubmit(,8)", # noqa: E501 decoders, ), [ KeyValue("load", "0x4->NXM_NX_REG13[]"), KeyValue("load", "0x9->NXM_NX_REG11[]"), KeyValue("load", "0x8->NXM_NX_REG12[]"), KeyValue("load", "0x1->OXM_OF_METADATA[]"), KeyValue("load", "0x1->NXM_NX_REG14[]"), KeyValue("mod_dl_src", "0a:58:a9:fe:00:02"), KeyValue("resubmit", ",8"), ], ), (("l1(l2(l3(l4())))", decoders), [KeyValue("l1", "l2(l3(l4()))")]), ( ("l1(l2(l3(l4()))),foo:bar", decoders), [KeyValue("l1", "l2(l3(l4()))"), KeyValue("foo", "bar")], ), ( ("enqueue:1:2,output=2", decoders), [KeyValue("enqueue", "1:2"), KeyValue("output", 2)], ), ( ("value_to_reg(100)->someReg[10],foo:bar", decoders), [ KeyValue("value_to_reg", "(100)->someReg[10]"), KeyValue("foo", "bar"), ], ), ], ) def test_kv_parser(input_data, expected): input_string = input_data[0] decoders = input_data[1] tparser = KVParser(input_string, decoders) tparser.parse() result = tparser.kv() assert len(expected) == len(result) for i in range(0, len(result)): assert result[i].key == expected[i].key assert result[i].value == expected[i].value kpos = result[i].meta.kpos kstr = result[i].meta.kstring vpos = result[i].meta.vpos vstr = result[i].meta.vstring assert input_string[kpos : kpos + len(kstr)] == kstr if vpos != -1: assert input_string[vpos : vpos + len(vstr)] == vstr openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_list.py000066400000000000000000000037161514270232600246450ustar00rootroot00000000000000import pytest from ovs.flow.list import ListParser, ListDecoders from ovs.flow.kv import KeyValue @pytest.mark.parametrize( "input_data,expected", [ ( ("field1,field2,3,nested:value", None, [","]), [ KeyValue("elem_0", "field1"), KeyValue("elem_1", "field2"), KeyValue("elem_2", 3), KeyValue("elem_3", "nested:value"), ], ), ( ( "field1,field2,3,nested:value", ListDecoders( [ ("key1", str), ("key2", str), ("key3", int), ("key4", lambda x: x.split(":"), [","]), ] ), [","], ), [ KeyValue("key1", "field1"), KeyValue("key2", "field2"), KeyValue("key3", 3), KeyValue("key4", ["nested", "value"]), ], ), ( ("field1:field2:3", None, [":"]), [ KeyValue("elem_0", "field1"), KeyValue("elem_1", "field2"), KeyValue("elem_2", 3), ], ), ], ) def test_kv_parser(input_data, expected): input_string = input_data[0] decoders = input_data[1] delims = input_data[2] tparser = ListParser(input_string, decoders, delims) tparser.parse() result = tparser.kv() assert len(expected) == len(result) for i in range(0, len(result)): assert result[i].key == expected[i].key assert result[i].value == expected[i].value kpos = result[i].meta.kpos kstr = result[i].meta.kstring vpos = result[i].meta.vpos vstr = result[i].meta.vstring assert input_string[kpos : kpos + len(kstr)] == kstr if vpos != -1: assert input_string[vpos : vpos + len(vstr)] == vstr openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_odp.py000066400000000000000000000526641514270232600244620ustar00rootroot00000000000000import netaddr import pytest from ovs.flow.odp import ODPFlow from ovs.flow.kv import KeyValue from ovs.flow.decoders import ( EthMask, IPMask, Mask32, Mask16, Mask8, Mask128, ) def do_test_section(input_string, section, expected): flow = ODPFlow(input_string) kv_list = flow.section(section).data assert len(expected) == len(kv_list) for i in range(len(expected)): assert expected[i].key == kv_list[i].key assert expected[i].value == kv_list[i].value # Assert positions relative to action string are OK. pos = flow.section(section).pos string = flow.section(section).string kpos = kv_list[i].meta.kpos kstr = kv_list[i].meta.kstring vpos = kv_list[i].meta.vpos vstr = kv_list[i].meta.vstring assert string[kpos : kpos + len(kstr)] == kstr if vpos != -1: assert string[vpos : vpos + len(vstr)] == vstr # Assert string meta is correct. assert input_string[pos : pos + len(string)] == string @pytest.mark.parametrize( "input_string,expected", [ ( "skb_priority(0x123),skb_mark(0x123),recirc_id(0x123),dp_hash(0x123),ct_zone(0x123), actions:", # noqa: E501 [ KeyValue("skb_priority", Mask32("0x123")), KeyValue("skb_mark", Mask32("0x123")), KeyValue("recirc_id", 0x123), KeyValue("dp_hash", Mask32("0x123")), KeyValue("ct_zone", Mask16("0x123")), ], ), ( "tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,ttl=64,flags(csum|key)) actions:", # noqa: E501 [ KeyValue( "tunnel", { "tun_id": 0x7F10354, "src": IPMask("10.10.10.10"), "dst": IPMask("20.20.20.20"), "ttl": 64, "flags": "csum|key", }, ) ], ), ( "tunnel(geneve({class=0,type=0,len=4,0xa/0xff}),vxlan(flags=0x800000,vni=0x1c7),erspan(ver=2,dir=1,hwid=0x1)), actions:", # noqa: E501 [ KeyValue( "tunnel", { "geneve": [ { "class": Mask16("0"), "type": Mask8("0"), "len": Mask8("4"), "data": Mask128("0xa/0xff"), } ], "vxlan": {"flags": 0x800000, "vni": 0x1C7}, "erspan": {"ver": 2, "dir": 1, "hwid": 0x1}, }, ) ], ), ( "in_port(2),eth(src=11:22:33:44:55:66,dst=66:55:44:33:22:11) actions:", # noqa: E501 [ KeyValue("in_port", 2), KeyValue( "eth", { "src": EthMask("11:22:33:44:55:66"), "dst": EthMask("66:55:44:33:22:11"), }, ), ], ), ( "eth_type(0x800/0x006),ipv4(src=192.168.1.1/24,dst=192.168.0.0/16,proto=0x1,tos=0x2/0xf0) actions:", # noqa: E501 [ KeyValue("eth_type", Mask16("0x800/0x006")), KeyValue( "ipv4", { "src": IPMask("192.168.1.1/24"), "dst": IPMask("192.168.0.0/16"), "proto": Mask8("0x1/0xFF"), "tos": Mask8("0x2/0xF0"), }, ), ], ), ( "encap(eth_type(0x800/0x006),ipv4(src=192.168.1.1/24,dst=192.168.0.0/16,proto=0x1,tos=0x2/0xf0)) actions:", # noqa: E501 [ KeyValue( "encap", { "eth_type": Mask16("0x800/0x006"), "ipv4": { "src": IPMask("192.168.1.1/24"), "dst": IPMask("192.168.0.0/16"), "proto": Mask8("0x1/0xff"), "tos": Mask8("0x2/0xf0"), }, }, ), ], ), ], ) def test_odp_fields(input_string, expected): do_test_section(input_string, "match", expected) @pytest.mark.parametrize( "input_string,expected", [ ( "actions:ct" ",ct(commit)" ",ct(commit,zone=5)" ",ct(commit,mark=0xa0a0a0a0/0xfefefefe)" ",ct(commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff)" # noqa: E501 ",ct(commit,helper=ftp)" ",ct(commit,helper=tftp)" ",ct(commit,timeout=ovs_tp_1_tcp4)" ",ct(nat)", [ KeyValue("ct", True), KeyValue("ct", {"commit": True}), KeyValue("ct", {"commit": True, "zone": 5}), KeyValue( "ct", {"commit": True, "mark": Mask32("0xA0A0A0A0/0xFEFEFEFE")}, ), KeyValue( "ct", { "commit": True, "label": Mask128( "0x1234567890ABCDEF1234567890ABCDEF/0xF1F2F3F4F5F6F7F8F9F0FAFBFCFDFEFF" # noqa: E501 ), }, ), KeyValue("ct", {"commit": True, "helper": "ftp"}), KeyValue("ct", {"commit": True, "helper": "tftp"}), KeyValue("ct", {"commit": True, "timeout": "ovs_tp_1_tcp4"}), KeyValue("ct", {"nat": True}), ], ), ( "actions:ct(nat)" ",ct(commit,nat(src))" ",ct(commit,nat(dst))" ",ct(commit,nat(src=10.0.0.240,random))" ",ct(commit,nat(src=10.0.0.240:32768-65535,random))" ",ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))" ",ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))" ",ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))" ",ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))" # noqa: E501 ",ct(commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random))" # noqa: E501 ",ct(commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255))" ",ct(force_commit)", [ KeyValue("ct", {"nat": True}), KeyValue("ct", {"commit": True, "nat": {"type": "src"}}), KeyValue("ct", {"commit": True, "nat": {"type": "dst"}}), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress("10.0.0.240"), "end": netaddr.IPAddress("10.0.0.240"), }, "random": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress("10.0.0.240"), "end": netaddr.IPAddress("10.0.0.240"), }, "ports": { "start": 32768, "end": 65535, }, "random": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "dst", "addrs": { "start": netaddr.IPAddress("10.0.0.128"), "end": netaddr.IPAddress("10.0.0.254"), }, "hash": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress("10.0.0.240"), "end": netaddr.IPAddress("10.0.0.254"), }, "ports": { "start": 32768, "end": 65535, }, "persistent": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress( "fe80::20c:29ff:fe88:a18b" ), "end": netaddr.IPAddress( "fe80::20c:29ff:fe88:a18b" ), }, "random": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress( "fe80::20c:29ff:fe88:1" ), "end": netaddr.IPAddress( "fe80::20c:29ff:fe88:a18b" ), }, "random": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress( "fe80::20c:29ff:fe88:1" ), "end": netaddr.IPAddress( "fe80::20c:29ff:fe88:a18b" ), }, "ports": { "start": 255, "end": 4096, }, "random": True, }, }, ), KeyValue( "ct", { "commit": True, "nat": { "type": "src", "addrs": { "start": netaddr.IPAddress("10.1.1.240"), "end": netaddr.IPAddress("10.1.1.255"), }, }, "helper": "ftp", }, ), KeyValue("ct", {"force_commit": True}), ], ), ( "actions:set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(df|csum|key)))" # noqa: E501 ",tnl_pop(4)" ",tnl_push(tnl_port(6),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1))" # noqa: E501 ",tnl_push(tnl_port(6),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1))", # noqa: E501 [ KeyValue( "set", { "tunnel": { "tun_id": 0xABCDEF1234567890, "src": IPMask("1.1.1.1"), "dst": IPMask("2.2.2.2"), "ttl": 64, "flags": "df|csum|key", } }, ), KeyValue("tnl_pop", 4), KeyValue( "tnl_push", { "tnl_port": 6, "header": { "size": 50, "type": 4, "eth": { "dst": EthMask("f8:bc:12:44:34:b6"), "src": EthMask("f8:bc:12:46:58:e0"), "dl_type": 0x800, }, "ipv4": { "src": IPMask("1.1.2.88"), "dst": IPMask("1.1.2.92"), "proto": 17, "tos": 0, "ttl": 64, "frag": 0x4000, }, "udp": {"src": 0, "dst": 4789, "csum": 0x0}, "vxlan": { "flags": 0x8000000, "vni": 0x1C7, }, }, "out_port": 1, }, ), KeyValue( "tnl_push", { "tnl_port": 6, "header": { "size": 70, "type": 4, "eth": { "dst": EthMask("f8:bc:12:44:34:b6"), "src": EthMask("f8:bc:12:46:58:e0"), "dl_type": 0x86DD, }, "ipv6": { "src": IPMask("2001:cafe::88"), "dst": IPMask("2001:cafe::92"), "label": 0, "proto": 17, "tclass": 0x0, "hlimit": 64, }, "udp": {"src": 0, "dst": 4789, "csum": 0x0}, "vxlan": { "flags": 0x8000000, "vni": 0x1C7, }, }, "out_port": 1, }, ), ], ), ( "actions:tnl_push(header(geneve(oam,vni=0x1c7)))" ",tnl_push(header(geneve(crit,vni=0x1c7,options({class=0xffff,type=0x80,len=4,0xa}))))" # noqa: E501 ",tnl_push(header(gre((flags=0xa000,proto=0x6558),csum=0x0,key=0x1e241)))", # noqa: E501 [ KeyValue( "tnl_push", { "header": { "geneve": { "oam": True, "vni": 0x1C7, } } }, ), KeyValue( "tnl_push", { "header": { "geneve": { "crit": True, "vni": 0x1C7, "options": [ { "class": 0xFFFF, "type": 0x80, "len": 4, "data": 0xA, } ], } } }, ), KeyValue( "tnl_push", { "header": { "gre": { "flags": 0xA000, "proto": 0x6558, "key": 0x1E241, "csum": 0x0, } } }, ), ], ), ( "actions:tnl_push(header(srv6(segments_left=1,segs(2001:cafe::90,2001:cafe::91))))", # noqa: E501 [ KeyValue( "tnl_push", { "header": { "srv6": { "segments_left": 1, "segs": "2001:cafe::90,2001:cafe::91", } } }, ), ], ), ( "actions:clone(1),clone(clone(push_vlan(vid=12,pcp=0),2),1)", [ KeyValue("clone", [{"output": {"port": 1}}]), KeyValue( "clone", [ { "clone": [ { "push_vlan": { "vid": 12, "pcp": 0, }, }, {"output": {"port": 2}}, ] }, {"output": {"port": 1}}, ], ), ], ), ( "actions:clone(recirc(0x1),recirc(0x2))", [ KeyValue( "clone", [ {"recirc": 1}, {"recirc": 2}, ], ), ], ), ( "actions: check_pkt_len(size=200,gt(4),le(5))" ",check_pkt_len(size=200,gt(drop),le(5))" ",check_pkt_len(size=200,gt(ct(nat)),le(drop))", [ KeyValue( "check_pkt_len", { "size": 200, "gt": [{"output": {"port": 4}}], "le": [{"output": {"port": 5}}], }, ), KeyValue( "check_pkt_len", { "size": 200, "gt": [{"drop": True}], "le": [{"output": {"port": 5}}], }, ), KeyValue( "check_pkt_len", { "size": 200, "gt": [{"ct": {"nat": True}}], "le": [{"drop": True}], }, ), ], ), ( "actions:check_pkt_len(size=200,gt(check_pkt_len(size=400,gt(4),le(2))),le(check_pkt_len(size=100,gt(1),le(drop))))", # noqa: E501 [ KeyValue( "check_pkt_len", { "size": 200, "gt": [ { "check_pkt_len": { "size": 400, "gt": [{"output": {"port": 4}}], "le": [{"output": {"port": 2}}], } } ], "le": [ { "check_pkt_len": { "size": 100, "gt": [{"output": {"port": 1}}], "le": [{"drop": True}], } } ], }, ) ], ), ( "actions:meter(1),hash(l4(0))", [ KeyValue("meter", 1), KeyValue( "hash", { "l4": 0, } ), ], ), ], ) def test_odp_actions(input_string, expected): do_test_section(input_string, "actions", expected) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/tests/test_ofp.py000066400000000000000000000703311514270232600244530ustar00rootroot00000000000000import netaddr import pytest from ovs.flow.ofp import OFPFlow from ovs.flow.kv import KeyValue, ParseError from ovs.flow.decoders import EthMask, IPMask, decode_mask def do_test_section(input_string, section, expected): flow = OFPFlow(input_string) kv_list = flow.section(section).data assert len(expected) == len(kv_list) for i in range(len(expected)): assert expected[i].key == kv_list[i].key assert expected[i].value == kv_list[i].value # Assert positions relative to action string are OK. pos = flow.section(section).pos string = flow.section(section).string kpos = kv_list[i].meta.kpos kstr = kv_list[i].meta.kstring vpos = kv_list[i].meta.vpos vstr = kv_list[i].meta.vstring assert string[kpos : kpos + len(kstr)] == kstr if vpos != -1: assert string[vpos : vpos + len(vstr)] == vstr # Assert string meta is correct. assert input_string[pos : pos + len(string)] == string @pytest.mark.parametrize( "input_string,expected", [ ( "actions=local,3,4,5,output:foo", [ KeyValue("output", {"port": "local"}), KeyValue("output", {"port": 3}), KeyValue("output", {"port": 4}), KeyValue("output", {"port": 5}), KeyValue("output", {"port": "foo"}), ], ), ( "actions=controller,controller:200", [ KeyValue("output", {"port": "CONTROLLER"}), KeyValue("controller", {"max_len": 200}), ], ), ( "actions=controller(max_len=123,reason=no_match,id=456,userdata=00.00.00.12.00.00.00.00,meter_id=12)", # noqa: E501 [ KeyValue( "controller", { "max_len": 123, "reason": "no_match", "id": 456, "userdata": "00.00.00.12.00.00.00.00", "meter_id": 12, } ), ], ), ( "actions=enqueue(foo,42),enqueue:foo:42,enqueue(bar,4242)", [ KeyValue("enqueue", {"port": "foo", "queue": 42}), KeyValue("enqueue", {"port": "foo", "queue": 42}), KeyValue("enqueue", {"port": "bar", "queue": 4242}), ], ), ( "actions=bundle(eth_src,0,hrw,ofport,members:4,8)", [ KeyValue( "bundle", { "fields": "eth_src", "basis": 0, "algorithm": "hrw", "members": [4, 8], }, ), ], ), ( "actions=bundle_load(eth_src,0,hrw,ofport,reg0,members:4,8)", [ KeyValue( "bundle_load", { "fields": "eth_src", "basis": 0, "algorithm": "hrw", "dst": "reg0", "members": [4, 8], }, ), ], ), ( "actions=group:3", [KeyValue("group", 3)], ), ( "actions=strip_vlan", [KeyValue("strip_vlan", True)], ), ( "actions=pop_vlan", [KeyValue("pop_vlan", True)], ), ( "actions=push_vlan:0x8100", [KeyValue("push_vlan", 0x8100)], ), ( "actions=push_mpls:0x8848", [KeyValue("push_mpls", 0x8848)], ), ( "actions=pop_mpls:0x8848", [KeyValue("pop_mpls", 0x8848)], ), ( "actions=pop_mpls:0x8848", [KeyValue("pop_mpls", 0x8848)], ), ( "actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))", [ KeyValue( "encap", { "header": "nsh", "props": { "md_type": 2, "tlv": { "class": 0x1000, "type": 10, "value": 0x12345678, }, }, }, ) ], ), ( "actions=encap(ethernet)", [ KeyValue( "encap", {"header": "ethernet"}, ) ], ), ( "actions=encap(mpls)", [ KeyValue( "encap", {"header": "mpls"}, ) ], ), ( "actions=load:0x001122334455->eth_src", [ KeyValue( "load", {"value": 0x001122334455, "dst": {"field": "eth_src"}}, ) ], ), ( "actions=load:1->eth_src[1]", [ KeyValue( "load", { "value": 1, "dst": {"field": "eth_src", "start": 1, "end": 1}, }, ) ], ), ( "actions=learn(load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[])", [ KeyValue( "learn", [ { "load": { "src": {"field": "NXM_NX_TUN_ID"}, "dst": {"field": "NXM_NX_TUN_ID"}, } } ], ), ], ), ( "actions=set_field:00:11:22:33:44:55->eth_src", [ KeyValue( "set_field", { "value": {"eth_src": EthMask("00:11:22:33:44:55")}, "dst": {"field": "eth_src"}, }, ) ], ), ( "actions=set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_src", [ KeyValue( "set_field", { "value": { "eth_src": EthMask( "01:00:00:00:00:00/01:00:00:00:00:00" ) }, "dst": {"field": "eth_src"}, }, ) ], ), ( "actions=set_field:0x10ff->vlan_vid", [ KeyValue( "set_field", { "value": {"vlan_vid": decode_mask(13)("0x10ff")}, "dst": {"field": "vlan_vid"}, }, ) ], ), ( "actions=move:reg0[0..5]->reg1[16..31]", [ KeyValue( "move", { "src": {"field": "reg0", "start": 0, "end": 5}, "dst": {"field": "reg1", "start": 16, "end": 31}, }, ) ], ), ( "actions=mod_dl_dst:00:11:22:33:44:55", [KeyValue("mod_dl_dst", EthMask("00:11:22:33:44:55"))], ), ( "actions=mod_nw_dst:192.168.1.1", [KeyValue("mod_nw_dst", IPMask("192.168.1.1"))], ), ( "actions=mod_nw_dst:fe80::ec17:7bff:fe61:7aac", [KeyValue("mod_nw_dst", IPMask("fe80::ec17:7bff:fe61:7aac"))], ), ( "actions=dec_ttl,dec_ttl(1,2,3)", [KeyValue("dec_ttl", True), KeyValue("dec_ttl", [1, 2, 3])], ), ( "actions=set_mpls_label:0x100,set_mpls_tc:2,set_mpls_ttl:10", [ KeyValue("set_mpls_label", 0x100), KeyValue("set_mpls_tc", 2), KeyValue("set_mpls_ttl", 10), ], ), ( "actions=check_pkt_larger(100)->reg0[10]", [ KeyValue( "check_pkt_larger", { "pkt_len": 100, "dst": {"field": "reg0", "start": 10, "end": 10}, }, ), ], ), ( "actions=pop_queue,set_tunnel:0x10,set_tunnel64:0x65000,set_queue=3", # noqa: E501 [ KeyValue("pop_queue", True), KeyValue("set_tunnel", 0x10), KeyValue("set_tunnel64", 0x65000), KeyValue("set_queue", 3), ], ), ( "actions=ct(zone=10,table=2,nat(snat=192.168.0.0-192.168.0.200:1000-2000,random))", # noqa: E501 [ KeyValue( "ct", { "zone": 10, "table": 2, "nat": { "type": "snat", "addrs": { "start": netaddr.IPAddress("192.168.0.0"), "end": netaddr.IPAddress("192.168.0.200"), }, "ports": { "start": 1000, "end": 2000, }, "random": True, }, }, ) ], ), ( "actions=ct(commit,zone=NXM_NX_REG13[0..15],table=2,exec(load:0->NXM_NX_CT_LABEL[0]))", # noqa: E501 [ KeyValue( "ct", { "commit": True, "zone": { "field": "NXM_NX_REG13", "start": 0, "end": 15, }, "table": 2, "exec": [ { "load": { "value": 0, "dst": { "field": "NXM_NX_CT_LABEL", "start": 0, "end": 0, }, }, }, ], }, ) ], ), ( "actions=load:0x1->NXM_NX_REG10[7],learn(table=69,delete_learned,cookie=0xda6f52b0,OXM_OF_METADATA[],eth_type=0x800,NXM_OF_IP_SRC[],ip_dst=172.30.204.105,nw_proto=6,NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[],load:0x1->NXM_NX_REG10[7])", # noqa: E501 [ KeyValue( "load", { "value": 1, "dst": {"field": "NXM_NX_REG10", "start": 7, "end": 7}, }, ), KeyValue( "learn", [ {"table": 69}, {"delete_learned": True}, {"cookie": 3664728752}, {"OXM_OF_METADATA[]": {"field": "OXM_OF_METADATA"}}, {"eth_type": 2048}, {"NXM_OF_IP_SRC[]": {"field": "NXM_OF_IP_SRC"}}, {"ip_dst": IPMask("172.30.204.105/32")}, {"nw_proto": 6}, {"NXM_OF_TCP_SRC[]": {"field": "NXM_OF_TCP_DST"}}, { "load": { "value": 1, "dst": { "field": "NXM_NX_REG10", "start": 7, "end": 7, }, } }, ], ), ], ), ( "actions=resubmit(,8),resubmit:3,resubmit(1,2,ct)", [ KeyValue("resubmit", {"port": "", "table": 8}), KeyValue("resubmit", {"port": 3}), KeyValue("resubmit", {"port": 1, "table": 2, "ct": True}), ], ), ( "actions=clone(ct_clear,load:0->NXM_NX_REG11[],load:0->NXM_NX_REG12[],load:0->NXM_NX_REG13[],load:0x1d->NXM_NX_REG13[],load:0x1f->NXM_NX_REG11[],load:0x1c->NXM_NX_REG12[],load:0x11->OXM_OF_METADATA[],load:0x2->NXM_NX_REG14[],load:0->NXM_NX_REG10[],load:0->NXM_NX_REG15[],load:0->NXM_NX_REG0[],load:0->NXM_NX_REG1[],load:0->NXM_NX_REG2[],load:0->NXM_NX_REG3[],load:0->NXM_NX_REG4[],load:0->NXM_NX_REG5[],load:0->NXM_NX_REG6[],load:0->NXM_NX_REG7[],load:0->NXM_NX_REG8[],load:0->NXM_NX_REG9[],resubmit(,8))", # noqa: E501 [ KeyValue( "clone", [ {"ct_clear": True}, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG11"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG12"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG13"}, } }, { "load": { "value": 29, "dst": {"field": "NXM_NX_REG13"}, } }, { "load": { "value": 31, "dst": {"field": "NXM_NX_REG11"}, } }, { "load": { "value": 28, "dst": {"field": "NXM_NX_REG12"}, } }, { "load": { "value": 17, "dst": {"field": "OXM_OF_METADATA"}, } }, { "load": { "value": 2, "dst": {"field": "NXM_NX_REG14"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG10"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG15"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG0"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG1"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG2"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG3"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG4"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG5"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG6"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG7"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG8"}, } }, { "load": { "value": 0, "dst": {"field": "NXM_NX_REG9"}, } }, {"resubmit": {"port": "", "table": 8}}, ], ) ], ), ( "actions=conjunction(1234, 1/2),note:00.00.11.22.33.ff,sample(probability=123,collector_set_id=0x123,obs_domain_id=0x123,obs_point_id=0x123,sampling_port=inport0,ingress)", # noqa: E501 [ KeyValue("conjunction", {"id": 1234, "k": 1, "n": 2}), KeyValue("note", "00.00.11.22.33.ff"), KeyValue( "sample", { "probability": 123, "collector_set_id": 0x123, "obs_domain_id": 0x123, "obs_point_id": 0x123, "sampling_port": "inport0", "ingress": True, }, ), ], ), ( "actions=POP_VLAN,push_vlan:0x8100,NORMAL,clone(MOD_NW_SRC:192.168.1.1,resubmit(,10))", # noqa: E501 [ KeyValue("POP_VLAN", True), KeyValue("push_vlan", 0x8100), KeyValue("output", {"port": "NORMAL"}), KeyValue( "clone", [ {"MOD_NW_SRC": netaddr.IPAddress("192.168.1.1")}, {"resubmit": {"port": "", "table": 10}}, ] ), ], ), ( "actions=MOD_NW_SRC:192.168.1.1,CONTROLLER,CONTROLLER:123", [ KeyValue("MOD_NW_SRC", netaddr.IPAddress("192.168.1.1")), KeyValue("output", {"port": "CONTROLLER"}), KeyValue("CONTROLLER", {"max_len": 123}), ], ), ( "actions=LOCAL,clone(myport,CONTROLLER)", [ KeyValue("output", {"port": "LOCAL"}), KeyValue( "clone", [ {"output": {"port": "myport"}}, {"output": {"port": "CONTROLLER"}}, ] ), ], ), ( "actions=LOCAL,clone(sample(probability=123))", [ KeyValue("output", {"port": "LOCAL"}), KeyValue( "clone", [ {"sample": { "probability": 123, }}, ] ), ], ), ( "actions=doesnotexist(1234)", ParseError, ), ( "actions=learn(eth_type=nofield)", ParseError, ), ( "actions=learn(nofield=eth_type)", ParseError, ), ( "nofield=0x123 actions=drop", ParseError, ), ( "actions=load:0x12334->NOFILED", ParseError, ), ( "actions=" + ",".join([ f"move:reg{i}[0..15]->reg{i + 1}[16..31]" for i in range(0, 31) ]), [ KeyValue( "move", { "src": { "field": f"reg{i}", "start": 0, "end": 15 }, "dst": { "field": f"reg{i + 1}", "start": 16, "end": 31 }, }, ) for i in range(0, 31) ], ), ( "actions=move:reg31[0..15]->reg32[16..31],move:reg32[0..15]->reg33[16..31]", # noqa: E501 ParseError, ), ( "actions=" + ",".join([ f"move:xreg{i}[0..31]->xreg{i + 1}[32..63]" for i in range(0, 15) ]), [ KeyValue( "move", { "src": { "field": f"xreg{i}", "start": 0, "end": 31 }, "dst": { "field": f"xreg{i + 1}", "start": 32, "end": 63 }, }, ) for i in range(0, 15) ], ), ( "actions=move:xreg16[0..31]->xreg17[32..63],move:xreg17[0..31]->xreg18[32..64]", # noqa: E501 ParseError, ), ( "actions=" + ",".join([ f"move:xxreg{i}[0..63]->xxreg{i + 1}[64..127]" for i in range(0, 7) ]), [ KeyValue( "move", { "src": { "field": f"xxreg{i}", "start": 0, "end": 63 }, "dst": { "field": f"xxreg{i + 1}", "start": 64, "end": 127 }, }, ) for i in range(0, 7) ], ), ( "actions=move:xxreg7[0..63]->xxreg8[64..128],move:xxreg8[0..63]->xxreg9[64..127]", # noqa: E501 ParseError, ), ( "actions=" + ",".join([ f"ct(commit,zone=NXM_NX_REG{i}[0..15],table={i})" for i in range(0, 16) ]), [ KeyValue( "ct", { "commit": True, "zone": { "field": f"NXM_NX_REG{i}", "start": 0, "end": 15, }, "table": i, }, ) for i in range(0, 16) ], ), ( "actions=" + ",".join([ f"ct(commit,zone=NXOXM_ET_REG{i}[0..15],table={i})" for i in range(16, 32) ]), [ KeyValue( "ct", { "commit": True, "zone": { "field": f"NXOXM_ET_REG{i}", "start": 0, "end": 15, }, "table": i, }, ) for i in range(16, 32) ], ), ( "actions=" + ",".join([ f"ct(commit,zone=OXM_OF_PKT_REG{i}[0..15],table={i})" for i in range(0, 16) ]), [ KeyValue( "ct", { "commit": True, "zone": { "field": f"OXM_OF_PKT_REG{i}", "start": 0, "end": 15, }, "table": i, }, ) for i in range(0, 16) ], ), ( "actions=" + ",".join([ f"ct(commit,zone=NXM_NX_XXREG{i}[0..15],table={i})" for i in range(0, 4) ]), [ KeyValue( "ct", { "commit": True, "zone": { "field": f"NXM_NX_XXREG{i}", "start": 0, "end": 15, }, "table": i, }, ) for i in range(0, 4) ], ), ( "actions=" + ",".join([ f"ct(commit,zone=NXOXM_ET_XXREG{i}[0..15],table={i})" for i in range(4, 8) ]), [ KeyValue( "ct", { "commit": True, "zone": { "field": f"NXOXM_ET_XXREG{i}", "start": 0, "end": 15, }, "table": i, }, ) for i in range(4, 8) ], ), ], ) def test_act(input_string, expected): if isinstance(expected, type): with pytest.raises(expected): OFPFlow(input_string) return do_test_section(input_string, "actions", expected) @pytest.mark.parametrize( "input_string,expected", [ ( "cookie=0x35f946ead8d8f9e4, duration=97746.271s, table=0, n_packets=12, n_bytes=254, idle_age=117, priority=4,in_port=1", # noqa: E501 ( [ KeyValue("cookie", 0x35f946ead8d8f9e4), KeyValue("duration", 97746.271), KeyValue("table", 0), KeyValue("n_packets", 12), KeyValue("n_bytes", 254), KeyValue("idle_age", 117), ], [ KeyValue("priority", 4), KeyValue("in_port", 1) ], ), ), ( "table=0, " + ",".join([f"reg{i}={i}" for i in range(0, 32)]), ( [ KeyValue("table", 0), ], [ KeyValue(f"reg{i}", i) for i in range(0, 32) ], ), ), ( "table=0, " + ",".join([f"xreg{i}={i}" for i in range(0, 16)]), ( [ KeyValue("table", 0), ], [ KeyValue(f"xreg{i}", i) for i in range(0, 16) ], ), ), ( "table=0, " + ",".join([f"xxreg{i}={i}" for i in range(0, 8)]), ( [ KeyValue("table", 0), ], [ KeyValue(f"xxreg{i}", i) for i in range(0, 8) ], ), ), ], ) def test_key(input_string, expected): if isinstance(expected, type): with pytest.raises(expected): OFPFlow(input_string) return input_string += " actions=drop" do_test_section(input_string, "info", expected[0]) do_test_section(input_string, "match", expected[1]) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/timeval.py000066400000000000000000000046001514270232600231230ustar00rootroot00000000000000# Copyright (c) 2009, 2010 Nicira, Inc. # # 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. import sys import time try: import ctypes LIBRT = 'librt.so.1' clock_gettime_name = 'clock_gettime' if sys.platform.startswith("linux"): CLOCK_MONOTONIC = 1 time_t = ctypes.c_long elif sys.platform.startswith("netbsd"): # NetBSD uses function renaming for ABI versioning. While the proper # way to get the appropriate version is of course "#include ", # it is difficult with ctypes. The following is appropriate for # recent versions of NetBSD, including NetBSD-6. LIBRT = 'libc.so.12' clock_gettime_name = '__clock_gettime50' CLOCK_MONOTONIC = 3 time_t = ctypes.c_int64 elif sys.platform.startswith("freebsd"): CLOCK_MONOTONIC = 4 time_t = ctypes.c_int64 else: raise Exception class timespec(ctypes.Structure): _fields_ = [ ('tv_sec', time_t), ('tv_nsec', ctypes.c_long), ] librt = ctypes.CDLL(LIBRT) clock_gettime = getattr(librt, clock_gettime_name) clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] except: # Librt shared library could not be loaded librt = None def monotonic(): if not librt: return time.time() t = timespec() if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) == 0: return t.tv_sec + t.tv_nsec * 1e-9 # Kernel does not support CLOCK_MONOTONIC return time.time() # Use time.monotonic() if Python version >= 3.3 if not hasattr(time, 'monotonic'): time.monotonic = monotonic def msec(): """ Returns the system's monotonic time if possible, otherwise returns the current time as the amount of time since the epoch, in milliseconds, as a float.""" return time.monotonic() * 1000.0 def postfork(): # Just a stub for now pass openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/unixctl/000077500000000000000000000000001514270232600225765ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/unixctl/__init__.py000066400000000000000000000062151514270232600247130ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. import enum import sys import ovs.util commands = {} @enum.unique # FIXME: Use @enum.verify(enum.NAMED_FLAGS) from Python 3.11 when available. class UnixctlOutputFormat(enum.IntFlag): TEXT = 1 << 0 JSON = 1 << 1 class _UnixctlCommand(object): def __init__(self, usage, min_args, max_args, callback, aux): self.usage = usage self.min_args = min_args self.max_args = max_args self.callback = callback self.aux = aux def _unixctl_help(conn, unused_argv, unused_aux): reply = "The available commands are:\n" command_names = sorted(commands.keys()) for name in command_names: reply += " " usage = commands[name].usage if usage: reply += "%-23s %s" % (name, usage) else: reply += name reply += "\n" conn.reply(reply) def command_register(name, usage, min_args, max_args, callback, aux): """ Registers a command with the given 'name' to be exposed by the UnixctlServer. 'usage' describes the arguments to the command; it is used only for presentation to the user in "help" output. 'callback' is called when the command is received. It is passed a UnixctlConnection object, the list of arguments as unicode strings, and 'aux'. Normally 'callback' should reply by calling UnixctlConnection.reply() or UnixctlConnection.reply_error() before it returns, but if the command cannot be handled immediately, then it can defer the reply until later. A given connection can only process a single request at a time, so a reply must be made eventually to avoid blocking that connection.""" assert isinstance(name, str) assert isinstance(usage, str) assert isinstance(min_args, int) assert isinstance(max_args, int) assert callable(callback) if name not in commands: commands[name] = _UnixctlCommand(usage, min_args, max_args, callback, aux) def socket_name_from_target(target): assert isinstance(target, str) """ On Windows an absolute path contains ':' ( i.e: C:\\ ) """ if target.startswith('/') or target.find(':') > -1: return 0, target pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target) pid = ovs.daemon.read_pidfile(pidfile_name) if pid < 0: return -pid, "cannot read pidfile \"%s\"" % pidfile_name if sys.platform == "win32": return 0, "%s/%s.ctl" % (ovs.dirs.RUNDIR, target) else: return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid) command_register("help", "", 0, 0, _unixctl_help, None) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/unixctl/client.py000066400000000000000000000037251514270232600244350ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. import os import ovs.json import ovs.jsonrpc import ovs.stream import ovs.util vlog = ovs.vlog.Vlog("unixctl_client") class UnixctlClient(object): def __init__(self, conn): assert isinstance(conn, ovs.jsonrpc.Connection) self._conn = conn def transact(self, command, argv): assert isinstance(command, str) assert isinstance(argv, list) for arg in argv: assert isinstance(arg, str) request = ovs.jsonrpc.Message.create_request(command, argv) error, reply = self._conn.transact_block(request) if error: vlog.warn("error communicating with %s: %s" % (self._conn.name, os.strerror(error))) return error, None, None if reply.error is not None: return 0, reply.error, None else: assert reply.result is not None return 0, None, reply.result def close(self): self._conn.close() self.conn = None @staticmethod def create(path): assert isinstance(path, str) unix = "unix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path) error, stream = ovs.stream.Stream.open_block( ovs.stream.Stream.open(unix)) if error: vlog.warn("failed to connect to %s" % path) return error, None return 0, UnixctlClient(ovs.jsonrpc.Connection(stream)) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/unixctl/server.py000066400000000000000000000174101514270232600244610ustar00rootroot00000000000000# Copyright (c) 2012 Nicira, Inc. # # 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. import argparse import copy import errno import os import sys import ovs.dirs import ovs.jsonrpc import ovs.stream import ovs.unixctl import ovs.util import ovs.version import ovs.vlog Message = ovs.jsonrpc.Message vlog = ovs.vlog.Vlog("unixctl_server") class UnixctlConnection(object): def __init__(self, rpc): assert isinstance(rpc, ovs.jsonrpc.Connection) self._rpc = rpc self._request_id = None self._fmt = ovs.unixctl.UnixctlOutputFormat.TEXT def run(self): self._rpc.run() error = self._rpc.get_status() if error or self._rpc.get_backlog(): return error for _ in range(10): if error or self._request_id: break error, msg = self._rpc.recv() if msg: if msg.type == Message.T_REQUEST: self._process_command(msg) else: # XXX: rate-limit vlog.warn("%s: received unexpected %s message" % (self._rpc.name, Message.type_to_string(msg.type))) error = errno.EINVAL if not error: error = self._rpc.get_status() return error def reply(self, body): assert body is None or isinstance(body, str) if body is None: body = "" if self._fmt == ovs.unixctl.UnixctlOutputFormat.JSON: body = { "reply-format": "plain", "reply": body } return self._reply_impl_json(True, body) def reply_json(self, body): self._reply_impl_json(True, body) def reply_error(self, body): assert body is None or isinstance(body, str) if body is None: body = "" return self._reply_impl_json(False, body) # Called only by unixctl classes. def _close(self): self._rpc.close() self._request_id = None def _wait(self, poller): self._rpc.wait(poller) if not self._rpc.get_backlog(): self._rpc.recv_wait(poller) def _reply_impl_json(self, success, body): assert isinstance(success, bool) assert self._request_id is not None if success: reply = Message.create_reply(body, self._request_id) else: reply = Message.create_error(body, self._request_id) self._rpc.send(reply) self._request_id = None def _process_command(self, request): assert isinstance(request, ovs.jsonrpc.Message) assert request.type == ovs.jsonrpc.Message.T_REQUEST self._request_id = request.id error = None params = request.params method = request.method command = ovs.unixctl.commands.get(method) if command is None: error = '"%s" is not a valid command' % method elif len(params) < command.min_args: error = '"%s" command requires at least %d arguments' \ % (method, command.min_args) elif len(params) > command.max_args: error = '"%s" command takes at most %d arguments' \ % (method, command.max_args) else: for param in params: if not isinstance(param, str): error = '"%s" command has non-string argument' % method break if error is None: unicode_params = [str(p) for p in params] command.callback(self, unicode_params, command.aux) if error: self.reply_error(error) def _unixctl_version(conn, unused_argv, version): assert isinstance(conn, UnixctlConnection) version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version) conn.reply(version) def _unixctl_set_options(conn, argv, unused_aux): assert isinstance(conn, UnixctlConnection) parser = argparse.ArgumentParser() parser.add_argument("--format", default="text", choices=[fmt.name.lower() for fmt in ovs.unixctl.UnixctlOutputFormat], type=str.lower) try: args = parser.parse_args(args=argv) except argparse.ArgumentError as e: conn.reply_error(str(e)) return conn._fmt = ovs.unixctl.UnixctlOutputFormat[args.format.upper()] conn.reply(None) class UnixctlServer(object): def __init__(self, listener): assert isinstance(listener, ovs.stream.PassiveStream) self._listener = listener self._conns = [] def run(self): for _ in range(10): error, stream = self._listener.accept() if sys.platform == "win32" and error == errno.WSAEWOULDBLOCK: # WSAEWOULDBLOCK would be the equivalent on Windows # for EAGAIN on Unix. error = errno.EAGAIN if not error: rpc = ovs.jsonrpc.Connection(stream) self._conns.append(UnixctlConnection(rpc)) elif error == errno.EAGAIN: break else: # XXX: rate-limit vlog.warn("%s: accept failed: %s" % (self._listener.name, os.strerror(error))) for conn in copy.copy(self._conns): error = conn.run() if error and error != errno.EAGAIN: conn._close() self._conns.remove(conn) def wait(self, poller): self._listener.wait(poller) for conn in self._conns: conn._wait(poller) def close(self): for conn in self._conns: conn._close() self._conns = None self._listener.close() self._listener = None @staticmethod def create(path, version=None): """Creates a new UnixctlServer which listens on a unixctl socket created at 'path'. If 'path' is None, the default path is chosen. 'version' contains the version of the server as reported by the unixctl version command. If None, ovs.version.VERSION is used.""" assert path is None or isinstance(path, str) if path is not None: path = "punix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path) else: if sys.platform == "win32": path = "punix:%s/%s.ctl" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME) else: path = "punix:%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME, os.getpid()) if version is None: version = ovs.version.VERSION error, listener = ovs.stream.PassiveStream.open(path) if error: ovs.util.ovs_error(error, "could not initialize control socket %s" % path) return error, None ovs.unixctl.command_register("version", "", 0, 0, _unixctl_version, version) ovs.unixctl.command_register("set-options", "[--format text|json]", 1, 2, _unixctl_set_options, None) return 0, UnixctlServer(listener) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/util.py000066400000000000000000000060601514270232600224410ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # 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. import os import os.path import sys PROGRAM_NAME = os.path.basename(sys.argv[0]) EOF = -1 def abs_file_name(dir_, file_name): """If 'file_name' starts with '/', returns a copy of 'file_name'. Otherwise, returns an absolute path to 'file_name' considering it relative to 'dir_', which itself must be absolute. 'dir_' may be None or the empty string, in which case the current working directory is used. Returns None if 'dir_' is None and getcwd() fails. This differs from os.path.abspath() in that it will never change the meaning of a file name. On Windows an absolute path contains ':' ( i.e: C:\\ ) """ if file_name.startswith('/') or file_name.find(':') > -1: return file_name else: if dir_ is None or dir_ == "": try: dir_ = os.getcwd() except OSError: return None if dir_.endswith('/'): return dir_ + file_name else: return "%s/%s" % (dir_, file_name) def ovs_retval_to_string(retval): """Many OVS functions return an int which is one of: - 0: no error yet - >0: errno value - EOF: end of file (not necessarily an error; depends on the function called) Returns the appropriate human-readable string.""" if not retval: return "" if retval > 0: return os.strerror(retval) if retval == EOF: return "End of file" return "***unknown return value: %s***" % retval def ovs_error(err_no, message, vlog=None): """Prints 'message' on stderr and emits an ERROR level log message to 'vlog' if supplied. If 'err_no' is nonzero, then it is formatted with ovs_retval_to_string() and appended to the message inside parentheses. 'message' should not end with a new-line, because this function will add one itself.""" err_msg = "%s: %s" % (PROGRAM_NAME, message) if err_no: err_msg += " (%s)" % ovs_retval_to_string(err_no) sys.stderr.write("%s\n" % err_msg) if vlog: vlog.err(err_msg) def ovs_fatal(*args, **kwargs): """Prints 'message' on stderr and emits an ERROR level log message to 'vlog' if supplied. If 'err_no' is nonzero, then it is formatted with ovs_retval_to_string() and appended to the message inside parentheses. Then, terminates with exit code 1 (indicating a failure). 'message' should not end with a new-line, because this function will add one itself.""" ovs_error(*args, **kwargs) sys.exit(1) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/vlog.py000066400000000000000000000404661514270232600224430ustar00rootroot00000000000000 # Copyright (c) 2011, 2012, 2013, 2015, 2016 Nicira, Inc. # # 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. import datetime import logging import logging.handlers import os import re import socket import sys import threading import ovs.dirs import ovs.unixctl import ovs.util DESTINATIONS = {"console": "info", "file": "info", "syslog": "info"} PATTERNS = { "console": "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m", "file": "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m", "syslog": "ovs|%05N|%c%T|%p|%m", } LEVELS = { "dbg": logging.DEBUG, "info": logging.INFO, "warn": logging.WARNING, "err": logging.ERROR, "emer": logging.CRITICAL, "off": logging.CRITICAL } FACILITIES = ['auth', 'authpriv', 'cron', 'daemon', 'ftp', 'kern', 'lpr', 'mail', 'news', 'syslog', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7'] syslog_facility = "daemon" syslog_handler = '' def get_level(level_str): return LEVELS.get(level_str.lower()) class Vlog(object): __inited = False __msg_num = 0 __start_time = 0 __mfl = {} # Module -> destination -> level __log_file = None __file_handler = None __log_patterns = PATTERNS def __init__(self, name): """Creates a new Vlog object representing a module called 'name'. The created Vlog object will do nothing until the Vlog.init() static method is called. Once called, no more Vlog objects may be created.""" assert not Vlog.__inited self.name = name.lower() if name not in Vlog.__mfl: Vlog.__mfl[self.name] = DESTINATIONS.copy() def __log(self, level, message, **kwargs): if not Vlog.__inited: return level_num = LEVELS.get(level.lower(), logging.DEBUG) msg_num = Vlog.__msg_num Vlog.__msg_num += 1 for f, f_level in Vlog.__mfl[self.name].items(): f_level = LEVELS.get(f_level, logging.CRITICAL) if level_num >= f_level: msg = self._build_message(message, f, level, msg_num) logging.getLogger(f).log(level_num, msg, **kwargs) def _build_message(self, message, destination, level, msg_num): pattern = self.__log_patterns[destination] tmp = pattern tmp = self._format_time(tmp) matches = re.findall("(%-?[0]?[0-9]?[AcmNnpPrtT])", tmp) for m in matches: if "A" in m: tmp = self._format_field(tmp, m, ovs.util.PROGRAM_NAME) elif "c" in m: tmp = self._format_field(tmp, m, self.name) elif "m" in m: tmp = self._format_field(tmp, m, message) elif "N" in m: tmp = self._format_field(tmp, m, str(msg_num)) elif "n" in m: tmp = re.sub(m, "\n", tmp) elif "p" in m: tmp = self._format_field(tmp, m, level.upper()) elif "P" in m: self._format_field(tmp, m, str(os.getpid())) elif "r" in m: now = datetime.datetime.utcnow() delta = now - self.__start_time ms = delta.microseconds / 1000 tmp = self._format_field(tmp, m, str(ms)) elif "t" in m: subprogram = threading.currentThread().getName() if subprogram == "MainThread": subprogram = "main" tmp = self._format_field(tmp, m, subprogram) elif "T" in m: subprogram = threading.currentThread().getName() if not subprogram == "MainThread": subprogram = "({})".format(subprogram) else: subprogram = "" tmp = self._format_field(tmp, m, subprogram) return tmp.strip() def _format_field(self, tmp, match, replace): formatting = re.compile("^%(0)?([1-9])?") matches = formatting.match(match) # Do we need to apply padding? if not matches.group(1) and replace != "": replace = replace.center(len(replace) + 2) # Does the field have a minimum width if matches.group(2): min_width = int(matches.group(2)) if len(replace) < min_width: replace = replace.center(min_width) return re.sub(match, replace.replace('\\', r'\\'), tmp) def _format_time(self, tmp): date_regex = re.compile(r'(%(0?[1-9]?[dD])(\{(.*)\})?)') match = date_regex.search(tmp) if match is None: return tmp # UTC date or Local TZ? if match.group(2) == "d": now = datetime.datetime.now() elif match.group(2) == "D": now = datetime.datetime.utcnow() # Custom format or ISO format? if match.group(3): time = datetime.date.strftime(now, match.group(4)) try: i = len(re.search("#+", match.group(4)).group(0)) msec = '{0:0>{i}.{i}}'.format(str(now.microsecond / 1000), i=i) time = re.sub('#+', msec, time) except AttributeError: pass else: time = datetime.datetime.isoformat(now.replace(microsecond=0)) return self._format_field(tmp, match.group(1), time) def emer(self, message, **kwargs): self.__log("EMER", message, **kwargs) def err(self, message, **kwargs): self.__log("ERR", message, **kwargs) def warn(self, message, **kwargs): self.__log("WARN", message, **kwargs) def info(self, message, **kwargs): self.__log("INFO", message, **kwargs) def dbg(self, message, **kwargs): self.__log("DBG", message, **kwargs) def __is_enabled(self, level): level = LEVELS.get(level.lower(), logging.DEBUG) for f, f_level in Vlog.__mfl[self.name].items(): f_level = LEVELS.get(f_level, logging.CRITICAL) if level >= f_level: return True return False def emer_is_enabled(self): return self.__is_enabled("EMER") def err_is_enabled(self): return self.__is_enabled("ERR") def warn_is_enabled(self): return self.__is_enabled("WARN") def info_is_enabled(self): return self.__is_enabled("INFO") def dbg_is_enabled(self): return self.__is_enabled("DBG") def exception(self, message): """Logs 'message' at ERR log level. Includes a backtrace when in exception context.""" self.err(message, exc_info=True) @staticmethod def init(log_file=None): """Intializes the Vlog module. Causes Vlog to write to 'log_file' if not None. Should be called after all Vlog objects have been created. No logging will occur until this function is called.""" if Vlog.__inited: return Vlog.__inited = True Vlog.__start_time = datetime.datetime.utcnow() logging.raiseExceptions = False Vlog.__log_file = log_file for f in DESTINATIONS: logger = logging.getLogger(f) logger.setLevel(logging.DEBUG) try: if f == "console": logger.addHandler(logging.StreamHandler(sys.stderr)) elif f == "syslog": Vlog.add_syslog_handler() elif f == "file" and Vlog.__log_file: Vlog.__file_handler = logging.FileHandler(Vlog.__log_file) logger.addHandler(Vlog.__file_handler) except (IOError, socket.error): logger.disabled = True ovs.unixctl.command_register("vlog/reopen", "", 0, 0, Vlog._unixctl_vlog_reopen, None) ovs.unixctl.command_register("vlog/close", "", 0, 0, Vlog._unixctl_vlog_close, None) try: # Windows limitation on Python 2, sys.maxsize is a long number # on 64 bits environments, while sys.maxint is the maximum int # number. Python 3 works as expected. maxsize_int = sys.maxint except AttributeError: maxsize_int = sys.maxsize ovs.unixctl.command_register("vlog/set", "spec", 1, maxsize_int, Vlog._unixctl_vlog_set, None) ovs.unixctl.command_register("vlog/list", "", 0, 0, Vlog._unixctl_vlog_list, None) @staticmethod def set_level(module, destination, level): """ Sets the log level of the 'module'-'destination' tuple to 'level'. All three arguments are strings which are interpreted the same as arguments to the --verbose flag. Should be called after all Vlog objects have already been created.""" module = module.lower() destination = destination.lower() level = level.lower() if destination != "any" and destination not in DESTINATIONS: return if module != "any" and module not in Vlog.__mfl: return if level not in LEVELS: return if module == "any": modules = list(Vlog.__mfl.keys()) else: modules = [module] if destination == "any": destinations = list(DESTINATIONS.keys()) else: destinations = [destination] for m in modules: for f in destinations: Vlog.__mfl[m][f] = level @staticmethod def set_pattern(destination, pattern): """ Sets the log pattern of the 'destination' to 'pattern' """ destination = destination.lower() Vlog.__log_patterns[destination] = pattern @staticmethod def add_syslog_handler(facility=None): global syslog_facility, syslog_handler # If handler is already added and there is no change in 'facility', # there is nothing to do. if (not facility or facility == syslog_facility) and syslog_handler: return logger = logging.getLogger('syslog') # Disable the logger if the "null" syslog method requested # by environment. if os.environ.get('OVS_SYSLOG_METHOD') == "null": logger.disabled = True return if facility is None: facility = syslog_facility new_handler = logging.handlers.SysLogHandler(address="/dev/log", facility=facility) if syslog_handler: logger.removeHandler(syslog_handler) syslog_handler = new_handler syslog_facility = facility logger.addHandler(syslog_handler) return @staticmethod def set_levels_from_string(s): module = None level = None destination = None words = re.split('[ :]', s) if words[0] == "pattern": try: if words[1] in DESTINATIONS and words[2]: segments = [words[i] for i in range(2, len(words))] pattern = "".join(segments) Vlog.set_pattern(words[1], pattern) return else: return "Destination %s does not exist" % words[1] except IndexError: return "Please supply a valid pattern and destination" elif words[0] == "FACILITY": if words[1] in FACILITIES: try: Vlog.add_syslog_handler(words[1]) except (IOError, socket.error): logger = logging.getLogger('syslog') logger.disabled = True return else: return "Facility %s is invalid" % words[1] for word in [w.lower() for w in words]: if word == "any": pass elif word in DESTINATIONS: if destination: return "cannot specify multiple destinations" destination = word elif word in LEVELS: if level: return "cannot specify multiple levels" level = word elif word in Vlog.__mfl: if module: return "cannot specify multiple modules" module = word else: return "no destination, level, or module \"%s\"" % word Vlog.set_level(module or "any", destination or "any", level or "any") @staticmethod def get_levels(): lines = [" console syslog file\n", " ------- ------ ------\n"] lines.extend(sorted(["%-16s %4s %4s %4s\n" % (m, Vlog.__mfl[m]["console"], Vlog.__mfl[m]["syslog"], Vlog.__mfl[m]["file"]) for m in Vlog.__mfl])) return ''.join(lines) @staticmethod def reopen_log_file(): """Closes and then attempts to re-open the current log file. (This is useful just after log rotation, to ensure that the new log file starts being used.)""" if Vlog.__log_file: logger = logging.getLogger("file") logger.removeHandler(Vlog.__file_handler) Vlog.__file_handler = logging.FileHandler(Vlog.__log_file) logger.addHandler(Vlog.__file_handler) @staticmethod def close_log_file(): """Closes the current log file. (This is useful on Windows, to ensure that a reference to the file is not kept by the daemon in case of detach.)""" if Vlog.__log_file: logger = logging.getLogger("file") logger.removeHandler(Vlog.__file_handler) Vlog.__file_handler.close() @staticmethod def _unixctl_vlog_reopen(conn, unused_argv, unused_aux): if Vlog.__log_file: Vlog.reopen_log_file() conn.reply(None) else: conn.reply("Logging to file not configured") @staticmethod def _unixctl_vlog_close(conn, unused_argv, unused_aux): if Vlog.__log_file: if sys.platform != 'win32': logger = logging.getLogger("file") logger.removeHandler(Vlog.__file_handler) else: Vlog.close_log_file() conn.reply(None) @staticmethod def _unixctl_vlog_set(conn, argv, unused_aux): for arg in argv: msg = Vlog.set_levels_from_string(arg) if msg: conn.reply(msg) return conn.reply(None) @staticmethod def _unixctl_vlog_list(conn, unused_argv, unused_aux): conn.reply(Vlog.get_levels()) def add_args(parser): """Adds vlog related options to 'parser', an ArgumentParser object. The resulting arguments parsed by 'parser' should be passed to handle_args.""" group = parser.add_argument_group(title="Logging Options") group.add_argument("--log-file", nargs="?", const="default", help="Enables logging to a file. Default log file" " is used if LOG_FILE is omitted.") group.add_argument("-v", "--verbose", nargs="*", help="Sets logging levels, see ovs-vswitchd(8)." " Defaults to dbg.") def handle_args(args): """ Handles command line arguments ('args') parsed by an ArgumentParser. The ArgumentParser should have been primed by add_args(). Also takes care of initializing the Vlog module.""" log_file = args.log_file if log_file == "default": log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME) if args.verbose is None: args.verbose = [] elif args.verbose == []: args.verbose = ["any:any:dbg"] for verbose in args.verbose: msg = Vlog.set_levels_from_string(verbose) if msg: ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg)) Vlog.init(log_file) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs/winutils.py000066400000000000000000000220121514270232600233350ustar00rootroot00000000000000# Copyright (c) 2016 Cloudbase Solutions Srl # # 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. import sys if sys.platform != 'win32': raise Exception("Intended to use only on Windows") else: import ntsecuritycon import pywintypes import win32con import win32event import win32file import win32pipe import win32security import winerror def close_handle(handle, logger=None): try: win32file.CloseHandle(handle) return None except pywintypes.error as e: if logger is not None: logger("failed to close handle: %s" % e.strerror) return e.winerror def windows_create_pipe(sAttrs=-1, nSize=None): # Default values if parameters are not passed if sAttrs == -1: sAttrs = win32security.SECURITY_ATTRIBUTES() sAttrs.bInheritHandle = 1 if nSize is None: # If this parameter is zero, the system uses the default buffer size. nSize = 0 try: (read_pipe, write_pipe) = win32pipe.CreatePipe(sAttrs, nSize) except pywintypes.error: raise return (read_pipe, write_pipe) def windows_read_pipe(fd, length): try: (error, data) = win32file.ReadFile(fd, length) return error, data except pywintypes.error as e: return e.winerror, "" def create_file(filename, desiredAccess=None, shareMode=None, attributes=-1, CreationDisposition=None, flagsAndAttributes=None, hTemplateFile=-1): # Default values if parameters are not passed if desiredAccess is None: desiredAccess = win32file.GENERIC_READ | win32file.GENERIC_WRITE if shareMode is None: shareMode = 0 if attributes == -1: # attributes can be None attributes = None if CreationDisposition is None: CreationDisposition = win32file.OPEN_EXISTING if flagsAndAttributes is None: flagsAndAttributes = (win32file.FILE_ATTRIBUTE_NORMAL | win32file.FILE_FLAG_OVERLAPPED | win32file.FILE_FLAG_NO_BUFFERING) if hTemplateFile == -1: hTemplateFile = None try: npipe = win32file.CreateFile(filename, desiredAccess, shareMode, attributes, CreationDisposition, flagsAndAttributes, hTemplateFile) except pywintypes.error: raise return npipe def write_file(handle, data, overlapped=None): try: (errCode, nBytesWritten) = win32file.WriteFile(handle, data, overlapped) # Note: win32file.WriteFile doesn't throw an exception # in case it receives ERROR_IO_PENDING. return (errCode, nBytesWritten) except pywintypes.error as e: return (e.winerror, 0) def read_file(handle, bufsize, overlapped=None): try: # Note: win32file.ReadFile doesn't throw an exception # in case it receives ERROR_IO_PENDING. (errCode, read_buffer) = win32file.ReadFile( handle, bufsize, overlapped) return (errCode, read_buffer) except pywintypes.error as e: return (e.winerror, "") def create_named_pipe(pipename, openMode=None, pipeMode=None, nMaxInstances=None, nOutBufferSize=None, nInBufferSize=None, nDefaultTimeOut=None, saAttr=-1): # Default values if parameters are not passed if openMode is None: openMode = win32con.PIPE_ACCESS_DUPLEX | win32con.FILE_FLAG_OVERLAPPED if pipeMode is None: pipeMode = (win32con.PIPE_TYPE_MESSAGE | win32con.PIPE_READMODE_BYTE | win32con.PIPE_WAIT) if nMaxInstances is None: nMaxInstances = 64 if nOutBufferSize is None: nOutBufferSize = 65000 if nInBufferSize is None: nInBufferSize = 65000 if nDefaultTimeOut is None: nDefaultTimeOut = 0 if saAttr == -1: # saAttr can be None saAttr = win32security.SECURITY_ATTRIBUTES() # The identifier authority. sia = ntsecuritycon.SECURITY_NT_AUTHORITY # Initialize the SID. remoteAccessSid = win32security.SID() remoteAccessSid.Initialize( sia, # The identifier authority. 1) # The number of sub authorities to allocate. # Disable access over network. remoteAccessSid.SetSubAuthority( 0, # The index of the sub authority to set ntsecuritycon.SECURITY_NETWORK_RID) allowedPsids = [] # Allow Windows Services to access the Named Pipe. allowedPsid_0 = win32security.SID() allowedPsid_0.Initialize( sia, # The identifier authority. 1) # The number of sub authorities to allocate. allowedPsid_0.SetSubAuthority( 0, # The index of the sub authority to set ntsecuritycon.SECURITY_LOCAL_SYSTEM_RID) # Allow Administrators to access the Named Pipe. allowedPsid_1 = win32security.SID() allowedPsid_1.Initialize( sia, # The identifier authority. 2) # The number of sub authorities to allocate. allowedPsid_1.SetSubAuthority( 0, # The index of the sub authority to set ntsecuritycon.SECURITY_BUILTIN_DOMAIN_RID) allowedPsid_1.SetSubAuthority( 1, # The index of the sub authority to set ntsecuritycon.DOMAIN_ALIAS_RID_ADMINS) allowedPsids.append(allowedPsid_0) allowedPsids.append(allowedPsid_1) # Initialize an ACL. acl = win32security.ACL() acl.Initialize() # Add denied ACL. acl.AddAccessDeniedAce(win32security.ACL_REVISION, ntsecuritycon.GENERIC_ALL, remoteAccessSid) # Add allowed ACLs. for allowedPsid in allowedPsids: acl.AddAccessAllowedAce(win32security.ACL_REVISION, ntsecuritycon.GENERIC_ALL, allowedPsid) # Initialize an SD. sd = win32security.SECURITY_DESCRIPTOR() sd.Initialize() # Set DACL. sd.SetSecurityDescriptorDacl(True, acl, False) saAttr.bInheritHandle = 1 saAttr.SECURITY_DESCRIPTOR = sd try: npipe = win32pipe.CreateNamedPipe(pipename, openMode, pipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, saAttr) if npipe == win32file.INVALID_HANDLE_VALUE: return None return npipe except pywintypes.error: return None def set_pipe_mode(hPipe, mode=-1, maxCollectionCount=None, collectDataTimeout=None): # Default values if parameters are not passed if mode == -1: mode = win32pipe.PIPE_READMODE_BYTE try: win32pipe.SetNamedPipeHandleState( hPipe, mode, maxCollectionCount, collectDataTimeout) except pywintypes.error: raise def connect_named_pipe(pipe_handle, overlapped=None): try: # If the result of ConnectNamedPipe is ERROR_IO_PENDING or # ERROR_PIPE_CONNECTED, then this value is returned. # All other error values raise a win32 exception error = win32pipe.ConnectNamedPipe(pipe_handle, overlapped) return error except pywintypes.error as e: return e.winerror def get_pipe_name(name): name = name.replace('/', '') name = name.replace('\\', '') name = "\\\\.\\pipe\\" + name return name def get_overlapped_result(handle, overlapped=None, bWait=False): try: return win32file.GetOverlappedResult(handle, overlapped, bWait) except pywintypes.error: raise def get_new_event(sa=None, bManualReset=True, bInitialState=True, objectName=None): return win32event.CreateEvent(sa, bManualReset, bInitialState, objectName) pipe_disconnected_errors = [winerror.ERROR_PIPE_NOT_CONNECTED, winerror.ERROR_BAD_PIPE, winerror.ERROR_NO_DATA, winerror.ERROR_BROKEN_PIPE] openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs_build_helpers/000077500000000000000000000000001514270232600240115ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs_build_helpers/__init__.py000066400000000000000000000000001514270232600261100ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs_build_helpers/extract_ofp_fields.py000066400000000000000000000273741514270232600302440ustar00rootroot00000000000000import sys import re line = "" # Maps from user-friendly version number to its protocol encoding. VERSION = { "1.0": 0x01, "1.1": 0x02, "1.2": 0x03, "1.3": 0x04, "1.4": 0x05, "1.5": 0x06, } VERSION_REVERSE = dict((v, k) for k, v in VERSION.items()) TYPES = { "u8": (1, False), "be16": (2, False), "be32": (4, False), "MAC": (6, False), "be64": (8, False), "be128": (16, False), "tunnelMD": (124, True), } FORMATTING = { "decimal": ("MFS_DECIMAL", 1, 8), "hexadecimal": ("MFS_HEXADECIMAL", 1, 127), "ct state": ("MFS_CT_STATE", 4, 4), "Ethernet": ("MFS_ETHERNET", 6, 6), "IPv4": ("MFS_IPV4", 4, 4), "IPv6": ("MFS_IPV6", 16, 16), "OpenFlow 1.0 port": ("MFS_OFP_PORT", 2, 2), "OpenFlow 1.1+ port": ("MFS_OFP_PORT_OXM", 4, 4), "frag": ("MFS_FRAG", 1, 1), "tunnel flags": ("MFS_TNL_FLAGS", 2, 2), "TCP flags": ("MFS_TCP_FLAGS", 2, 2), "packet type": ("MFS_PACKET_TYPE", 4, 4), } PREREQS = { "none": "MFP_NONE", "Ethernet": "MFP_ETHERNET", "ARP": "MFP_ARP", "VLAN VID": "MFP_VLAN_VID", "IPv4": "MFP_IPV4", "IPv6": "MFP_IPV6", "IPv4/IPv6": "MFP_IP_ANY", "NSH": "MFP_NSH", "CT": "MFP_CT_VALID", "MPLS": "MFP_MPLS", "TCP": "MFP_TCP", "UDP": "MFP_UDP", "SCTP": "MFP_SCTP", "ICMPv4": "MFP_ICMPV4", "ICMPv6": "MFP_ICMPV6", "ND": "MFP_ND", "ND solicit": "MFP_ND_SOLICIT", "ND advert": "MFP_ND_ADVERT", } # Maps a name prefix into an (experimenter ID, class) pair, so: # # - Standard OXM classes are written as (0, ) # # - Experimenter OXM classes are written as (, 0xffff) # # If a name matches more than one prefix, the longest one is used. OXM_CLASSES = { "NXM_OF_": (0, 0x0000, "extension"), "NXM_NX_": (0, 0x0001, "extension"), "NXOXM_NSH_": (0x005AD650, 0xFFFF, "extension"), "OXM_OF_": (0, 0x8000, "standard"), "OXM_OF_PKT_REG": (0, 0x8001, "standard"), "ONFOXM_ET_": (0x4F4E4600, 0xFFFF, "standard"), "ERICOXM_OF_": (0, 0x1000, "extension"), # This is the experimenter OXM class for Nicira, which is the # one that OVS would be using instead of NXM_OF_ and NXM_NX_ # if OVS didn't have those grandfathered in. It is currently # used only to test support for experimenter OXM, since there # are barely any real uses of experimenter OXM in the wild. "NXOXM_ET_": (0x00002320, 0xFFFF, "extension"), } def oxm_name_to_class(name): prefix = "" class_ = None for p, c in OXM_CLASSES.items(): if name.startswith(p) and len(p) > len(prefix): prefix = p class_ = c return class_ def is_standard_oxm(name): oxm_vendor, oxm_class, oxm_class_type = oxm_name_to_class(name) return oxm_class_type == "standard" def get_line(): global line global line_number line = input_file.readline() line_number += 1 if line == "": fatal("unexpected end of input") n_errors = 0 def error(msg): global n_errors sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) n_errors += 1 def fatal(msg): error(msg) sys.exit(1) def parse_oxms(s, prefix, n_bytes): if s == "none": return () return tuple(parse_oxm(s2.strip(), prefix, n_bytes) for s2 in s.split(",")) match_types = dict() def parse_oxm(s, prefix, n_bytes): global match_types m = re.match( r"([A-Z0-9_]+)\(([0-9]+)\) since(?: OF(1\.[0-9]+) and)? v([123]\.[0-9]+)$", # noqa: E501 s, ) if not m: fatal("%s: syntax error parsing %s" % (s, prefix)) name, oxm_type, of_version, ovs_version = m.groups() class_ = oxm_name_to_class(name) if class_ is None: fatal("unknown OXM class for %s" % name) oxm_vendor, oxm_class, oxm_class_type = class_ if int(oxm_type) > 127: fatal("%s: OXM field is out of range (%s > 127)" % (name, oxm_type)) if class_ in match_types: if oxm_type in match_types[class_]: fatal( "duplicate match type for %s (conflicts with %s)" % (name, match_types[class_][oxm_type]) ) else: match_types[class_] = dict() match_types[class_][oxm_type] = name # Normally the oxm_length is the size of the field, but for experimenter # OXMs oxm_length also includes the 4-byte experimenter ID. oxm_length = n_bytes if oxm_class == 0xFFFF: oxm_length += 4 header = (oxm_vendor, oxm_class, int(oxm_type), oxm_length) if of_version: if oxm_class_type == "extension": fatal("%s: OXM extension can't have OpenFlow version" % name) if of_version not in VERSION: fatal("%s: unknown OpenFlow version %s" % (name, of_version)) of_version_nr = VERSION[of_version] if of_version_nr < VERSION["1.2"]: fatal("%s: claimed version %s predates OXM" % (name, of_version)) else: if oxm_class_type == "standard": fatal("%s: missing OpenFlow version number" % name) of_version_nr = 0 return (header, name, of_version_nr, ovs_version) def parse_field(mff, comment): f = {"mff": mff} # First line of comment is the field name. m = re.match( r'"([^"]+)"(?:\s+\(aka "([^"]+)"\))?(?:\s+\(.*\))?\.', comment[0] ) if not m: fatal("%s lacks field name" % mff) f["name"], f["extra_name"] = m.groups() # Find the last blank line the comment. The field definitions # start after that. blank = None for i in range(len(comment)): if not comment[i]: blank = i if not blank: fatal("%s: missing blank line in comment" % mff) d = {} for key in ( "Type", "Maskable", "Formatting", "Prerequisites", "Access", "Prefix lookup member", "OXM", "NXM", "OF1.0", "OF1.1", ): d[key] = None for fline in comment[blank + 1 :]: m = re.match(r"([^:]+):\s+(.*)\.$", fline) if not m: fatal( "%s: syntax error parsing key-value pair as part of %s" % (fline, mff) ) key, value = m.groups() if key not in d: fatal("%s: unknown key" % key) elif key == "Code point": d[key] += [value] elif d[key] is not None: fatal("%s: duplicate key" % key) d[key] = value for key, value in d.items(): if not value and key not in ( "OF1.0", "OF1.1", "Prefix lookup member", "Notes", ): fatal("%s: missing %s" % (mff, key)) m = re.match(r"([a-zA-Z0-9]+)(?: \(low ([0-9]+) bits\))?$", d["Type"]) if not m: fatal("%s: syntax error in type" % mff) type_ = m.group(1) if type_ not in TYPES: fatal("%s: unknown type %s" % (mff, d["Type"])) f["n_bytes"] = TYPES[type_][0] if m.group(2): f["n_bits"] = int(m.group(2)) if f["n_bits"] > f["n_bytes"] * 8: fatal( "%s: more bits (%d) than field size (%d)" % (mff, f["n_bits"], 8 * f["n_bytes"]) ) else: f["n_bits"] = 8 * f["n_bytes"] f["variable"] = TYPES[type_][1] if d["Maskable"] == "no": f["mask"] = "MFM_NONE" elif d["Maskable"] == "bitwise": f["mask"] = "MFM_FULLY" else: fatal("%s: unknown maskable %s" % (mff, d["Maskable"])) fmt = FORMATTING.get(d["Formatting"]) if not fmt: fatal("%s: unknown format %s" % (mff, d["Formatting"])) f["formatting"] = d["Formatting"] if f["n_bytes"] < fmt[1] or f["n_bytes"] > fmt[2]: fatal( "%s: %d-byte field can't be formatted as %s" % (mff, f["n_bytes"], d["Formatting"]) ) f["string"] = fmt[0] f["prereqs"] = d["Prerequisites"] if f["prereqs"] not in PREREQS: fatal("%s: unknown prerequisites %s" % (mff, d["Prerequisites"])) if d["Access"] == "read-only": f["writable"] = False elif d["Access"] == "read/write": f["writable"] = True else: fatal("%s: unknown access %s" % (mff, d["Access"])) f["OF1.0"] = d["OF1.0"] if d["OF1.0"] not in (None, "exact match", "CIDR mask"): fatal("%s: unknown OF1.0 match type %s" % (mff, d["OF1.0"])) f["OF1.1"] = d["OF1.1"] if d["OF1.1"] not in (None, "exact match", "bitwise mask"): fatal("%s: unknown OF1.1 match type %s" % (mff, d["OF1.1"])) f["OXM"] = parse_oxms(d["OXM"], "OXM", f["n_bytes"]) + parse_oxms( d["NXM"], "NXM", f["n_bytes"] ) f["prefix"] = d["Prefix lookup member"] return f def extract_ofp_fields(fn): global file_name global input_file global line_number global line file_name = fn input_file = open(file_name) line_number = 0 fields = [] while True: get_line() if re.match("enum.*mf_field_id", line): break while True: get_line() if ( line.startswith("/*") or line.startswith(" *") or line.startswith("#") or not line or line.isspace() ): continue elif re.match(r"}", line) or re.match(r"\s+MFF_N_IDS", line): break # Parse the comment preceding an MFF_ constant into 'comment', # one line to an array element. line = line.strip() if not line.startswith("/*"): fatal("unexpected syntax between fields") line = line[1:] comment = [] end = False while not end: line = line.strip() if line.startswith("*/"): get_line() break if not line.startswith("*"): fatal("unexpected syntax within field") line = line[1:] if line.startswith(" "): line = line[1:] if line.startswith(" ") and comment: continuation = True line = line.lstrip() else: continuation = False if line.endswith("*/"): line = line[:-2].rstrip() end = True else: end = False if continuation: comment[-1] += " " + line else: comment += [line] get_line() # Drop blank lines at each end of comment. while comment and not comment[0]: comment = comment[1:] while comment and not comment[-1]: comment = comment[:-1] # Parse the MFF_ constant(s). mffs = [] while True: m = re.match(r"\s+(MFF_[A-Z0-9_]+),?\s?$", line) if not m: break mffs += [m.group(1)] get_line() if not mffs: fatal("unexpected syntax looking for MFF_ constants") if len(mffs) > 1 or "" in comment[0]: for mff in mffs: # Extract trailing integer. m = re.match(".*[^0-9]([0-9]+)$", mff) if not m: fatal("%s lacks numeric suffix in register group" % mff) n = m.group(1) # Search-and-replace within the comment, # and drop lines that have for x != n. instance = [] for x in comment: y = x.replace("", n) if re.search("<[0-9]+>", y): if ("<%s>" % n) not in y: continue y = re.sub("<[0-9]+>", "", y) instance += [y.strip()] fields += [parse_field(mff, instance)] else: fields += [parse_field(mffs[0], comment)] continue input_file.close() if n_errors: sys.exit(1) return fields openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs_build_helpers/nroff.py000066400000000000000000000342601514270232600255020ustar00rootroot00000000000000# Copyright (c) 2010, 2011, 2012, 2015, 2016, 2017 Nicira, Inc. # # 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. import re import sys from ovs.db import error def text_to_nroff(s, font=r'\fR', escape_dot=True): def escape(match): c = match.group(0) # In Roman type, let -- in XML be \- in nroff. That gives us a way to # write minus signs, which is important in some places in manpages. # # Bold in nroff usually represents literal text, where there's no # distinction between hyphens and minus sign. The convention in nroff # appears to be to use a minus sign in such cases, so we follow that # convention. # # Finally, we always output - as a minus sign when it is followed by a # digit. if c.startswith('-'): if c == '--' and font == r'\fR': return r'\-' if c != '-' or font in (r'\fB', r'\fL'): return c.replace('-', r'\-') else: return '-' if c == '\\': return r'\e' elif c == '"': return r'\(dq' elif c == "'": return r'\(cq' elif c == ".": if escape_dot: # groff(7) says that . can be escaped by \. but in practice # groff still gives an error with \. at the beginning of a # line. return r'\[char46]' else: return '.' else: raise error.Error("bad escape") # Escape - \ " ' . as needed by nroff. s = re.sub('(-[0-9]|--|[-"\'\\\\.])', escape, s) return s def escape_nroff_literal(s, font=r'\fB'): return font + r'%s\fR' % text_to_nroff(s, font) def inline_xml_to_nroff(node, font, to_upper=False, newline='\n'): if node.nodeType == node.TEXT_NODE: if to_upper: s = text_to_nroff(node.data.upper(), font) else: s = text_to_nroff(node.data, font) return s.replace('\n', newline) elif node.nodeType == node.ELEMENT_NODE: if node.tagName in ['code', 'em', 'option', 'env', 'b']: s = r'\fB' for child in node.childNodes: s += inline_xml_to_nroff(child, r'\fB', to_upper, newline) return s + font elif node.tagName == 'ref': if node.hasAttribute('column'): s = node.attributes['column'].nodeValue if node.hasAttribute('key'): s += ':' + node.attributes['key'].nodeValue elif node.hasAttribute('table'): s = node.attributes['table'].nodeValue elif node.hasAttribute('group'): s = node.attributes['group'].nodeValue elif node.hasAttribute('db'): s = node.attributes['db'].nodeValue elif node.hasAttribute('field'): s = node.attributes['field'].nodeValue elif node.hasAttribute('section'): s = node.attributes['section'].nodeValue else: raise error.Error("'ref' lacks required attributes: %s" % list(node.attributes.keys())) return r'\fB' + re.sub(r'\s+', ' ', s) + font elif node.tagName in ['var', 'dfn', 'i', 'cite']: s = r'\fI' for child in node.childNodes: s += inline_xml_to_nroff(child, r'\fI', to_upper, newline) return s + font elif node.tagName in ['literal']: s = r'\fL' for child in node.childNodes: s += inline_xml_to_nroff(child, r'\fL') return s + font elif node.tagName == 'url': return ('\n.URL "' + text_to_nroff(node.attributes['href'].nodeValue, escape_dot=False) + '"\n') else: raise error.Error("element <%s> unknown or invalid here" % node.tagName) elif node.nodeType == node.COMMENT_NODE: return '' else: raise error.Error("unknown node %s in inline xml" % node) def pre_to_nroff(nodes, para, font): # This puts 'font' at the beginning of each line so that leading and # trailing whitespace stripping later doesn't removed leading spaces # from preformatted text. s = para + '\n.nf\n' + font for node in nodes: s += inline_xml_to_nroff(node, font, False, '\n.br\n' + font) + '\\fR' s += '\n.fi\n' return s def tbl_to_nroff(nodes, para): s = para + '\n.TS\n' for node in nodes: if node.nodeType != node.TEXT_NODE: fatal(" element may only have text children") s += node.data + '\n' s += '.TE\n' return s def fatal(msg): sys.stderr.write('%s\n' % msg) sys.exit(1) def put_text(text, x, y, s): x = int(x) y = int(y) extend = x + len(s) - len(text[y]) if extend > 0: text[y] += ' ' * extend text[y] = text[y][:x] + s + text[y][x + len(s):] def put_centered(text, x, width, y, s): put_text(text, x + (width - len(s)) / 2, y, s) def diagram_header_to_nroff(header_node, text, x): # Parse header. header_fields = [] i = 0 for node in header_node.childNodes: if node.nodeType == node.ELEMENT_NODE and node.tagName == 'bits': name = node.attributes['name'].nodeValue width = node.attributes['width'].nodeValue above = node.getAttribute('above') below = node.getAttribute('below') fill = node.getAttribute('fill') header_fields += [{"name": name, "tag": "B%d" % i, "width": width, "above": above, "below": below, "fill": fill}] i += 1 elif node.nodeType == node.COMMENT_NODE: pass elif node.nodeType == node.TEXT_NODE and node.data.isspace(): pass else: fatal("unknown node %s in diagram
element" % node) # Format pic version. pic_s = "" for f in header_fields: name = f['name'].replace('...', '. . .') pic_s += " %s: box \"%s\" width %s" % (f['tag'], name, f['width']) if f['fill'] == 'yes': pic_s += " fill" pic_s += '\n' for f in header_fields: pic_s += " \"%s\" at %s.n above\n" % (f['above'], f['tag']) pic_s += " \"%s\" at %s.s below\n" % (f['below'], f['tag']) name = header_node.getAttribute('name') if name == "": visible = " invis" else: visible = "" pic_s += "line <->%s \"%s\" above " % (visible, name) pic_s += "from %s.nw + (0,textht) " % header_fields[0]['tag'] pic_s += "to %s.ne + (0,textht)\n" % header_fields[-1]['tag'] # Format text version. header_width = 1 for f in header_fields: field_width = max(len(f['above']), len(f['below']), len(f['name'])) f['width'] = field_width header_width += field_width + 1 min_header_width = 2 + len(name) while min_header_width > header_width: for f in header_fields: f['width'] += 1 header_width += 1 if header_width >= min_header_width: break if name != "": put_centered(text, x, header_width, 0, name) if header_width >= 4: arrow = '<' + '-' * (header_width - 4) + '>' put_text(text, x + 1, 1, arrow) for f in header_fields: box1 = '+' + '-' * f['width'] + '+' box2 = '|' + ' ' * f['width'] + '|' put_text(text, x, 3, box1) put_text(text, x, 4, box2) put_text(text, x, 5, box1) put_centered(text, x + 1, f['width'], 2, f['above']) put_centered(text, x + 1, f['width'], 4, f['name']) put_centered(text, x + 1, f['width'], 6, f['below']) x += f['width'] + 1 return pic_s, x + 1 def diagram_to_nroff(nodes, para): pic_s = '' text = [''] * 7 x = 0 move = False for node in nodes: if node.nodeType == node.ELEMENT_NODE and node.tagName == 'header': if move: pic_s += "move .1\n" x += 1 elif x > 0: x -= 1 pic_header, x = diagram_header_to_nroff(node, text, x) pic_s += "[\n" + pic_header + "]\n" move = True elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'nospace': move = False elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'dots': pic_s += "move .1\n" pic_s += '". . ." ljust\n' put_text(text, x, 4, " ... ") x += 5 elif node.nodeType == node.COMMENT_NODE: pass elif node.nodeType == node.TEXT_NODE and node.data.isspace(): pass else: fatal("unknown node %s in diagram
element" % node) text_s = '.br\n'.join(["\\fL%s\n" % s for s in text if s != ""]) return para + """ .\\" check if in troff mode (TTY) .if t \\{ .PS boxht = .2 textht = 1/6 fillval = .2 """ + pic_s + """\ .PE \\} .\\" check if in nroff mode: .if n \\{ .nf """ + text_s + """\ .fi \\}""" def flatten_header(s): s = s.strip() return re.sub(r'\s+', ' ', s) def block_xml_to_nroff(nodes, para='.PP'): HEADER_TAGS = ('h1', 'h2', 'h3', 'h4') s = '' prev = '' for node in nodes: if node.nodeType == node.TEXT_NODE: if s == '' and para != '.IP': s = para + '\n' text = re.sub(r'\s+', ' ', node.data) if s.endswith(' '): text = text.lstrip() s += text_to_nroff(text) s = s.lstrip() elif node.nodeType == node.ELEMENT_NODE: if node.tagName in ['ul', 'ol']: if s != "": s += "\n" s += ".RS\n" i = 0 for li_node in node.childNodes: if (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'li'): i += 1 if node.tagName == 'ul': s += ".IP \\(bu\n" else: s += ".IP %d. .4in\n" % i s += block_xml_to_nroff(li_node.childNodes, ".IP") elif li_node.nodeType == node.COMMENT_NODE: pass elif (li_node.nodeType != node.TEXT_NODE or not li_node.data.isspace()): raise error.Error("<%s> element may only have " "
  • children" % node.tagName) s += ".RE\n" elif node.tagName == 'dl': indent = True if prev in HEADER_TAGS: indent = False if s != "": s += "\n" if indent: s += ".RS\n" prev = "dd" for li_node in node.childNodes: if (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'dt'): if prev == 'dd': s += '.TP\n' else: s += '.TQ .5in\n' prev = 'dt' elif (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'dd'): if prev == 'dd': s += '.IP\n' prev = 'dd' elif li_node.nodeType == node.COMMENT_NODE: continue elif (li_node.nodeType != node.TEXT_NODE or not li_node.data.isspace()): raise error.Error("
    element may only have " "
    and
    children") s += block_xml_to_nroff(li_node.childNodes, ".IP") if indent: s += ".RE\n" elif node.tagName == 'p': if s != "": if not s.endswith("\n"): s += "\n" s += para + "\n" s += block_xml_to_nroff(node.childNodes, para) elif node.tagName in HEADER_TAGS: if s != "": if not s.endswith("\n"): s += "\n" nroffTag, font = {'h1': ('SH', r'\fR'), 'h2': ('SS', r'\fB'), 'h3': ('ST', r'\fI'), 'h4': ('SU', r'\fI')}[node.tagName] to_upper = node.tagName == 'h1' s += ".%s \"" % nroffTag s += flatten_header(''.join([ inline_xml_to_nroff(child_node, font, to_upper) for child_node in node.childNodes])) s += "\"\n" elif node.tagName == 'pre': fixed = node.getAttribute('fixed') if fixed == 'yes': font = r'\fL' else: font = r'\fB' s += pre_to_nroff(node.childNodes, para, font) elif node.tagName == 'tbl': s += tbl_to_nroff(node.childNodes, para) elif node.tagName == 'diagram': s += diagram_to_nroff(node.childNodes, para) else: s += inline_xml_to_nroff(node, r'\fR') prev = node.tagName elif node.nodeType == node.COMMENT_NODE: pass else: raise error.Error("unknown node %s in block xml" % node) if s != "" and not s.endswith('\n'): s += '\n' return s openvswitch-3.7.0~git20260211.8c6ebf8/python/ovs_build_helpers/soutil.py000077500000000000000000000026701514270232600257120ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2008, 2017, 2020 Nicira, Inc. # # 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. import getopt import os import re import sys def parse_include_dirs(): include_dirs = [] options, args = getopt.gnu_getopt(sys.argv[1:], 'I:', ['include=']) for key, value in options: if key in ['-I', '--include']: include_dirs.append(value) else: assert False include_dirs.append('.') return include_dirs, args def find_file(include_dirs, name): for dir in include_dirs: file = "%s/%s" % (dir, name) try: os.stat(file) return file except OSError: pass sys.stderr.write("%s not found in: %s\n" % (name, ' '.join(include_dirs))) return None so_re = re.compile(r'^\.so (\S+)$') def extract_include_directive(line): m = so_re.match(line) if m: return m.group(1) else: return None openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/000077500000000000000000000000001514270232600220105ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/__init__.py000066400000000000000000000000461514270232600241210ustar00rootroot00000000000000# This file intentionally left blank. openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/args.py000066400000000000000000000257131514270232600233260ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. """ ovsargs provide argument parsing for ovs-test utility """ import argparse import re import socket import sys CONTROL_PORT = 15531 DATA_PORT = 15532 def ip_address(string): """Verifies if string is a valid IP address""" try: socket.inet_aton(string) except socket.error: raise argparse.ArgumentTypeError("Not a valid IPv4 address") return string def ip_optional_mask(string): """ Verifies if string contains a valid IP address and an optional mask in CIDR notation. """ token = string.split("/") if len(token) > 2: raise argparse.ArgumentTypeError("IP address and netmask must be " "separated by a single slash") elif len(token) == 2: try: mask = int(token[1]) except ValueError: raise argparse.ArgumentTypeError("Netmask is not a valid integer") if mask < 0 or mask > 31: raise argparse.ArgumentTypeError("Netmask must be in range 0..31") ip_address(token[0]) return string def port(string): """Convert a string into a TCP/UDP Port (integer)""" try: port_number = int(string) if port_number < 1 or port_number > 65535: raise argparse.ArgumentTypeError("Port is out of range") except ValueError: raise argparse.ArgumentTypeError("Port is not an integer") return port_number def ip_optional_port(string, default_port, ip_callback): """Convert a string into IP and Port pair. If port was absent then use default_port as the port. The third argument is a callback that verifies whether IP address is given in correct format.""" value = string.split(':') if len(value) == 1: return (ip_callback(value[0]), default_port) elif len(value) == 2: return (ip_callback(value[0]), port(value[1])) else: raise argparse.ArgumentTypeError("IP address from the optional Port " "must be colon-separated") def ip_optional_port_port(string, default_port1, default_port2, ip_callback): """Convert a string into IP, Port1, Port2 tuple. If any of ports were missing, then default ports will be used. The fourth argument is a callback that verifies whether IP address is given in the expected format.""" value = string.split(':') if len(value) == 1: return (ip_callback(value[0]), default_port1, default_port2) elif len(value) == 2: return (ip_callback(value[0]), port(value[1]), default_port2) elif len(value) == 3: return (ip_callback(value[0]), port(value[1]), port(value[2])) else: raise argparse.ArgumentTypeError("Expected IP address and at most " "two colon-separated ports") def vlan_tag(string): """ This function verifies whether given string is a correct VLAN tag. """ try: value = int(string) except ValueError: raise argparse.ArgumentTypeError("VLAN tag is not a valid integer") if value < 1 or value > 4094: raise argparse.ArgumentTypeError("Not a valid VLAN tag. " "VLAN tag should be in the " "range 1..4094.") return string def server_endpoint(string): """Converts a string OuterIP[:OuterPort],InnerIP[/Mask][:InnerPort] into a 4-tuple, where: 1. First element is OuterIP 2. Second element is OuterPort (if omitted will use default value 15531) 3 Third element is InnerIP with optional mask 4. Fourth element is InnerPort (if omitted will use default value 15532) """ value = string.split(',') if len(value) == 2: ret1 = ip_optional_port(value[0], CONTROL_PORT, ip_address) ret2 = ip_optional_port(value[1], DATA_PORT, ip_optional_mask) return (ret1[0], ret1[1], ret2[0], ret2[1]) else: raise argparse.ArgumentTypeError("OuterIP:OuterPort and InnerIP/Mask:" "InnerPort must be comma separated") class UniqueServerAction(argparse.Action): """ This custom action class will prevent user from entering multiple ovs-test servers with the same OuterIP. If there is an server with 127.0.0.1 outer IP address then it will be inserted in the front of the list. """ def __call__(self, parser, namespace, values, option_string=None): outer_ips = set() endpoints = [] for server in values: try: endpoint = server_endpoint(server) except argparse.ArgumentTypeError: raise argparse.ArgumentError(self, str(sys.exc_info()[1])) if endpoint[0] in outer_ips: raise argparse.ArgumentError(self, "Duplicate OuterIPs found") else: outer_ips.add(endpoint[0]) if endpoint[0] == "127.0.0.1": endpoints.insert(0, endpoint) else: endpoints.append(endpoint) setattr(namespace, self.dest, endpoints) def bandwidth(string): """Convert a string (given in bits/second with optional magnitude for units) into a long (bytes/second)""" if re.match("^[1-9][0-9]*[MK]?$", string) is None: raise argparse.ArgumentTypeError("Not a valid target bandwidth") bwidth = string.replace("M", "000000") bwidth = bwidth.replace("K", "000") return int(bwidth) / 8 # Convert from bits to bytes def tunnel_types(string): """ This function converts a string into a list that contains all tunnel types that user intended to test. """ return string.split(',') def l3_endpoint_client(string): """ This function parses command line argument string in remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[: ControlPort[:TestPort]] format. """ try: remote_ip, me, he = string.split(',') except ValueError: raise argparse.ArgumentTypeError("All 3 IP addresses must be comma " "separated.") r = (ip_address(remote_ip), ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask), ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address)) return r def l3_endpoint_server(string): """ This function parses a command line argument string in remoteIP,localInnerIP[/mask][:ControlPort] format. """ try: remote_ip, me = string.split(',') except ValueError: raise argparse.ArgumentTypeError("Both IP addresses must be comma " "separated.") return (ip_address(remote_ip), ip_optional_port(me, CONTROL_PORT, ip_optional_mask)) def ovs_initialize_args(): """ Initialize argument parsing for ovs-test utility. """ parser = argparse.ArgumentParser(description='Test connectivity ' 'between two Open vSwitches.') parser.add_argument('-v', '--version', action='version', version='ovs-test (Open vSwitch) @VERSION@') parser.add_argument("-b", "--bandwidth", action='store', dest="targetBandwidth", default="1M", type=bandwidth, help='Target bandwidth for UDP tests in bits/second. Use ' 'postfix M or K to alter unit magnitude.') parser.add_argument("-i", "--interval", action='store', dest="testInterval", default=5, type=int, help='Interval for how long to run each test in seconds.') parser.add_argument("-t", "--tunnel-modes", action='store', dest="tunnelModes", default=(), type=tunnel_types, help='Do L3 tests with the given tunnel modes.') parser.add_argument("-l", "--vlan-tag", action='store', dest="vlanTag", default=None, type=vlan_tag, help='Do VLAN tests and use the given VLAN tag.') parser.add_argument("-d", "--direct", action='store_true', dest="direct", default=None, help='Do direct tests between both ovs-test servers.') group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-s", "--server", action="store", dest="port", type=port, help='Run in server mode and wait for the client to ' 'connect to this port.') group.add_argument('-c', "--client", nargs=2, dest="servers", action=UniqueServerAction, metavar=("SERVER1", "SERVER2"), help='Run in client mode and do tests between these ' 'two ovs-test servers. Each server must be specified in ' 'following format - OuterIP:OuterPort,InnerIP[/mask] ' ':InnerPort. It is possible to start local instance of ' 'ovs-test server in the client mode by using 127.0.0.1 as ' 'OuterIP.') return parser.parse_args() def l3_initialize_args(): """ Initialize argument parsing for ovs-l3ping utility. """ parser = argparse.ArgumentParser(description='Test L3 tunnel ' 'connectivity between two Open vSwitch instances.') parser.add_argument('-v', '--version', action='version', version='ovs-l3ping (Open vSwitch) @VERSION@') parser.add_argument("-b", "--bandwidth", action='store', dest="targetBandwidth", default="1M", type=bandwidth, help='Target bandwidth for UDP tests in bits/second. Use ' 'postfix M or K to alter unit magnitude.') parser.add_argument("-i", "--interval", action='store', dest="testInterval", default=5, type=int, help='Interval for how long to run each test in seconds.') parser.add_argument("-t", "--tunnel-mode", action='store', dest="tunnelMode", required=True, help='Do L3 tests with this tunnel type.') group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-s", "--server", action="store", dest="server", metavar="TUNNELIP,SERVER", type=l3_endpoint_server, help='Run in server mode and wait for the client to ' 'connect.') group.add_argument('-c', "--client", action="store", dest="client", metavar="TUNNELIP,CLIENT,SERVER", type=l3_endpoint_client, help='Run in client mode and connect to the server.') return parser.parse_args() openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/rpcserver.py000066400000000000000000000270641514270232600244060ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. """ rpcserver is an XML RPC server that allows RPC client to initiate tests """ import sys import xmlrpc.client from twisted.internet import reactor from twisted.internet.error import CannotListenError from twisted.web import server from twisted.web import xmlrpc from . import tcp, udp, util, vswitch class TestArena(xmlrpc.XMLRPC): """ This class contains all the functions that ovs-test client will call remotely. The caller is responsible to use designated handleIds for designated methods (e.g. do not mix UDP and TCP handles). """ def __init__(self): xmlrpc.XMLRPC.__init__(self, allowNone=True) self.handle_id = 1 self.handle_map = {} self.bridges = set() self.pbridges = set() self.ports = set() self.request = None def __acquire_handle(self, value): """ Allocates new handle and assigns value object to it """ handle = self.handle_id self.handle_map[handle] = value self.handle_id += 1 return handle def __get_handle_resources(self, handle): """ Return resources that were assigned to handle """ return self.handle_map[handle] def __delete_handle(self, handle): """ Releases handle from handle_map """ del self.handle_map[handle] def cleanup(self): """ Delete all remaining bridges and ports if ovs-test client did not had a chance to remove them. It is necessary to call this function if ovs-test server is abruptly terminated when doing the tests. """ for port in self.ports: # Remove ports that were added to existing bridges vswitch.ovs_vsctl_del_port_from_bridge(port) for bridge in self.bridges: # Remove bridges that were added for L3 tests vswitch.ovs_vsctl_del_bridge(bridge) for pbridge in self.pbridges: # Remove bridges that were added for VLAN tests vswitch.ovs_vsctl_del_pbridge(pbridge[0], pbridge[1]) def render(self, request): """ This method overrides the original XMLRPC.render method so that it would be possible to get the XML RPC client IP address from the request object. """ self.request = request return xmlrpc.XMLRPC.render(self, request) def xmlrpc_get_my_address(self): """ Returns the RPC client's IP address. """ return self.request.getClientIP() def xmlrpc_get_my_address_from(self, his_ip, his_port): """ Returns the ovs-test server IP address that the other ovs-test server with the given ip will see. """ server1 = xmlrpc.client.Server("http://%s:%u/" % (his_ip, his_port)) return server1.get_my_address() def xmlrpc_create_udp_listener(self, port): """ Creates a UDP listener that will receive packets from UDP sender """ try: listener = udp.UdpListener() reactor.listenUDP(port, listener) handle_id = self.__acquire_handle(listener) except CannotListenError: return -1 return handle_id def xmlrpc_create_udp_sender(self, host, count, size, duration): """ Send UDP datagrams to UDP listener """ sender = udp.UdpSender(tuple(host), count, size, duration) reactor.listenUDP(0, sender) handle_id = self.__acquire_handle(sender) return handle_id def xmlrpc_get_udp_listener_results(self, handle): """ Returns number of datagrams that were received """ listener = self.__get_handle_resources(handle) return listener.getResults() def xmlrpc_get_udp_sender_results(self, handle): """ Returns number of datagrams that were sent """ sender = self.__get_handle_resources(handle) return sender.getResults() def xmlrpc_close_udp_listener(self, handle): """ Releases UdpListener and all its resources """ listener = self.__get_handle_resources(handle) listener.transport.stopListening() self.__delete_handle(handle) return 0 def xmlrpc_close_udp_sender(self, handle): """ Releases UdpSender and all its resources """ sender = self.__get_handle_resources(handle) sender.transport.stopListening() self.__delete_handle(handle) return 0 def xmlrpc_create_tcp_listener(self, port): """ Creates a TcpListener that will accept connection from TcpSender """ try: listener = tcp.TcpListenerFactory() port = reactor.listenTCP(port, listener) handle_id = self.__acquire_handle((listener, port)) return handle_id except CannotListenError: return -1 def xmlrpc_create_tcp_sender(self, his_ip, his_port, duration): """ Creates a TcpSender that will connect to TcpListener """ sender = tcp.TcpSenderFactory(duration) connector = reactor.connectTCP(his_ip, his_port, sender) handle_id = self.__acquire_handle((sender, connector)) return handle_id def xmlrpc_get_tcp_listener_results(self, handle): """ Returns number of bytes received """ (listener, _) = self.__get_handle_resources(handle) return listener.getResults() def xmlrpc_get_tcp_sender_results(self, handle): """ Returns number of bytes sent """ (sender, _) = self.__get_handle_resources(handle) return sender.getResults() def xmlrpc_close_tcp_listener(self, handle): """ Releases TcpListener and all its resources """ try: (_, port) = self.__get_handle_resources(handle) port.loseConnection() self.__delete_handle(handle) except KeyError: return -1 return 0 def xmlrpc_close_tcp_sender(self, handle): """ Releases TcpSender and all its resources """ try: (_, connector) = self.__get_handle_resources(handle) connector.disconnect() self.__delete_handle(handle) except KeyError: return -1 return 0 def xmlrpc_create_test_bridge(self, bridge, iface): """ This function creates a physical bridge from iface. It moves the IP configuration from the physical interface to the bridge. """ ret = vswitch.ovs_vsctl_add_bridge(bridge) if ret == 0: self.pbridges.add((bridge, iface)) util.interface_up(bridge) (ip_addr, mask) = util.interface_get_ip(iface) util.interface_assign_ip(bridge, ip_addr, mask) util.interface_up(bridge) util.move_routes(iface, bridge) util.interface_remove_ip(iface, ip_addr, mask) ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, iface) if ret == 0: self.ports.add(iface) else: util.interface_assign_ip(iface, ip_addr, mask) util.interface_up(iface) util.move_routes(bridge, iface) vswitch.ovs_vsctl_del_bridge(bridge) return ret def xmlrpc_del_test_bridge(self, bridge, iface): """ This function deletes the test bridge and moves its IP configuration back to the physical interface. """ ret = vswitch.ovs_vsctl_del_pbridge(bridge, iface) self.pbridges.discard((bridge, iface)) return ret def xmlrpc_get_iface_from_bridge(self, brname): """ Tries to figure out physical interface from bridge. """ return vswitch.ovs_get_physical_interface(brname) def xmlrpc_create_bridge(self, brname): """ Creates an OVS bridge. """ ret = vswitch.ovs_vsctl_add_bridge(brname) if ret == 0: self.bridges.add(brname) return ret def xmlrpc_del_bridge(self, brname): """ Deletes an OVS bridge. """ ret = vswitch.ovs_vsctl_del_bridge(brname) if ret == 0: self.bridges.discard(brname) return ret def xmlrpc_is_ovs_bridge(self, bridge): """ This function verifies whether given interface is an ovs bridge. """ return vswitch.ovs_vsctl_is_ovs_bridge(bridge) def xmlrpc_add_port_to_bridge(self, bridge, port): """ Adds a port to the OVS bridge. """ ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, port) if ret == 0: self.ports.add(port) return ret def xmlrpc_del_port_from_bridge(self, port): """ Removes a port from OVS bridge. """ ret = vswitch.ovs_vsctl_del_port_from_bridge(port) if ret == 0: self.ports.discard(port) return ret def xmlrpc_ovs_vsctl_set(self, table, record, column, key, value): """ This function allows to alter OVS database. """ return vswitch.ovs_vsctl_set(table, record, column, key, value) def xmlrpc_interface_up(self, iface): """ This function brings up given interface. """ return util.interface_up(iface) def xmlrpc_interface_assign_ip(self, iface, ip_address, mask): """ This function allows to assing ip address to the given interface. """ return util.interface_assign_ip(iface, ip_address, mask) def xmlrpc_interface_remove_ip(self, iface, ip_address, mask): """ This function allows to assing ip address to the given interface. """ return util.interface_remove_ip(iface, ip_address, mask) def xmlrpc_get_interface(self, address): """ Finds first interface that has given address """ return util.get_interface(address) def xmlrpc_get_interface_mtu(self, iface): """ Returns MTU of the given interface """ return util.get_interface_mtu(iface) def xmlrpc_uname(self): """ Return information about running kernel """ return util.uname() def xmlrpc_get_driver(self, iface): """ Returns driver version """ return util.get_driver(iface) def xmlrpc_get_interface_from_routing_decision(self, ip): """ Returns driver version """ return util.get_interface_from_routing_decision(ip) def start_rpc_server(port): """ This function creates a RPC server and adds it to the Twisted Reactor. """ rpc_server = TestArena() reactor.listenTCP(port, server.Site(rpc_server)) try: print("Starting RPC server\n") sys.stdout.flush() # If this server was started from ovs-test client then we must flush # STDOUT so that client would know that server is ready to accept # XML RPC connections. reactor.run() finally: rpc_server.cleanup() openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/tcp.py000066400000000000000000000070251514270232600231540ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. """ tcp module contains listener and sender classes for TCP protocol """ import time from twisted.internet import interfaces from twisted.internet.protocol import ClientFactory, Factory, Protocol from zope.interface.declarations import implementer class TcpListenerConnection(Protocol): """ This per-connection class is instantiated each time sender connects """ def __init__(self): self.stats = 0 def dataReceived(self, data): self.stats += len(data) def connectionLost(self, reason): self.factory.stats += self.stats class TcpListenerFactory(Factory): """ This per-listening socket class is used to instantiate TcpListenerConnections """ protocol = TcpListenerConnection def __init__(self): self.stats = 0 def getResults(self): """ returns the number of bytes received as string""" # XML RPC does not support 64bit int (http://bugs.python.org/issue2985) # so we have to convert the amount of bytes into a string return str(self.stats) @implementer(interfaces.IPushProducer) class Producer(object): """ This producer class generates infinite byte stream for a specified time duration """ def __init__(self, proto, duration): self.proto = proto self.start = time.time() self.produced = 0 self.paused = False self.data = "X" * 65535 self.duration = duration def pauseProducing(self): """This function is called whenever write() to socket would block""" self.paused = True def resumeProducing(self): """This function is called whenever socket becomes writable""" self.paused = False current = time.time() while (not self.paused) and (current < self.start + self.duration): self.proto.transport.write(self.data) self.produced += len(self.data) current = time.time() if current >= self.start + self.duration: self.proto.factory.stats += self.produced self.proto.transport.unregisterProducer() self.proto.transport.loseConnection() def stopProducing(self): pass class TcpSenderConnection(Protocol): """ TCP connection instance class that sends all traffic at full speed. """ def connectionMade(self): producer = Producer(self, self.factory.duration) self.transport.registerProducer(producer, True) producer.resumeProducing() def dataReceived(self, data): self.transport.loseConnection() class TcpSenderFactory(ClientFactory): """ This factory is responsible to instantiate TcpSenderConnection classes each time sender initiates connection """ protocol = TcpSenderConnection def __init__(self, duration): self.duration = duration self.stats = 0 def getResults(self): """Returns amount of bytes sent to the Listener (as a string)""" return str(self.stats) openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/tests.py000066400000000000000000000232161514270232600235300ustar00rootroot00000000000000# 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. import math import time import ovstest.util as util DEFAULT_TEST_BRIDGE = "ovstestbr0" DEFAULT_TEST_PORT = "ovstestport0" DEFAULT_TEST_TUN = "ovstestport1" NO_HANDLE = -1 def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes): """Schedule UDP tests between receiver and sender""" server1 = util.rpc_client(receiver[0], receiver[1]) server2 = util.rpc_client(sender[0], sender[1]) udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}' print("UDP test from %s:%u to %s:%u with target bandwidth %s" % (sender[0], sender[1], receiver[0], receiver[1], util.bandwidth_to_string(tbwidth))) print(udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams", "Datagram Loss", "Bandwidth")) for size in port_sizes: listen_handle = NO_HANDLE send_handle = NO_HANDLE try: packetcnt = (tbwidth * duration) / size listen_handle = server1.create_udp_listener(receiver[3]) if listen_handle == NO_HANDLE: print("Server could not open UDP listening socket on port" " %u. Try to restart the server.\n" % receiver[3]) return send_handle = server2.create_udp_sender( (util.ip_from_cidr(receiver[2]), receiver[3]), packetcnt, size, duration) # Using sleep here because there is no other synchronization # source that would notify us when all sent packets were received time.sleep(duration + 1) rcv_packets = server1.get_udp_listener_results(listen_handle) snt_packets = server2.get_udp_sender_results(send_handle) loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) / snt_packets) / 100 bwidth = (rcv_packets * size) / duration print(udpformat.format(size, snt_packets, rcv_packets, '%.2f%%' % loss, util.bandwidth_to_string(bwidth))) finally: if listen_handle != NO_HANDLE: server1.close_udp_listener(listen_handle) if send_handle != NO_HANDLE: server2.close_udp_sender(send_handle) print("\n") def do_tcp_tests(receiver, sender, duration): """Schedule TCP tests between receiver and sender""" server1 = util.rpc_client(receiver[0], receiver[1]) server2 = util.rpc_client(sender[0], sender[1]) tcpformat = '{0:>15} {1:>15} {2:>15}' print("TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1], receiver[0], receiver[1])) print(tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")) listen_handle = NO_HANDLE send_handle = NO_HANDLE try: listen_handle = server1.create_tcp_listener(receiver[3]) if listen_handle == NO_HANDLE: print("Server was unable to open TCP listening socket on port" " %u. Try to restart the server.\n" % receiver[3]) return send_handle = server2.create_tcp_sender(util.ip_from_cidr(receiver[2]), receiver[3], duration) time.sleep(duration + 1) rcv_bytes = int(server1.get_tcp_listener_results(listen_handle)) snt_bytes = int(server2.get_tcp_sender_results(send_handle)) bwidth = rcv_bytes / duration print(tcpformat.format(snt_bytes, rcv_bytes, util.bandwidth_to_string(bwidth))) finally: if listen_handle != NO_HANDLE: server1.close_tcp_listener(listen_handle) if send_handle != NO_HANDLE: server2.close_tcp_sender(send_handle) print("\n") def do_l3_tests(node1, node2, bandwidth, duration, ps, type): """ Do L3 tunneling tests. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. """ server1 = util.rpc_client(node1[0], node1[1]) server2 = util.rpc_client(node2[0], node2[1]) servers_with_bridges = [] try: server1.create_bridge(DEFAULT_TEST_BRIDGE) servers_with_bridges.append(server1) server2.create_bridge(DEFAULT_TEST_BRIDGE) servers_with_bridges.append(server2) server1.interface_up(DEFAULT_TEST_BRIDGE) server2.interface_up(DEFAULT_TEST_BRIDGE) server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None) server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None) server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN) server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type", None, type) server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type", None, type) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options", "remote_ip", node2[0]) server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options", "remote_ip", node1[0]) do_udp_tests(node1, node2, bandwidth, duration, ps) do_udp_tests(node2, node1, bandwidth, duration, ps) do_tcp_tests(node1, node2, duration) do_tcp_tests(node2, node1, duration) finally: for server in servers_with_bridges: server.del_bridge(DEFAULT_TEST_BRIDGE) def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag): """ Do VLAN tests between node1 and node2. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. """ server1 = util.rpc_client(node1[0], node1[1]) server2 = util.rpc_client(node2[0], node2[1]) br_name1 = None br_name2 = None servers_with_test_ports = [] try: interface_node1 = server1.get_interface(node1[0]) interface_node2 = server2.get_interface(node2[0]) if server1.is_ovs_bridge(interface_node1): br_name1 = interface_node1 else: br_name1 = DEFAULT_TEST_BRIDGE server1.create_test_bridge(br_name1, interface_node1) if server2.is_ovs_bridge(interface_node2): br_name2 = interface_node2 else: br_name2 = DEFAULT_TEST_BRIDGE server2.create_test_bridge(br_name2, interface_node2) server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT) servers_with_test_ports.append(server1) server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT) servers_with_test_ports.append(server2) server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag) server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, "internal") server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, "internal") server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None) server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None) server1.interface_up(DEFAULT_TEST_PORT) server2.interface_up(DEFAULT_TEST_PORT) do_udp_tests(node1, node2, bandwidth, duration, ps) do_udp_tests(node2, node1, bandwidth, duration, ps) do_tcp_tests(node1, node2, duration) do_tcp_tests(node2, node1, duration) finally: for server in servers_with_test_ports: server.del_port_from_bridge(DEFAULT_TEST_PORT) if br_name1 == DEFAULT_TEST_BRIDGE: server1.del_test_bridge(br_name1, interface_node1) if br_name2 == DEFAULT_TEST_BRIDGE: server2.del_test_bridge(br_name2, interface_node2) def do_direct_tests(node1, node2, bandwidth, duration, ps): """ Do tests between outer IPs without involving Open vSwitch. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. Direct tests will use physical interface IP as the test IP address. """ n1 = (node1[0], node1[1], node1[0], node1[3]) n2 = (node2[0], node2[1], node2[0], node2[3]) do_udp_tests(n1, n2, bandwidth, duration, ps) do_udp_tests(n2, n1, bandwidth, duration, ps) do_tcp_tests(n1, n2, duration) do_tcp_tests(n2, n1, duration) def configure_l3(conf, tunnel_mode): """ This function creates a temporary test bridge and adds an L3 tunnel. """ s = util.start_local_server(conf[1][1]) server = util.rpc_client("127.0.0.1", conf[1][1]) server.create_bridge(DEFAULT_TEST_BRIDGE) server.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_PORT) server.interface_up(DEFAULT_TEST_BRIDGE) server.interface_assign_ip(DEFAULT_TEST_BRIDGE, conf[1][0], None) server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, tunnel_mode) server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "options", "remote_ip", conf[0]) return s openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/udp.py000066400000000000000000000050741514270232600231600ustar00rootroot00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # 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. """ ovsudp contains listener and sender classes for UDP protocol """ import array import struct import time from twisted.internet.protocol import DatagramProtocol from twisted.internet.task import LoopingCall class UdpListener(DatagramProtocol): """ Class that will listen for incoming UDP packets """ def __init__(self): self.stats = [] def datagramReceived(self, data, _1_2): """This function is called each time datagram is received""" try: self.stats.append(struct.unpack_from("Q", data, 0)) except struct.error: pass # ignore packets that are less than 8 bytes of size def getResults(self): """Returns number of packets that were actually received""" return len(self.stats) class UdpSender(DatagramProtocol): """ Class that will send UDP packets to UDP Listener """ def __init__(self, host, count, size, duration): # LoopingCall does not know whether UDP socket is actually writable self.looper = None self.host = host self.count = count self.duration = duration self.start = time.time() self.sent = 0 self.data = array.array('c', 'X' * size) def startProtocol(self): self.looper = LoopingCall(self.sendData) period = self.duration / float(self.count) self.looper.start(period, now=False) def stopProtocol(self): if (self.looper is not None): self.looper.stop() self.looper = None def datagramReceived(self, data, host_port): pass def sendData(self): """This function is called from LoopingCall""" if self.start + self.duration < time.time(): self.looper.stop() self.looper = None self.sent += 1 struct.pack_into('Q', self.data, 0, self.sent) self.transport.write(self.data, self.host) def getResults(self): """Returns number of packets that were sent""" return self.sent openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/util.py000066400000000000000000000156511514270232600233470ustar00rootroot00000000000000# Copyright (c) 2011, 2012, 2017 Nicira, Inc. # # 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. """ util module contains some helper function """ import array import fcntl import os import re import select import signal import socket import struct import subprocess import xmlrpc.client def str_ip(ip_address): """ Converts an IP address from binary format to a string. """ (x1, x2, x3, x4) = struct.unpack("BBBB", ip_address) return ("%u.%u.%u.%u") % (x1, x2, x3, x4) def get_interface_mtu(iface): """ Returns MTU of the given interface. """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) indata = iface + ('\0' * (32 - len(iface))) try: outdata = fcntl.ioctl(s.fileno(), 0x8921, indata) # socket.SIOCGIFMTU mtu = struct.unpack("16si12x", outdata)[1] except: return 0 return mtu def get_interface(address): """ Finds first interface that has given address """ bytes = 256 * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * bytes) outbytes = struct.unpack('iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', bytes, names.buffer_info()[0]) ))[0] namestr = names.tostring() for i in range(0, outbytes, 40): name = namestr[i:i + 16].split('\0', 1)[0] if address == str_ip(namestr[i + 20:i + 24]): return name return None # did not find interface we were looking for def uname(): os_info = os.uname() return os_info[2] # return only the kernel version number def start_process(args): try: p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() return (p.returncode, out, err) except OSError: return (-1, None, None) def get_driver(iface): ret, out, _err = start_process(["ethtool", "-i", iface]) if ret == 0: lines = out.splitlines() driver = "%s(%s)" % (lines[0], lines[1]) # driver name + version else: driver = None return driver def interface_up(iface): """ This function brings given iface up. """ ret, _out, _err = start_process(["ip", "link", "set", iface, "up"]) return ret def interface_assign_ip(iface, ip_addr, mask): """ This function adds an IP address to an interface. If mask is None then a mask will be selected automatically. In case of success this function returns 0. """ interface_ip_op(iface, ip_addr, mask, "add") def interface_remove_ip(iface, ip_addr, mask): """ This function removes an IP address from an interface. If mask is None then a mask will be selected automatically. In case of success this function returns 0. """ interface_ip_op(iface, ip_addr, mask, "del") def interface_ip_op(iface, ip_addr, mask, op): if mask is not None: arg = "%s/%s" % (ip_addr, mask) elif '/' in ip_addr: arg = ip_addr else: (x1, x2, x3, x4) = struct.unpack("BBBB", socket.inet_aton(ip_addr)) if x1 < 128: arg = "%s/8" % ip_addr elif x1 < 192: arg = "%s/16" % ip_addr else: arg = "%s/24" % ip_addr ret, _out, _err = start_process(["ip", "addr", op, arg, "dev", iface]) return ret def interface_get_ip(iface): """ This function returns tuple - ip and mask that was assigned to the interface. """ args = ["ip", "addr", "show", iface] ret, out, _err = start_process(args) if ret == 0: ip = re.search(r'inet (\S+)/(\S+)', out) if ip is not None: return (ip.group(1), ip.group(2)) else: return ret def move_routes(iface1, iface2): """ This function moves routes from iface1 to iface2. """ args = ["ip", "route", "show", "dev", iface1] ret, out, _err = start_process(args) if ret == 0: for route in out.splitlines(): args = ["ip", "route", "replace", "dev", iface2] + route.split() start_process(args) def get_interface_from_routing_decision(ip): """ This function returns the interface through which the given ip address is reachable. """ args = ["ip", "route", "get", ip] ret, out, _err = start_process(args) if ret == 0: iface = re.search(r'dev (\S+)', out) if iface: return iface.group(1) return None def rpc_client(ip, port): return xmlrpc.client.Server("http://%s:%u/" % (ip, port), allow_none=True) def sigint_intercept(): """ Intercept SIGINT from child (the local ovs-test server process). """ signal.signal(signal.SIGINT, signal.SIG_IGN) def start_local_server(port): """ This function spawns an ovs-test server that listens on specified port and blocks till the spawned ovs-test server is ready to accept XML RPC connections. """ p = subprocess.Popen(["ovs-test", "-s", str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=sigint_intercept) fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK) while p.poll() is None: fd = select.select([p.stdout.fileno()], [], [])[0] if fd: out = p.stdout.readline() if out.startswith("Starting RPC server"): break if p.poll() is not None: raise RuntimeError("Couldn't start local instance of ovs-test server") return p def get_datagram_sizes(mtu1, mtu2): """ This function calculates all the "interesting" datagram sizes so that we test both - receive and send side with different packets sizes. """ s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1]) s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2]) return sorted(s1.union(s2)) def ip_from_cidr(string): """ This function removes the netmask (if present) from the given string and returns the IP address. """ token = string.split("/") return token[0] def bandwidth_to_string(bwidth): """Convert bandwidth from long to string and add units.""" bwidth = bwidth * 8 # Convert back to bits/second if bwidth >= 10000000: return str(int(bwidth / 1000000)) + "Mbps" elif bwidth > 10000: return str(int(bwidth / 1000)) + "Kbps" else: return str(int(bwidth)) + "bps" openvswitch-3.7.0~git20260211.8c6ebf8/python/ovstest/vswitch.py000066400000000000000000000065131514270232600240560ustar00rootroot00000000000000# Copyright (c) 2012 Nicira, Inc. # # 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. """ vswitch module allows its callers to interact with OVS DB. """ from . import util def ovs_vsctl_add_bridge(bridge): """ This function creates an OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "add-br", bridge]) return ret def ovs_vsctl_del_bridge(bridge): """ This function deletes the OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "del-br", bridge]) return ret def ovs_vsctl_del_pbridge(bridge, iface): """ This function deletes the OVS bridge and assigns the bridge IP address back to the iface. """ (ip_addr, mask) = util.interface_get_ip(bridge) util.interface_assign_ip(iface, ip_addr, mask) util.interface_up(iface) util.move_routes(bridge, iface) return ovs_vsctl_del_bridge(bridge) def ovs_vsctl_is_ovs_bridge(bridge): """ This function verifies whether given port is an OVS bridge. If it is an OVS bridge then it will return True. """ ret, _out, _err = util.start_process(["ovs-vsctl", "br-exists", bridge]) return ret == 0 def ovs_vsctl_add_port_to_bridge(bridge, iface): """ This function adds given interface to the bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "add-port", bridge, iface]) return ret def ovs_vsctl_del_port_from_bridge(port): """ This function removes given port from a OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "del-port", port]) return ret def ovs_vsctl_set(table, record, column, key, value): """ This function allows to alter the OVS database. If column is a map, then caller should also set the key, otherwise the key should be left as an empty string. """ if key is None: index = column else: index = "%s:%s" % (column, key) index_value = "%s=%s" % (index, value) ret, _out, _err = util.start_process(["ovs-vsctl", "set", table, record, index_value]) return ret def ovs_get_physical_interface(bridge): """ This function tries to figure out which is the physical interface that belongs to the bridge. If there are multiple physical interfaces assigned to this bridge then it will return the first match. """ ret, out, _err = util.start_process(["ovs-vsctl", "list-ifaces", bridge]) if ret == 0: ifaces = out.splitlines() for iface in ifaces: ret, out, _err = util.start_process(["ovs-vsctl", "get", "Interface", iface, "type"]) if ret == 0: if ('""' in out) or ('system' in out): return iface # this should be the physical interface return None openvswitch-3.7.0~git20260211.8c6ebf8/python/setup.py.template000066400000000000000000000110161514270232600236240ustar00rootroot00000000000000# 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. import os import os.path import sys from setuptools.command.build_ext import build_ext try: from setuptools.errors import CCompilerError, ExecError, PlatformError except ImportError: # Needed for setuptools < 59.0 from distutils.errors import CCompilerError from distutils.errors import DistutilsExecError as ExecError from distutils.errors import DistutilsPlatformError as PlatformError import setuptools VERSION = "@VERSION@" README_PATH = os.path.abspath( os.path.join(os.path.abspath(__file__), os.pardir, 'README.rst') ) for x in ("version.py", "dirs.py"): if not os.path.exists(f"ovs/{x}"): print(f"Ensure {x} is created by running make python/ovs/{x}", file=sys.stderr) sys.exit(-1) with open(README_PATH) as fh: long_description = fh.read() ext_errors = (CCompilerError, ExecError, PlatformError) if sys.platform == 'win32': ext_errors += (IOError, ValueError) class BuildFailed(Exception): pass class try_build_ext(build_ext): # This class allows C extension building to fail # NOTE: build_ext is not a new-style class def run(self): try: build_ext.run(self) except PlatformError: raise BuildFailed() def build_extension(self, ext): try: build_ext.build_extension(self, ext) except ext_errors: raise BuildFailed() # Allow caller of setup.py to pass in libopenvswitch.a as an object for linking # plus all the dependencies through the use of 'extra_cflags' and 'extra_libs' # environment variables when not building a shared openvswitch library. if os.environ.get('enable_shared', '') == 'no': libraries = [] else: libraries = ['openvswitch'] extra_cflags = os.environ.get('extra_cflags', '').split() extra_libs = os.environ.get('extra_libs', '').split() flow_extras_require = ['netaddr', 'pyparsing'] setup_args = dict( name='ovs', description='Open vSwitch library', long_description=long_description, long_description_content_type='text/x-rst', version=VERSION, url='http://www.openvswitch.org/', author='Open vSwitch', author_email='dev@openvswitch.org', packages=['ovs', 'ovs.compat', 'ovs.compat.sortedcontainers', 'ovs.db', 'ovs.flow', 'ovs.flowviz', 'ovs.flowviz.odp', 'ovs.flowviz.ofp', 'ovs.unixctl'], keywords=['openvswitch', 'ovs', 'OVSDB'], license='Apache 2.0', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Topic :: Database :: Front-Ends', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Networking', 'License-Expression :: Apache 2.0', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', ], ext_modules=[setuptools.Extension("ovs._json", sources=["ovs/_json.c"], libraries=libraries, extra_compile_args=extra_cflags, extra_link_args=extra_libs)], cmdclass={'build_ext': try_build_ext}, install_requires=['sortedcontainers'], extras_require={':sys_platform == "win32"': ['pywin32 >= 1.0'], 'dns': ['unbound'], 'flow': flow_extras_require, 'flowviz': [*flow_extras_require, 'click', 'rich', 'graphviz'], }, scripts=["ovs/flowviz/ovs-flowviz"], package_data={'ovs.flowviz': ['ovs-flowviz.conf']}, ) try: setuptools.setup(**setup_args) except BuildFailed: BUILD_EXT_WARNING = ("WARNING: The C extension could not be compiled, " "speedups are not enabled.") print("*" * 75) print(BUILD_EXT_WARNING) print("Failure information, if any, is above.") print("Retrying the build without the C extension.") print("*" * 75) del setup_args['cmdclass'] del setup_args['ext_modules'] setuptools.setup(**setup_args) openvswitch-3.7.0~git20260211.8c6ebf8/python/test_requirements.txt000066400000000000000000000000711514270232600246220ustar00rootroot00000000000000netaddr packaging pyftpdlib pyparsing pytest scapy tftpy openvswitch-3.7.0~git20260211.8c6ebf8/rhel/000077500000000000000000000000001514270232600177125ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/rhel/.gitignore000066400000000000000000000001251514270232600217000ustar00rootroot00000000000000openvswitch.spec openvswitch-fedora.spec usr_lib_systemd_system_ovs-vswitchd.service openvswitch-3.7.0~git20260211.8c6ebf8/rhel/README.RHEL.rst000066400000000000000000000232611514270232600221360ustar00rootroot00000000000000=================================== Red Hat network scripts integration =================================== The RPM packages for Open vSwitch provide some integration with Red Hat's network scripts. Using this integration is optional. To use the integration for a Open vSwitch bridge or interface named ````, create or edit ``/etc/sysconfig/network-scripts/ifcfg-``. This is a shell script that consists of a series of ``VARIABLE=VALUE`` assignments. The following OVS-specific variable names are supported: DEVICETYPE Always set to "ovs". TYPE If this is "OVSBridge", then this file represents an OVS bridge named . Otherwise, it represents a port on an OVS bridge and TYPE must have one of the following values: * ``OVSPort``, if ```` is a physical port (e.g. eth0) or virtual port (e.g. vif1.0). * ``OVSIntPort``, if ```` is an internal port (e.g. a tagged VLAN). * ``OVSBond``, if ```` is an OVS bond. * ``OVSTunnel``, if ```` is an OVS tunnel. * ``OVSPatchPort``, if ```` is a patch port Additionally the following DPDK port types may be available, depends on OVS build- and runtime configuration: * ``OVSDPDKPort``, if ```` is a physical DPDK NIC port (name must start with ``dpdk`` and end with portid, eg ``dpdk0``) * ``OVSDPDKVhostUserPort`` if ```` is a DPDK vhost-user port * ``OVSDPDKBond`` if ```` is an OVS DPDK bond. OVS_BRIDGE If TYPE is anything other than "OVSBridge", set to the name of the OVS bridge to which the port should be attached. OVS_OPTIONS Optionally, extra options to set in the "Port" table when adding the port to the bridge, as a sequence of column[:key]=value options. For example, "tag=100" to make the port an access port for VLAN 100. See the documentation of "add-port" in ovs-vsctl(8) for syntax and the section on the Port table in ovs-vswitchd.conf.db(5) for available options. OVS_EXTRA Optionally, additional ovs-vsctl commands, separated by ``--`` (double dash). BOND_IFACES For "OVSBond" and "OVSDPDKBond" interfaces, a list of physical interfaces to bond together. OVS_TUNNEL_TYPE For "OVSTunnel" interfaces, the type of the tunnel. For example, "gre", "vxlan", etc. OVS_TUNNEL_OPTIONS For "OVSTunnel" interfaces, this field should be used to specify the tunnel options like remote_ip, key, etc. OVS_PATCH_PEER For "OVSPatchPort" devices, this field specifies the patch's peer on the other bridge. OVS_PORT_MODE For "OVSDPDKVhostUserPort" devices, this field can be set to "client" which indicates that the port will be used in client mode. OVS_PORT_PATH For "OVSDPDKVhostUserPort" devices, this field specifies the path to the vhost-user server socket. It will only be used if OVS_PORT_MODE is set to "client". Note ---- * ``ifdown`` on a bridge will not bring individual ports on the bridge down. "ifup" on a bridge will not add ports to the bridge. This behavior should be compatible with standard bridges (with ``TYPE=Bridge``). * If ``ifup`` on an interface is called multiple times, one can see ``RTNETLINK answers: File exists`` printed on the console. This comes from ifup-eth trying to add zeroconf route multiple times and is harmless. * ``ifup`` on OVSDPDKPort or OVSDPDKBond may result in change of bridge mac address. Since OVS changes the device state to DOWN before changing its mac address this result in loss of bridge configuration (e.g. routes). ``ifup-ovs`` perform post-up operation on the bridge again to restore configuration. Examples -------- Standalone bridge: :: ==> ifcfg-ovsbridge0 <== DEVICE=ovsbridge0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBridge BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no Enable DHCP on the bridge: * Needs ``OVSBOOTPROTO`` instead of ``BOOTPROTO``. * All the interfaces that can reach the DHCP server as a space separated list in ``OVSDHCPINTERFACES``. :: DEVICE=ovsbridge0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBridge OVSBOOTPROTO="dhcp" OVSDHCPINTERFACES="eth0" HOTPLUG=no Adding Internal Port to ovsbridge0: :: ==> ifcfg-intbr0 <== DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 HOTPLUG=no Internal Port with fixed IP address: :: DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no Internal Port with DHCP: * Needs ``OVSBOOTPROTO`` or ``BOOTPROTO``. * All the interfaces that can reach the DHCP server as a space separated list in ``OVSDHCPINTERFACES``. :: DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 OVSBOOTPROTO="dhcp" OVSDHCPINTERFACES="eth0" HOTPLUG=no Adding physical ``eth0`` to ``ovsbridge0`` described above: :: ==> ifcfg-eth0 <== DEVICE=eth0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPort OVS_BRIDGE=ovsbridge0 BOOTPROTO=none HOTPLUG=no Tagged VLAN interface on top of ``ovsbridge0``: :: ==> ifcfg-vlan100 <== DEVICE=vlan100 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 OVS_BRIDGE=ovsbridge0 OVS_OPTIONS="tag=100" OVS_EXTRA="set Interface $DEVICE external-ids:iface-id=$(hostname -s)-$DEVICE-vif" HOTPLUG=no Bonding: :: ==> ifcfg-bond0 <== DEVICE=bond0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBond OVS_BRIDGE=ovsbridge0 BOOTPROTO=none BOND_IFACES="gige-1b-0 gige-1b-1 gige-21-0 gige-21-1" OVS_OPTIONS="bond_mode=balance-tcp lacp=active" HOTPLUG=no :: ==> ifcfg-gige-* <== DEVICE=gige-* ONBOOT=yes HOTPLUG=no An Open vSwitch Tunnel: :: ==> ifcfg-gre0 <== DEVICE=ovs-gre0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSTunnel OVS_BRIDGE=ovsbridge0 OVS_TUNNEL_TYPE=gre OVS_TUNNEL_OPTIONS="options:remote_ip=A.B.C.D" Patch Ports: :: ==> ifcfg-patch-ovs-0 <== DEVICE=patch-ovs-0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPatchPort OVS_BRIDGE=ovsbridge0 OVS_PATCH_PEER=patch-ovs-1 :: ==> ifcfg-patch-ovs-1 <== DEVICE=patch-ovs-1 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPatchPort OVS_BRIDGE=ovsbridge1 OVS_PATCH_PEER=patch-ovs-0 User bridge: :: ==> ifcfg-obr0 <== DEVICE=obr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSUserBridge BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no DPDK NIC port: :: ==> ifcfg-dpdk0 <== DPDK vhost-user port: DEVICE=dpdk0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSDPDKPort OVS_BRIDGE=obr0 :: ==> ifcfg-vhu0 <== DEVICE=vhu0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSDPDKVhostUserPort OVS_BRIDGE=obr0 :: ==> ifcfg-bond0 <== DEVICE=bond0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSDPDKBond OVS_BRIDGE=ovsbridge0 BOOTPROTO=none BOND_IFACES="dpdk0 dpdk1" OVS_OPTIONS="bond_mode=active-backup" HOTPLUG=no Red Hat systemd integration --------------------------- The RPM packages for Open vSwitch provide support for systemd integration. It's recommended to use the openvswitch.service to start and stop the Open vSwitch daemons. The below table shows systemd's behavior: =============================== ============== ============== ============== =============== =============== - Process Status systemctl <> status ------------------------------- ----------------------------- ---------------------------------------------- Action ovs-vswitch ovsdb-server openvswitch ovs-vswitchd ovsdb-server =============================== ============== ============== ============== =============== =============== systemctl start openvswitch* started started active, exited active, running active, running crash of vswitchd crash, started re-started active, exited active, running active, running crash of ovsdb re-started crash, started active, exited active, running active, running systemctl restart openvswitch re-started re-started active, exited active, running active, running systemctl restart ovs-vswitchd re-started re-started active, exited active, running active, running systemctl restart ovsdb-server re-started re-started active, exited active, running active, running systemctl stop openvswitch stopped stopped inactive, dead inactive, dead inactive, dead systemctl stop ovs-vswitchd stopped stopped inactive, dead inactive, dead inactive, dead systemctl stop ovsdb-server stopped stopped inactive, dead inactive, dead inactive, dead systemctl start ovs-vswitchd* started started inactive, dead active, running active, running systemctl start ovsdb-server* not started started inactive, dead inactive, dead active, running =============================== ============== ============== ============== =============== =============== \* These commands where executed when no Open vSwitch related processes where running. All other commands where executed when Open vSwitch was successfully running. Non-root User Support ----------------------- Fedora and RHEL support running the Open vSwitch daemons as a non-root user. By default, a fresh installation will create an *openvswitch* user, along with any additional support groups needed (such as *hugetlbfs* for DPDK support). This is controlled by modifying the ``OVS_USER_ID`` option. Setting this to 'root:root', or commenting the variable out will revert this behavior. Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. openvswitch-3.7.0~git20260211.8c6ebf8/rhel/automake.mk000066400000000000000000000040551514270232600220550ustar00rootroot00000000000000# Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ rhel/README.RHEL.rst \ rhel/automake.mk \ rhel/etc_init.d_openvswitch \ rhel/etc_logrotate.d_openvswitch \ rhel/etc_openvswitch_default.conf \ rhel/etc_sysconfig_network-scripts_ifdown-ovs \ rhel/etc_sysconfig_network-scripts_ifup-ovs \ rhel/openvswitch.spec \ rhel/openvswitch.spec.in \ rhel/openvswitch-fedora.spec \ rhel/openvswitch-fedora.spec.in \ rhel/usr_share_openvswitch_scripts_ovs-systemd-reload \ rhel/usr_share_openvswitch_scripts_sysconfig.template \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ rhel/usr_lib_udev_rules.d_91-vfio.rules \ rhel/usr_lib_systemd_system_openvswitch.service \ rhel/usr_lib_systemd_system_ovsdb-server.service \ rhel/usr_lib_systemd_system_ovs-vswitchd.service.in \ rhel/usr_lib_systemd_system_ovs-delete-transient-ports.service \ rhel/usr_lib_systemd_system_openvswitch-ipsec.service DISTCLEANFILES += rhel/usr_lib_systemd_system_ovs-vswitchd.service update_rhel_spec = \ $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ < $(srcdir)/rhel/$(@F).in > $(@F).tmp || exit 1; \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi $(srcdir)/rhel/openvswitch.spec: rhel/openvswitch.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-fedora.spec: rhel/openvswitch-fedora.spec.in $(top_builddir)/config.status $(update_rhel_spec) RPMBUILD_TOP := $(abs_top_builddir)/rpm/rpmbuild RPMBUILD_OPT ?= --without check # Build user-space RPMs rpm-fedora: dist $(srcdir)/rhel/openvswitch-fedora.spec ${MKDIR_P} ${RPMBUILD_TOP}/SOURCES cp ${DIST_ARCHIVES} ${RPMBUILD_TOP}/SOURCES rpmbuild ${RPMBUILD_OPT} \ -D "_topdir ${RPMBUILD_TOP}" \ -ba $(srcdir)/rhel/openvswitch-fedora.spec openvswitch-3.7.0~git20260211.8c6ebf8/rhel/etc_init.d_openvswitch000077500000000000000000000046231514270232600243160ustar00rootroot00000000000000#!/bin/sh # # openvswitch # # chkconfig: 2345 09 91 # description: Manage Open vSwitch kernel modules and user-space daemons # Copyright (C) 2009, 2010, 2011, 2013 Nicira, Inc. # # 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. ### BEGIN INIT INFO # Provides: openvswitch # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Open vSwitch switch ### END INIT INFO SYSTEMCTL_SKIP_REDIRECT=yes SYSTEMD_NO_WRAP=yes . /usr/share/openvswitch/scripts/ovs-lib || exit 1 test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch start () { set ovs_ctl ${1-start} set "$@" --system-id=random if test X"$FORCE_COREFILES" != X; then set "$@" --force-corefiles="$FORCE_COREFILES" fi if test X"$OVSDB_SERVER_PRIORITY" != X; then set "$@" --ovsdb-server-priority="$OVSDB_SERVER_PRIORITY" fi if test X"$VSWITCHD_PRIORITY" != X; then set "$@" --ovs-vswitchd-priority="$VSWITCHD_PRIORITY" fi if test X"$VSWITCHD_MLOCKALL" != X; then set "$@" --mlockall="$VSWITCHD_MLOCKALL" fi set "$@" $OVS_CTL_OPTS "$@" touch /var/lock/subsys/openvswitch } stop () { ovs_ctl stop rm -f /var/lock/subsys/openvswitch } restart () { if [ "$1" = "--save-flows=yes" ]; then start restart else stop start fi } case $1 in start) start ;; stop) stop ;; restart) shift restart "$@" ;; reload|force-reload) # Nothing to do. ;; status) ovs_ctl status exit $? ;; version) ovs_ctl version ;; force-reload-kmod) start force-reload-kmod ;; help) printf "$0 [start|stop|restart|reload|force-reload|status|version|force-reload-kmod]\n" ;; *) printf "Unknown command: $1\n" exit 1 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/rhel/etc_logrotate.d_openvswitch000066400000000000000000000012431514270232600253430ustar00rootroot00000000000000# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. /var/log/openvswitch/*.log { su root root daily compress sharedscripts missingok postrotate # Tell Open vSwitch daemons to reopen their log files if [ -d /run/openvswitch ]; then for ctl in /run/openvswitch/*.ctl; do ovs-appctl -t "$ctl" vlog/reopen 2>/dev/null || : done fi endscript } openvswitch-3.7.0~git20260211.8c6ebf8/rhel/etc_openvswitch_default.conf000066400000000000000000000002431514270232600254700ustar00rootroot00000000000000# DO NOT EDIT THIS FILE # The following is the *default* configuration for the openvswitch user ID. # This is for backward compatibility. OVS_USER_ID="root:root" openvswitch-3.7.0~git20260211.8c6ebf8/rhel/etc_sysconfig_network-scripts_ifdown-ovs000077500000000000000000000042001514270232600301040ustar00rootroot00000000000000#!/bin/bash # Copyright (c) 2011 Alexey I. Froloff. # # 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. . /etc/init.d/functions cd /etc/sysconfig/network-scripts . ./network-functions [ -f ../network ] && . ../network CONFIG=${1} TIMEOUT=10 source_config . /etc/sysconfig/network OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-${REAL_DEVICETYPE}" if [ ! -x ${OTHERSCRIPT} ]; then OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-eth" fi SERVICE_UNIT=/usr/lib/systemd/system/ovsdb-server.service if [ -f $SERVICE_UNIT ] && [ -x /usr/bin/systemctl ]; then if ! systemctl --quiet is-active ovsdb-server.service; then systemctl start ovsdb-server.service fi else if [ ! -f /var/lock/subsys/openvswitch ]; then /sbin/service openvswitch start fi fi case "$TYPE" in OVSBridge|OVSUserBridge) ${OTHERSCRIPT} ${CONFIG} $2 retval=$? ovs-vsctl -t ${TIMEOUT} -- --if-exists del-br "$DEVICE" ;; OVSPort|OVSIntPort|OVSBond) ${OTHERSCRIPT} ${CONFIG} $2 retval=$? ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; OVSPatchPort|OVSTunnel) ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; OVSDPDKPort|OVSDPDKVhostUserPort|OVSDPDKBond) ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; *) echo $"Invalid OVS interface type $TYPE" exit 1 ;; esac exit $retval openvswitch-3.7.0~git20260211.8c6ebf8/rhel/etc_sysconfig_network-scripts_ifup-ovs000077500000000000000000000224151514270232600275710ustar00rootroot00000000000000#!/bin/bash # Copyright (c) 2011 Alexey I. Froloff. # # 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. . /etc/init.d/functions cd /etc/sysconfig/network-scripts . ./network-functions [ -f ../network ] && . ../network CONFIG=${1} TIMEOUT=10 need_config ${CONFIG} source_config OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${REAL_DEVICETYPE}" if [ ! -x ${OTHERSCRIPT} ]; then OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth" fi check_recursion () { [ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do [ "$_r" = "$1" ] && return 1 done return 0 } ifup_ovs_bridge () { if ovs-vsctl br-exists "${OVS_BRIDGE}"; then :; else /sbin/ifup "${OVS_BRIDGE}" fi } if [ -z "${UPPEDSTACK}" ]; then UPPEDSTACK="${DEVICE}" fi [ -n "${OVSREQUIRES}" ] && for _i in ${OVSREQUIRES}; do if ( check_recursion "$_i" ); then UPPEDSTACK="${UPPEDSTACK} $_i" /sbin/ifup "$_i" fi done SERVICE_UNIT=/usr/lib/systemd/system/openvswitch.service if [ -f $SERVICE_UNIT ] && [ -x /usr/bin/systemctl ]; then if ! systemctl --quiet is-active openvswitch.service; then systemctl start openvswitch.service fi else if [ ! -f /var/lock/subsys/openvswitch ]; then /sbin/service openvswitch start fi fi case "$TYPE" in OVSBridge|OVSUserBridge) # If bridge already exists and is up, it has been configured through # other cases like OVSPort, OVSIntPort and OVSBond. If it is down or # it does not exist, create it. It is possible for a bridge to exist # because it remained in the OVSDB for some reason, but it won't be up. if [ "${TYPE}" = "OVSUserBridge" ]; then DATAPATH="netdev" fi if check_device_down "${DEVICE}"; then ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS \ ${OVS_EXTRA+-- $OVS_EXTRA} \ ${STP+-- set bridge "$DEVICE" stp_enable="${STP}"} \ ${DATAPATH+-- set bridge "$DEVICE" datapath_type="$DATAPATH"} else OVSBRIDGECONFIGURED="yes" fi # If MACADDR is provided in the interface configuration file, # we need to set it using ovs-vsctl; setting it with the "ip" # command in ifup-eth does not make the change persistent. if [ -n "$MACADDR" ]; then ovs-vsctl -t ${TIMEOUT} -- set bridge "$DEVICE" \ other-config:hwaddr="$MACADDR" fi # When dhcp is enabled, the assumption is that there will be a port to # attach (otherwise, we can't reach out for dhcp). So, we do not # configure the bridge through rhel's ifup infrastructure unless # it is being configured after the port has been configured. # The "OVSINTF" is set only after the port is configured. if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then case " ${OVSDHCPINTERFACES} " in *" ${OVSINTF} "*) BOOTPROTO=dhcp ${OTHERSCRIPT} ${CONFIG} ;; esac fi # When dhcp is not enabled, it is possible that someone may want # a standalone bridge (i.e it may not have any ports). Configure it. if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ] && \ [ "${OVSBRIDGECONFIGURED}" != "yes" ]; then ${OTHERSCRIPT} ${CONFIG} fi exit 0 ;; OVSPort) ifup_ovs_bridge ${OTHERSCRIPT} ${CONFIG} ${2} # The port might be already in the database but not yet # in the datapath. So, remove the stale interface first. ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE" ;; OVSIntPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \ -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA} if [ -n "${OVSDHCPINTERFACES}" ]; then for _iface in ${OVSDHCPINTERFACES}; do /sbin/ifup ${_iface} done fi BOOTPROTO="${OVSBOOTPROTO}" ${OTHERSCRIPT} ${CONFIG} ${2} ;; OVSBond) ifup_ovs_bridge for _iface in $BOND_IFACES; do /sbin/ifup ${_iface} done ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE" ;; OVSTunnel) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \ -- set Interface "$DEVICE" type=$OVS_TUNNEL_TYPE $OVS_TUNNEL_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSPatchPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \ -- set Interface "$DEVICE" type=patch options:peer="${OVS_PATCH_PEER}" ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDKPort) ifup_ovs_bridge BRIDGE_MAC_ORIG=$(get_hwaddr $OVS_BRIDGE) ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \ -- set Interface "$DEVICE" type=dpdk ${OVS_EXTRA+-- $OVS_EXTRA} BRIDGE_MAC=$(get_hwaddr $OVS_BRIDGE) # The bridge may change its MAC to be the lower one among all its # ports. If that happens, bridge configuration (e.g. routes) will # be lost. Restore the post-up bridge configuration again. if [ "$BRIDGE_MAC_ORIG" != "$BRIDGE_MAC" ]; then ${OTHERSCRIPT} "$OVS_BRIDGE" fi ;; OVSDPDKVhostUserPort) ifup_ovs_bridge PORT_TYPE="dpdkvhostuser" PORT_PATH="" if [ "$OVS_PORT_MODE" == "client" ]; then PORT_TYPE="dpdkvhostuserclient" PORT_PATH="options:vhost-server-path=${OVS_PORT_PATH}" fi ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS \ -- set Interface "$DEVICE" type=$PORT_TYPE \ $PORT_PATH \ ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDKBond) ifup_ovs_bridge BRIDGE_MAC_ORIG=$(get_hwaddr $OVS_BRIDGE) for _iface in $BOND_IFACES; do IFACE_TYPES="${IFACE_TYPES} -- set interface ${_iface} type=dpdk" done ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${IFACE_TYPES} ${OVS_EXTRA+-- $OVS_EXTRA} BRIDGE_MAC=$(get_hwaddr $OVS_BRIDGE) # The bridge may change its MAC to be the lower one among all its # ports. If that happens, bridge configuration (e.g. routes) will # be lost. Restore the post-up bridge configuration again. if [ "$BRIDGE_MAC_ORIG" != "$BRIDGE_MAC" ]; then ${OTHERSCRIPT} "$OVS_BRIDGE" fi ;; *) echo $"Invalid OVS interface type $TYPE" exit 1 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/rhel/openvswitch-fedora.spec.in000066400000000000000000000375511514270232600250150ustar00rootroot00000000000000# Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2013, 2014, 2015, 2016 Nicira Networks, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch-fedora.spec # # Support for executing kernel data path tests under rpmbuild is # provided, however this is intended for use only in test environments # and should not be used otherwise (these tests require root privileges). # These tests can be executed, for example, via: # rpmbuild -rb --with check_datapath_kernel openvswitch-fedora.src.rpm # If libcap-ng isn't available and there is no need for running OVS # as regular user, specify the '--without libcapng' %bcond_without libcapng # To enable DPDK support, specify '--with dpdk' when building %bcond_with dpdk # To disable AF_XDP support, specify '--without afxdp' when building %bcond_without afxdp # To control the USDT support %bcond_without usdt # If there is a need to automatically enable the package after installation, # specify the "--with autoenable" %bcond_with autoenable # Enable PIE, bz#955181 %global _hardened_build 1 # some distros (e.g: RHEL-7) don't define _rundir macro yet # Fedora 15 onwards uses /run as _rundir %if 0%{!?_rundir:1} %define _rundir /run %endif %{!?release_number:%define release_number 1} Name: openvswitch Summary: Open vSwitch Group: System Environment/Daemons URL: http://www.openvswitch.org/ Version: @VERSION@ # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL License: ASL 2.0 and LGPLv2+ and SISSL Release: %{release_number}%{?dist} Source: http://openvswitch.org/releases/%{name}-%{version}.tar.gz BuildRequires: gcc gcc-c++ BuildRequires: autoconf automake libtool BuildRequires: systemd-units openssl openssl-devel BuildRequires: python3-devel BuildRequires: desktop-file-utils BuildRequires: groff graphviz BuildRequires: checkpolicy, selinux-policy-devel BuildRequires: /usr/bin/sphinx-build-3 # make check dependencies BuildRequires: procps-ng %if %{with libcapng} BuildRequires: libcap-ng libcap-ng-devel Provides: user(openvswitch) Provides: group(openvswitch) %if %{with dpdk} Provides: group(hugetlbfs) %endif %endif %if %{with dpdk} BuildRequires: libpcap-devel numactl-devel BuildRequires: dpdk-devel >= 25.11 Provides: %{name}-dpdk = %{version}-%{release} %endif %if %{with afxdp} BuildRequires: libxdp-devel libbpf-devel numactl-devel %endif %if %{with usdt} BuildRequires: libbpf-devel systemtap-sdt-devel %endif BuildRequires: unbound unbound-devel Requires: openssl hostname iproute module-init-tools unbound Requires(pre): shadow-utils Requires(post): /bin/sed Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units Obsoletes: openvswitch-controller <= 0:2.1.0-1 # to skip running checks, pass --without check %bcond_without check %bcond_with check_datapath_kernel %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %package selinux-policy Summary: Open vSwitch SELinux policy License: ASL 2.0 BuildArch: noarch Requires: selinux-policy-targeted %description selinux-policy Tailored Open vSwitch SELinux policy %package -n python3-openvswitch Summary: Open vSwitch python3 bindings License: ASL 2.0 BuildArch: noarch Requires: python3 # DNS resolution support in Python IDL. Suggests: python3-unbound # Dependencies of ovs.flow library. Suggests: python3-netaddr python3-pyparsing # Dependencies of ovs-flowviz. Suggests: python3-click python3-graphviz python3-rich %{?python_provide:%python_provide python3-openvswitch = %{version}-%{release}} %description -n python3-openvswitch Python bindings for the Open vSwitch database %package test Summary: Open vSwitch testing utilities License: ASL 2.0 BuildArch: noarch %description test Utilities that are useful to diagnose performance and connectivity issues in Open vSwitch setup. %package devel Summary: Open vSwitch OpenFlow development package (library, headers) License: ASL 2.0 %description devel This provides shared library, libopenswitch.so and the openvswitch header files needed to build an external application. %if (0%{?rhel} > 7 && 0%{?rhel} < 9) || (0%{?fedora} > 28 && 0%{?fedora} < 41) %package -n network-scripts-%{name} Summary: Open vSwitch legacy network service support License: ASL 2.0 Requires: network-scripts Supplements: (%{name} and network-scripts) %description -n network-scripts-%{name} This provides the ifup and ifdown scripts for use with the legacy network service. %endif %package ipsec Summary: Open vSwitch IPsec tunneling support License: ASL 2.0 Requires: openvswitch python3-openvswitch libreswan %description ipsec This package provides IPsec tunneling support for OVS tunnels. %prep %setup -q %build %configure \ %if %{with libcapng} --enable-libcapng \ %else --disable-libcapng \ %endif %if %{with dpdk} --with-dpdk=shared \ %endif %if %{with afxdp} --enable-afxdp \ %else --disable-afxdp \ %endif %if %{with usdt} --enable-usdt-probes \ %endif --enable-ssl \ --disable-static \ --enable-shared \ --with-pkidir=%{_sharedstatedir}/openvswitch/pki \ --with-version-suffix=-%{release} \ PYTHON3=%{__python3} build-aux/dpdkstrip.py \ %if %{with dpdk} --dpdk \ %else --nodpdk \ %endif < rhel/usr_lib_systemd_system_ovs-vswitchd.service.in \ > rhel/usr_lib_systemd_system_ovs-vswitchd.service make %{?_smp_mflags} make selinux-policy %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT install -d -m 0755 $RPM_BUILD_ROOT%{_rundir}/openvswitch install -d -m 0750 $RPM_BUILD_ROOT%{_localstatedir}/log/openvswitch install -d -m 0755 $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch %if %{with dpdk} install -p -D -m 0644 rhel/usr_lib_udev_rules.d_91-vfio.rules \ $RPM_BUILD_ROOT%{_prefix}/lib/udev/rules.d/91-vfio.rules %endif install -p -D -m 0644 \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/openvswitch for service in openvswitch ovsdb-server ovs-vswitchd ovs-delete-transient-ports \ openvswitch-ipsec; do install -p -D -m 0644 \ rhel/usr_lib_systemd_system_${service}.service \ $RPM_BUILD_ROOT%{_unitdir}/${service}.service done install -m 0755 rhel/etc_init.d_openvswitch \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/openvswitch.init install -p -D -m 0644 rhel/etc_openvswitch_default.conf \ $RPM_BUILD_ROOT/%{_sysconfdir}/openvswitch/default.conf install -p -D -m 0644 rhel/etc_logrotate.d_openvswitch \ $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/openvswitch install -m 0644 vswitchd/vswitch.ovsschema \ $RPM_BUILD_ROOT/%{_datadir}/openvswitch/vswitch.ovsschema install -d -m 0755 $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ %if ! (0%{?rhel} >= 9 || 0%{?fedora} >= 41) install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifdown-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifup-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifup-ovs %endif install -d -m 0755 $RPM_BUILD_ROOT%{python3_sitelib} cp -a $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/* \ $RPM_BUILD_ROOT%{python3_sitelib} mv $RPM_BUILD_ROOT%{python3_sitelib}/ovs/flowviz/ovs-flowviz \ $RPM_BUILD_ROOT/%{_bindir}/ovs-flowviz rm -rf $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/ install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch touch $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch/system-id.conf install -p -m 644 -D selinux/openvswitch-custom.pp \ $RPM_BUILD_ROOT%{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp install -d $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ install -p -D -m 0755 \ rhel/usr_share_openvswitch_scripts_ovs-systemd-reload \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs-systemd-reload # remove unpackaged files rm -f $RPM_BUILD_ROOT%{_bindir}/ovs-parse-backtrace # Remove .la files, if present (automatic with %__brp_remove_la_files on f36+). rm -f $RPM_BUILD_ROOT%{_libdir}/*.la %check %if %{with check} touch resolv.conf export OVS_RESOLV_CONF=$(pwd)/resolv.conf if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %if %{with check_datapath_kernel} if make check-kernel RECHECK=yes; then :; else cat tests/system-kmod-testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %pre selinux-policy %selinux_relabel_pre -s targeted %preun %if 0%{?systemd_preun:1} %systemd_preun %{name}.service %else if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable %{name}.service >/dev/null 2>&1 || : /bin/systemctl stop %{name}.service >/dev/null 2>&1 || : fi %endif %pre %if %{with libcapng} getent group openvswitch >/dev/null || groupadd -r openvswitch getent passwd openvswitch >/dev/null || \ useradd -r -g openvswitch -d / -s /sbin/nologin \ -c "Open vSwitch Daemons" openvswitch %if %{with dpdk} getent group hugetlbfs >/dev/null || groupadd -r hugetlbfs usermod -a -G hugetlbfs openvswitch %endif %endif %if %{with autoenable} if [ -x "/etc/init.d/openvswitch" ]; then touch %{_tmppath}/ovs-upgrade-from-sysv fi %endif exit 0 %post %if %{with libcapng} if [ $1 -eq 1 ]; then %if %{with dpdk} %define gname hugetlbfs %else %define gname openvswitch %endif sed -i \ 's@^#OVS_USER_ID="openvswitch:openvswitch"@OVS_USER_ID="openvswitch:%{gname}"@'\ %{_sysconfdir}/sysconfig/openvswitch sed -i 's:\(.*su\).*:\1 openvswitch %{gname}:' %{_sysconfdir}/logrotate.d/openvswitch # In the case of upgrade, this is not needed chown -R openvswitch:openvswitch %{_sysconfdir}/openvswitch chown -R openvswitch:%{gname} %{_localstatedir}/log/openvswitch fi %endif # Ensure that /etc/openvswitch/conf.db links to /var/lib/openvswitch, # moving an existing file if there is one. # # Ditto for .conf.db.~lock~. for base in conf.db .conf.db.~lock~; do new=/var/lib/openvswitch/$base old=/etc/openvswitch/$base if test -f $old && test ! -e $new; then mv $old $new fi if test ! -e $old && test ! -h $old; then ln -s $new $old fi done %if 0%{?systemd_post:1} # This may not enable openvswitch service or do daemon-reload. %systemd_post %{name}.service %else # Package install, not upgrade if [ $1 -eq 1 ]; then /bin/systemctl daemon-reload >/dev/null || : fi %endif %if %{with autoenable} systemctl daemon-reload systemctl enable openvswitch # Handle upgrades to this package from the OVS repo's rhel packages. # One "restart" is needed for newer systemd files to see the old running # daemons. Another "restart" (outside the package postinst script) is # needed to actually run new daemons. if [ -e "%{_tmppath}/ovs-upgrade-from-sysv" ]; then systemctl restart openvswitch rm "%{_tmppath}/ovs-upgrade-from-sysv" fi %endif %post selinux-policy %selinux_modules_install -s targeted %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp %postun %if 0%{?systemd_postun:1} %systemd_postun %{name}.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif %postun selinux-policy if [ $1 -eq 0 ] ; then %selinux_modules_uninstall -s targeted openvswitch-custom fi %posttrans selinux-policy %selinux_relabel_post -s targeted %files selinux-policy %defattr(-,root,root) %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp %files -n python3-openvswitch %{_bindir}/ovs-flowviz %{_mandir}/man8/ovs-flowviz.8* %{python3_sitelib}/ovs %files test %{_bindir}/ovs-test %{_bindir}/ovs-vlan-test %{_bindir}/ovs-l3ping %{_bindir}/ovs-pcap %{_bindir}/ovs-tcpdump %{_bindir}/ovs-tcpundump %{_datadir}/openvswitch/scripts/usdt/* %{_mandir}/man8/ovs-test.8* %{_mandir}/man8/ovs-vlan-test.8* %{_mandir}/man8/ovs-l3ping.8* %{_mandir}/man1/ovs-pcap.1* %{_mandir}/man8/ovs-tcpdump.8* %{_mandir}/man1/ovs-tcpundump.1* %{python3_sitelib}/ovstest %files devel %{_libdir}/lib*.so %{_libdir}/pkgconfig/*.pc %{_includedir}/openvswitch/* %{_includedir}/openflow/* %if (0%{?rhel} > 7 && 0%{?rhel} < 9) || (0%{?fedora} > 28 && 0%{?fedora} < 41) %files -n network-scripts-%{name} %{_sysconfdir}/sysconfig/network-scripts/ifup-ovs %{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs %endif %files %if %{with libcapng} %defattr(-,openvswitch,openvswitch) %else %defattr(-,root,root) %endif %dir %{_sysconfdir}/openvswitch %{_sysconfdir}/openvswitch/default.conf %config %ghost %{_sharedstatedir}/openvswitch/conf.db %ghost %{_sharedstatedir}/openvswitch/.conf.db.~lock~ %config %ghost %{_sysconfdir}/openvswitch/system-id.conf %config(noreplace) %{_sysconfdir}/sysconfig/openvswitch %defattr(-,root,root) %{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash %{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash %config(noreplace) %{_sysconfdir}/logrotate.d/openvswitch %{_unitdir}/openvswitch.service %{_unitdir}/ovsdb-server.service %{_unitdir}/ovs-vswitchd.service %{_unitdir}/ovs-delete-transient-ports.service %{_datadir}/openvswitch/scripts/openvswitch.init %if ! (0%{?rhel} > 7 || 0%{?fedora} > 28) %{_sysconfdir}/sysconfig/network-scripts/ifup-ovs %{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs %endif %{_datadir}/openvswitch/bugtool-plugins/ %{_datadir}/openvswitch/scripts/ovs-bugtool-* %{_datadir}/openvswitch/scripts/ovs-check-dead-ifs %{_datadir}/openvswitch/scripts/ovs-lib %{_datadir}/openvswitch/scripts/ovs-save %{_datadir}/openvswitch/scripts/ovs-vtep %{_datadir}/openvswitch/scripts/ovs-ctl %{_datadir}/openvswitch/scripts/ovs-kmod-ctl %{_datadir}/openvswitch/scripts/ovs-systemd-reload %config %{_datadir}/openvswitch/local-config.ovsschema %config %{_datadir}/openvswitch/vswitch.ovsschema %config %{_datadir}/openvswitch/vtep.ovsschema %{_bindir}/ovs-appctl %{_bindir}/ovs-docker %{_bindir}/ovs-dpctl %{_bindir}/ovs-dpctl-top %{_bindir}/ovs-ofctl %{_bindir}/ovs-vsctl %{_bindir}/ovsdb-client %{_bindir}/ovsdb-tool %{_bindir}/ovs-testcontroller %{_bindir}/ovs-pki %{_bindir}/vtep-ctl %{_libdir}/lib*.so.* %{_sbindir}/ovs-bugtool %{_sbindir}/ovs-vswitchd %{_sbindir}/ovsdb-server %{_mandir}/man1/ovsdb-client.1* %{_mandir}/man1/ovsdb-server.1* %{_mandir}/man1/ovsdb-tool.1* %{_mandir}/man5/ovsdb-server.5* %{_mandir}/man5/ovsdb.local-config.5* %{_mandir}/man5/ovs-vswitchd.conf.db.5* %{_mandir}/man5/ovsdb.5* %{_mandir}/man5/vtep.5* %{_mandir}/man7/ovs-actions.7* %{_mandir}/man7/ovs-fields.7* %{_mandir}/man7/ovsdb.7* %{_mandir}/man7/ovsdb-server.7* %{_mandir}/man8/vtep-ctl.8* %{_mandir}/man8/ovs-appctl.8* %{_mandir}/man8/ovs-bugtool.8* %{_mandir}/man8/ovs-ctl.8* %{_mandir}/man8/ovs-dpctl.8* %{_mandir}/man8/ovs-dpctl-top.8* %{_mandir}/man8/ovs-kmod-ctl.8* %{_mandir}/man8/ovs-ofctl.8* %{_mandir}/man8/ovs-pki.8* %{_mandir}/man8/ovs-vsctl.8* %{_mandir}/man8/ovs-vswitchd.8* %{_mandir}/man8/ovs-parse-backtrace.8* %{_mandir}/man8/ovs-testcontroller.8* %if %{with dpdk} %{_prefix}/lib/udev/rules.d/91-vfio.rules %endif %doc NOTICE README.rst NEWS rhel/README.RHEL.rst %if %{with dpdk} %attr(750,openvswitch,hugetlbfs) /var/lib/openvswitch %else %attr(750,openvswitch,openvswitch) /var/lib/openvswitch %endif %attr(750,root,root) /var/log/openvswitch %ghost %attr(755,root,root) %{_rundir}/openvswitch %ghost %attr(644,root,root) %{_rundir}/openvswitch.useropts %files ipsec %{_datadir}/openvswitch/scripts/ovs-monitor-ipsec %{_unitdir}/openvswitch-ipsec.service %changelog * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 openvswitch-3.7.0~git20260211.8c6ebf8/rhel/openvswitch.spec.in000066400000000000000000000172141514270232600235510ustar00rootroot00000000000000# Spec file for Open vSwitch on Red Hat Enterprise Linux. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch.spec # # Support for executing kernel data path tests under rpmbuild is # provided, however this is intended for use only in test environments # and should not be used otherwise (these tests require root privileges). # These tests can be executed, for example, via: # rpmbuild -rb --with check_datapath_kernel openvswitch.src.rpm # # These tests will use the currently installed OVS kernel modules. %{!?release_number:%define release_number 1} Name: openvswitch Summary: Open vSwitch daemon/database/utilities Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: Nicira, Inc. Version: @VERSION@ License: ASL 2.0 Release: %{release_number}%{?dist} Source: openvswitch-%{version}.tar.gz Buildroot: /tmp/openvswitch-rpm Requires: logrotate, hostname, python >= 2.7, python-six BuildRequires: python-six BuildRequires: openssl-devel BuildRequires: checkpolicy, selinux-policy-devel BuildRequires: autoconf, automake, libtool BuildRequires: python3-sphinx BuildRequires: unbound-devel BuildRequires: libunwind-devel %bcond_without check %bcond_with check_datapath_kernel %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %package devel Summary: Open vSwitch development package Group: Development/Libraries %description devel This package provides openvswitch headers and libopenvswitch for developers. %package selinux-policy Summary: Open vSwitch SELinux policy License: ASL 2.0 BuildArch: noarch Requires: selinux-policy-targeted %description selinux-policy Tailored Open vSwitch SELinux policy %prep %setup -q %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} \ --libdir=%{_libdir} --enable-ssl --enable-shared make %{_smp_mflags} make selinux-policy %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT rhel_cp() { base=$1 mode=$2 dst=$RPM_BUILD_ROOT/$(echo $base | sed 's,_,/,g') install -D -m $mode rhel/$base $dst } rhel_cp etc_init.d_openvswitch 0755 rhel_cp etc_logrotate.d_openvswitch 0644 rhel_cp etc_sysconfig_network-scripts_ifup-ovs 0755 rhel_cp etc_sysconfig_network-scripts_ifdown-ovs 0755 rhel_cp usr_share_openvswitch_scripts_sysconfig.template 0644 install -p -m 644 -D selinux/openvswitch-custom.pp \ $RPM_BUILD_ROOT%{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp # Get rid of stuff we don't want to make RPM happy. rm \ $RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \ $RPM_BUILD_ROOT/usr/bin/ovs-test \ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 (cd "$RPM_BUILD_ROOT" && rm -rf usr/%{_lib}/*.la) (cd "$RPM_BUILD_ROOT" && rm -rf usr/include) install -d -m 0755 $RPM_BUILD_ROOT%{_rundir}/openvswitch install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/log/openvswitch install -d -m 0755 $RPM_BUILD_ROOT/var/lib/openvswitch %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %if %{with check_datapath_kernel} if make check-kernel RECHECK=yes; then :; else cat tests/system-kmod-testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %post # Create default or update existing /etc/sysconfig/openvswitch. SYSCONFIG=/etc/sysconfig/openvswitch TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template if [ ! -e $SYSCONFIG ]; then cp $TEMPLATE $SYSCONFIG else for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE) do if ! grep $var $SYSCONFIG >/dev/null 2>&1; then echo >> $SYSCONFIG sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG fi done fi # Ensure all required services are set to run /sbin/chkconfig --add openvswitch /sbin/chkconfig openvswitch on %pre selinux-policy %selinux_relabel_pre -s targeted %post selinux-policy %selinux_modules_install -s targeted %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp %preun if [ "$1" = "0" ]; then # $1 = 0 for uninstall /sbin/service openvswitch stop /sbin/chkconfig --del openvswitch fi %postun if [ "$1" = "0" ]; then # $1 = 0 for uninstall rm -f /etc/openvswitch/conf.db rm -f /etc/sysconfig/openvswitch rm -f /etc/openvswitch/vswitchd.cacert fi %postun selinux-policy if [ $1 -eq 0 ] ; then %selinux_modules_uninstall -s targeted openvswitch-custom fi exit 0 %posttrans selinux-policy %selinux_relabel_post -s targeted %files %defattr(-,root,root) %dir /etc/openvswitch /etc/bash_completion.d/ovs-appctl-bashcomp.bash /etc/bash_completion.d/ovs-vsctl-bashcomp.bash /etc/init.d/openvswitch %config(noreplace) /etc/logrotate.d/openvswitch /etc/sysconfig/network-scripts/ifup-ovs /etc/sysconfig/network-scripts/ifdown-ovs /usr/bin/ovs-appctl /usr/bin/ovs-dpctl /usr/bin/ovs-dpctl-top /usr/bin/ovs-docker /usr/bin/ovs-ofctl /usr/bin/ovs-parse-backtrace /usr/bin/ovs-pcap /usr/bin/ovs-pki /usr/bin/ovs-tcpdump /usr/bin/ovs-tcpundump /usr/bin/ovs-vlan-test /usr/bin/ovs-vsctl /usr/bin/ovsdb-client /usr/bin/ovsdb-tool /usr/bin/vtep-ctl %{_libdir}/lib*.so.* /usr/sbin/ovs-bugtool /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man1/ovsdb-client.1.gz /usr/share/man/man1/ovsdb-server.1.gz /usr/share/man/man1/ovsdb-tool.1.gz /usr/share/man/man5/ovsdb.local-config.5.gz /usr/share/man/man5/ovsdb-server.5.gz /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz %{_mandir}/man5/ovsdb.5* /usr/share/man/man5/vtep.5.gz /usr/share/man/man7/ovs-actions.7.gz /usr/share/man/man7/ovs-fields.7.gz %{_mandir}/man7/ovsdb.7* %{_mandir}/man7/ovsdb-server.7* /usr/share/man/man8/ovs-appctl.8.gz /usr/share/man/man8/ovs-bugtool.8.gz /usr/share/man/man8/ovs-ctl.8.gz /usr/share/man/man8/ovs-dpctl.8.gz /usr/share/man/man8/ovs-dpctl-top.8.gz /usr/share/man/man8/ovs-flowviz.8.gz /usr/share/man/man8/ovs-kmod-ctl.8.gz /usr/share/man/man8/ovs-ofctl.8.gz /usr/share/man/man8/ovs-parse-backtrace.8.gz /usr/share/man/man8/ovs-pki.8.gz /usr/share/man/man8/ovs-tcpdump.8.gz /usr/share/man/man8/ovs-vlan-test.8.gz /usr/share/man/man8/ovs-vsctl.8.gz /usr/share/man/man8/ovs-vswitchd.8.gz /usr/share/man/man8/vtep-ctl.8.gz /usr/share/openvswitch/bugtool-plugins/ /usr/share/openvswitch/python/ /usr/share/openvswitch/scripts/ovs-bugtool-* /usr/share/openvswitch/scripts/ovs-check-dead-ifs /usr/share/openvswitch/scripts/ovs-ctl /usr/share/openvswitch/scripts/ovs-kmod-ctl /usr/share/openvswitch/scripts/ovs-lib /usr/share/openvswitch/scripts/ovs-save /usr/share/openvswitch/scripts/ovs-vtep /usr/share/openvswitch/scripts/sysconfig.template /usr/share/openvswitch/scripts/ovs-monitor-ipsec /usr/share/openvswitch/local-config.ovsschema /usr/share/openvswitch/vswitch.ovsschema /usr/share/openvswitch/vtep.ovsschema %doc NOTICE README.rst NEWS rhel/README.RHEL.rst /var/lib/openvswitch /var/log/openvswitch %files devel %{_libdir}/lib*.so %{_libdir}/lib*.a %{_libdir}/pkgconfig %{_includedir}/openvswitch/* %files selinux-policy %defattr(-,root,root) %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_systemd_system_openvswitch-ipsec.service000066400000000000000000000007701514270232600316450ustar00rootroot00000000000000[Unit] Description=OVS IPsec daemon Requires=openvswitch.service After=openvswitch.service [Service] Type=forking PIDFile=/run/openvswitch/ovs-monitor-ipsec.pid Restart=on-failure EnvironmentFile=/etc/openvswitch/default.conf EnvironmentFile=-/etc/sysconfig/openvswitch ExecStart=/usr/share/openvswitch/scripts/ovs-ctl --no-monitor \ --ike-daemon=libreswan start-ovs-ipsec $OPTIONS ExecStop=/usr/share/openvswitch/scripts/ovs-ctl stop-ovs-ipsec [Install] WantedBy=multi-user.target openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_systemd_system_openvswitch.service000066400000000000000000000006211514270232600305370ustar00rootroot00000000000000[Unit] Description=Open vSwitch Before=network.target network.service After=network-pre.target ovsdb-server.service ovs-vswitchd.service PartOf=network.target Requires=ovsdb-server.service Requires=ovs-vswitchd.service [Service] Type=oneshot ExecStart=/bin/true ExecReload=/usr/share/openvswitch/scripts/ovs-systemd-reload ExecStop=/bin/true RemainAfterExit=yes [Install] WantedBy=multi-user.target openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_systemd_system_ovs-delete-transient-ports.service000066400000000000000000000004141514270232600334070ustar00rootroot00000000000000[Unit] Description=Open vSwitch Delete Transient Ports After=ovsdb-server.service Before=ovs-vswitchd.service AssertPathExists=/run/openvswitch/db.sock [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/share/openvswitch/scripts/ovs-ctl delete-transient-ports openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_systemd_system_ovs-vswitchd.service.in000066400000000000000000000022031514270232600312310ustar00rootroot00000000000000[Unit] Description=Open vSwitch Forwarding Unit After=ovsdb-server.service network-pre.target systemd-udev-settle.service Before=network.target network.service Requires=ovsdb-server.service ReloadPropagatedFrom=ovsdb-server.service AssertPathIsReadWrite=/run/openvswitch/db.sock PartOf=openvswitch.service [Service] Type=forking PIDFile=/run/openvswitch/ovs-vswitchd.pid Restart=on-failure Environment=XDG_RUNTIME_DIR=/run/openvswitch EnvironmentFile=/etc/openvswitch/default.conf EnvironmentFile=-/etc/sysconfig/openvswitch EnvironmentFile=-/run/openvswitch.useropts LimitSTACK=2M @begin_dpdk@ ExecStartPre=-/bin/sh -c '/usr/bin/chown :$${OVS_USER_ID##*:} /dev/hugepages' ExecStartPre=-/usr/bin/chmod 0775 /dev/hugepages @end_dpdk@ ExecStart=/usr/share/openvswitch/scripts/ovs-ctl \ --no-ovsdb-server --no-monitor --system-id=random \ ${OVS_USER_OPT} \ start $OPTIONS ExecStop=/usr/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server stop ExecReload=/usr/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server \ --no-monitor --system-id=random \ ${OVS_USER_OPT} \ restart $OPTIONS TimeoutSec=300 openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_systemd_system_ovsdb-server.service000066400000000000000000000025511514270232600306130ustar00rootroot00000000000000[Unit] Description=Open vSwitch Database Unit After=syslog.target network-pre.target Before=network.target network.service Wants=ovs-delete-transient-ports.service PartOf=openvswitch.service [Service] Type=forking PIDFile=/run/openvswitch/ovsdb-server.pid Restart=on-failure EnvironmentFile=/etc/openvswitch/default.conf EnvironmentFile=-/etc/sysconfig/openvswitch EnvironmentFile=-/run/openvswitch.useropts # Environment is reloaded for each Exec*, make sure to # remove openvswitch.useropts first to reload a fresh # OVS_USER_ID from default.conf or sysconfig. ExecStartPre=/usr/bin/rm -f /run/openvswitch.useropts ExecStartPre=-/usr/bin/chown -R ${OVS_USER_ID} \ /etc/openvswitch /run/openvswitch /var/log/openvswitch ExecStartPre=/bin/sh -c '/usr/bin/echo "OVS_USER_ID=${OVS_USER_ID}" > /run/openvswitch.useropts' ExecStartPre=/bin/sh -c 'if [ "$${OVS_USER_ID/:*/}" != "root" ]; then /usr/bin/echo "OVS_USER_OPT=--ovs-user=${OVS_USER_ID}" >> /run/openvswitch.useropts; fi' ExecStart=/usr/share/openvswitch/scripts/ovs-ctl \ --no-ovs-vswitchd --no-monitor --system-id=random \ ${OVS_USER_OPT} \ start $OPTIONS ExecStop=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd stop ExecReload=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd \ ${OVS_USER_OPT} \ --no-monitor restart $OPTIONS TimeoutSec=300 openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_lib_udev_rules.d_91-vfio.rules000066400000000000000000000001021514270232600263470ustar00rootroot00000000000000ACTION=="add", SUBSYSTEM=="vfio*", GROUP="hugetlbfs", MODE="0660" openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_share_openvswitch_scripts_ovs-systemd-reload000077500000000000000000000022111514270232600316500ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 2017 Red Hat, Inc. # # 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. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 stop_ovsdb() { systemctl --job-mode=ignore-dependencies stop ovsdb-server } start_ovsdb() { systemctl --job-mode=ignore-dependencies start ovsdb-server } stop_forwarding() { systemctl --job-mode=ignore-dependencies stop ovs-vswitchd } start_forwarding() { systemctl --job-mode=ignore-dependencies start ovs-vswitchd } add_managers() { : } if [ "$1" = "force-reload-kmod" ]; then force_reload_kmod else restart fi exit 0 openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_share_openvswitch_scripts_sysconfig.template000066400000000000000000000015521514270232600317310ustar00rootroot00000000000000### Configuration options for openvswitch # Copyright (C) 2009, 2010, 2011 Nicira, Inc. # FORCE_COREFILES: If 'yes' then core files will be enabled. # FORCE_COREFILES=yes # OVSDB_SERVER_PRIORITY: "nice" priority at which to run ovsdb-server. # # OVSDB_SERVER_PRIORITY=-10 # VSWITCHD_PRIORITY: "nice" priority at which to run ovs-vswitchd. # VSWITCHD_PRIORITY=-10 # VSWITCHD_MLOCKALL: Whether to pass ovs-vswitchd the --mlockall option. # This option should be set to "yes" or "no". The default is "yes". # Enabling this option can avoid networking interruptions due to # system memory pressure in extraordinary situations, such as multiple # concurrent VM import operations. # VSWITCHD_MLOCKALL=yes # OVS_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, # a suitable place to specify --ovs-vswitchd-wrapper=valgrind. # OVS_CTL_OPTS= openvswitch-3.7.0~git20260211.8c6ebf8/rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template000066400000000000000000000022021514270232600334720ustar00rootroot00000000000000### Configuration options for openvswitch # # Enable core files. # This option should be set to "yes" or "no". The default is "yes". # --force-corefiles=yes # # Set "nice" priority at which to run ovsdb-server: # --ovsdb-server-priority=-10 # # Set "nice" priority at which to run ovsdb-vswitchd: # --ovs-vswitchd-priority=-10 # # Pass or not --mlockall option to ovs-vswitchd. # This option should be set to "yes" or "no". The default is "yes". # Enabling this option can avoid networking interruptions due to # system memory pressure in extraordinary situations, such as multiple # concurrent VM import operations. # --mlockall=yes # # Use valgrind: # --ovs-vswitchd-wrapper=valgrind # --ovsdb-server-wrapper=valgrind # # Specify additional options, for example to start with debug logs: # --ovs-vswitchd-options='-vconsole:dbg -vfile:dbg' # --ovsdb-server-options='-vconsole:dbg -vfile:dbg' # # Or to start with non-root IPsec config file: # --ovs-monitor-ipsec-options='--ipsec-conf=/etc/ipsec.d/ovs.conf --root-ipsec-conf=/etc/ipsec.conf' # OPTIONS="" # Uncomment and set the OVS User/Group value #OVS_USER_ID="openvswitch:openvswitch" openvswitch-3.7.0~git20260211.8c6ebf8/selinux/000077500000000000000000000000001514270232600204475ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/selinux/.gitignore000066400000000000000000000001351514270232600224360ustar00rootroot00000000000000openvswitch-custom.te openvswitch-custom.fc openvswitch-custom.pp openvswitch-custom.if tmp/ openvswitch-3.7.0~git20260211.8c6ebf8/selinux/automake.mk000066400000000000000000000012531514270232600226070ustar00rootroot00000000000000# Copyright (C) 2016 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ selinux/openvswitch-custom.fc.in \ selinux/openvswitch-custom.te.in PHONY: selinux-policy selinux-policy: selinux/openvswitch-custom.te selinux/openvswitch-custom.fc $(MAKE) -C selinux/ -f /usr/share/selinux/devel/Makefile CLEANFILES += \ selinux/openvswitch-custom.te \ selinux/openvswitch-custom.pp \ selinux/openvswitch-custom.fc \ selinux/openvswitch-custom.if openvswitch-3.7.0~git20260211.8c6ebf8/selinux/openvswitch-custom.fc.in000066400000000000000000000001461514270232600252500ustar00rootroot00000000000000@pkgdatadir@/scripts/ovs-kmod-ctl -- gen_context(system_u:object_r:openvswitch_load_module_exec_t,s0) openvswitch-3.7.0~git20260211.8c6ebf8/selinux/openvswitch-custom.te.in000066400000000000000000000164641514270232600253020ustar00rootroot00000000000000# SPDX-License-Identifier: Apache-2.0 module openvswitch-custom @VERSION@; require { role system_r; role object_r; type openvswitch_t; type openvswitch_rw_t; type openvswitch_tmp_t; type openvswitch_var_run_t; type bin_t; type ifconfig_exec_t; type init_t; type init_var_run_t; type insmod_exec_t; type kernel_t; type hostname_exec_t; type modules_conf_t; type modules_dep_t; type modules_object_t; type passwd_file_t; type plymouth_exec_t; type proc_t; type shell_exec_t; type sssd_t; type sssd_public_t; type sssd_var_lib_t; type sysfs_t; type systemd_unit_file_t; type tun_tap_device_t; @begin_dpdk@ type hugetlbfs_t; type svirt_t; type svirt_image_t; type svirt_tmpfs_t; type vfio_device_t; type zero_device_t; @end_dpdk@ class capability { dac_override audit_write net_broadcast net_raw }; class chr_file { write getattr read open ioctl map }; class dir { write remove_name add_name lock read getattr search open }; class fd { use }; class file { map write getattr read open execute execute_no_trans create unlink map entrypoint lock ioctl }; class fifo_file { getattr read write append ioctl lock open }; class filesystem getattr; class lnk_file { read open }; class netlink_audit_socket { create nlmsg_relay read write }; class netlink_netfilter_socket { create read write }; @begin_dpdk@ class netlink_rdma_socket { setopt getattr getopt bind create }; @end_dpdk@ class netlink_socket { setopt getopt create connect getattr write read }; class sock_file { write }; class system { module_load module_request }; class process { sigchld signull transition noatsecure siginh rlimitinh }; class unix_stream_socket { write getattr read connectto connect setopt getopt sendto accept bind recvfrom acceptfrom ioctl }; @begin_dpdk@ class sock_file { read append getattr open }; class tun_socket { relabelfrom relabelto create }; @end_dpdk@ } #============= Set up the transition domain ============= type openvswitch_load_module_exec_t; type openvswitch_load_module_t; domain_type(openvswitch_load_module_exec_t); domain_type(openvswitch_load_module_t); role object_r types openvswitch_load_module_exec_t; role system_r types openvswitch_load_module_t; domain_entry_file(openvswitch_load_module_t, openvswitch_load_module_exec_t); domtrans_pattern(openvswitch_t, openvswitch_load_module_exec_t, openvswitch_load_module_t); #============= openvswitch_t ============== allow openvswitch_t self:capability { dac_override audit_write net_broadcast net_raw }; allow openvswitch_t self:netlink_audit_socket { create nlmsg_relay read write }; allow openvswitch_t self:netlink_netfilter_socket { create read write }; @begin_dpdk@ allow openvswitch_t self:netlink_rdma_socket { setopt getattr getopt bind create }; @end_dpdk@ allow openvswitch_t self:netlink_socket { setopt getopt create connect getattr write read }; allow openvswitch_t hostname_exec_t:file { read getattr open execute execute_no_trans }; allow openvswitch_t ifconfig_exec_t:file { read getattr open execute execute_no_trans }; allow openvswitch_t openvswitch_rw_t:dir { write remove_name add_name lock read getattr open search }; allow openvswitch_t openvswitch_rw_t:file { write getattr read open execute execute_no_trans create unlink }; allow openvswitch_t openvswitch_tmp_t:file { execute execute_no_trans }; allow openvswitch_t openvswitch_tmp_t:unix_stream_socket { write getattr read connectto connect setopt getopt sendto accept bind recvfrom acceptfrom }; allow openvswitch_t openvswitch_var_run_t:dir { getattr read open search write remove_name add_name lock }; allow openvswitch_t openvswitch_var_run_t:file { map open read write getattr create unlink }; allow openvswitch_t tun_tap_device_t:chr_file { read write getattr open ioctl }; @begin_dpdk@ allow openvswitch_t hugetlbfs_t:dir { write remove_name add_name lock read }; allow openvswitch_t hugetlbfs_t:file { create unlink map }; allow openvswitch_t kernel_t:unix_stream_socket { write getattr read connectto connect setopt getopt sendto accept bind recvfrom acceptfrom }; allow openvswitch_t self:tun_socket { relabelfrom relabelto create }; allow openvswitch_t svirt_image_t:file { getattr read write }; allow openvswitch_t svirt_tmpfs_t:file { read write }; allow openvswitch_t svirt_tmpfs_t:sock_file { read write append getattr open }; allow openvswitch_t svirt_t:unix_stream_socket { connectto read write getattr sendto recvfrom setopt }; allow openvswitch_t vfio_device_t:chr_file { read write open ioctl getattr }; allow openvswitch_t zero_device_t:chr_file { read open getattr map }; @end_dpdk@ #============= Transition allows ============= type_transition openvswitch_t openvswitch_load_module_exec_t:process openvswitch_load_module_t; allow openvswitch_t openvswitch_load_module_exec_t:file { execute read open getattr }; allow openvswitch_t openvswitch_load_module_t:process transition; allow openvswitch_load_module_t bin_t:file { execute execute_no_trans map }; allow openvswitch_load_module_t init_t:unix_stream_socket { getattr ioctl read write }; allow openvswitch_load_module_t init_var_run_t:dir { getattr read open search }; allow openvswitch_load_module_t insmod_exec_t:file { execute execute_no_trans getattr map open read }; allow openvswitch_load_module_t kernel_t:system module_request; allow openvswitch_load_module_t modules_conf_t:dir { getattr open read search }; allow openvswitch_load_module_t modules_conf_t:file { getattr open read }; allow openvswitch_load_module_t modules_dep_t:file { getattr map open read }; allow openvswitch_load_module_t modules_object_t:file { map getattr open read }; allow openvswitch_load_module_t modules_object_t:dir { getattr open read search }; allow openvswitch_load_module_t openvswitch_load_module_exec_t:file { entrypoint }; allow openvswitch_load_module_t passwd_file_t:file { getattr open read }; allow openvswitch_load_module_t plymouth_exec_t:file { getattr read open execute execute_no_trans map }; allow openvswitch_load_module_t proc_t:file { getattr open read }; allow openvswitch_load_module_t self:system module_load; allow openvswitch_load_module_t self:process { siginh noatsecure rlimitinh siginh }; allow openvswitch_load_module_t shell_exec_t:file { map execute execute_no_trans read open getattr }; allow openvswitch_load_module_t sssd_public_t:dir { getattr open read search }; allow openvswitch_load_module_t sssd_public_t:file { getattr map open read }; allow openvswitch_load_module_t sssd_t:unix_stream_socket connectto; allow openvswitch_load_module_t sssd_var_lib_t:dir { getattr open read search }; allow openvswitch_load_module_t sssd_var_lib_t:sock_file write; allow openvswitch_load_module_t sysfs_t:dir { getattr open read search }; allow openvswitch_load_module_t sysfs_t:file { open read }; allow openvswitch_load_module_t sysfs_t:lnk_file { read open }; allow openvswitch_load_module_t systemd_unit_file_t:dir getattr; # no need to grant search permissions for this - and no need to emit # an error, either. dontaudit openvswitch_load_module_t openvswitch_var_run_t:dir { search }; kernel_load_module(openvswitch_load_module_t); openvswitch-3.7.0~git20260211.8c6ebf8/tests/000077500000000000000000000000001514270232600201225ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tests/.gitignore000066400000000000000000000026221514270232600221140ustar00rootroot00000000000000*.pem /Makefile /Makefile.in /atconfig /atlocal /clang-analyzer-results/ /idltest.c /idltest.h /idltest.ovsidl /ovstest /ovs-pki.log /ovsdb-cluster-testsuite /ovsdb-cluster-testsuite.dir/ /ovsdb-cluster-testsuite.log /pcap/ /pki/ /system-afxdp-testsuite /system-afxdp-testsuite.dir/ /system-afxdp-testsuite.log /system-dpdk-testsuite /system-dpdk-testsuite.dir/ /system-dpdk-testsuite.log /system-kmod-testsuite /system-kmod-testsuite.dir/ /system-kmod-testsuite.log /system-userspace-testsuite /system-userspace-testsuite.dir/ /system-userspace-testsuite.log /system-tso-testsuite /system-tso-testsuite.dir/ /system-tso-testsuite.log /system-offloads-testsuite /system-offloads-testsuite.dir/ /system-offloads-testsuite.log /system-dpdk-offloads-testsuite /system-dpdk-offloads-testsuite.dir/ /system-dpdk-offloads-testsuite.log /test-aes128 /test-atomic /test-bundle /test-byte-order /test-classifier /test-csum /test-flows /test-hash /test-heap /test-hindex /test-hmap /test-json /test-jsonrpc /test-list /test-lockfile /test-multipath /test-netflow /test-odp /test-ofpbuf /test-ovsdb /test-packets /test-raft /test-random /test-reconnect /test-rstp /test-sflow /test-sha1 /test-skiplist /test-stp /test-stream /test-strtok_r /test-timeval /test-type-props /test-unix-socket /test-util /test-uuid /test-uuidset /test-vconn /testsuite /testsuite.dir/ /testsuite.log /test-lib /testsuite.tmp.orig /valgrind.[0-9]* /valgrind/ openvswitch-3.7.0~git20260211.8c6ebf8/tests/aes128.at000066400000000000000000000076521514270232600214650ustar00rootroot00000000000000AT_BANNER([AES-128 unit tests]) m4_define([AES128_CHECK], [AT_SETUP([$1]) AT_KEYWORDS([aes128]) AT_CHECK([ovstest test-aes128 $2 $3], [0], [$4 ], []) AT_CLEANUP]) AES128_CHECK( [wikipedia test vector 1], [00010203050607080a0b0c0d0f101112], [506812a45f08c889b97f5980038b8359], [d8f532538289ef7d06b506a4fd5be9c9]) AES128_CHECK( [wikipedia test vector 2], [95A8EE8E89979B9EFDCBC6EB9797528D], [4ec137a426dabf8aa0beb8bc0c2b89d6], [d9b65d1232ba0199cdbd487b2a1fd646]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 0], [10a58869d74be5a374cf867cfb473859], [00000000000000000000000000000000], [6d251e6944b051e04eaa6fb4dbf78465]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 1], [caea65cdbb75e9169ecd22ebe6e54675], [00000000000000000000000000000000], [6e29201190152df4ee058139def610bb]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 2], [a2e2fa9baf7d20822ca9f0542f764a41], [00000000000000000000000000000000], [c3b44b95d9d2f25670eee9a0de099fa3]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 3], [b6364ac4e1de1e285eaf144a2415f7a0], [00000000000000000000000000000000], [5d9b05578fc944b3cf1ccf0e746cd581]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 4], [64cf9c7abc50b888af65f49d521944b2], [00000000000000000000000000000000], [f7efc89d5dba578104016ce5ad659c05]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 5], [47d6742eefcc0465dc96355e851b64d9], [00000000000000000000000000000000], [0306194f666d183624aa230a8b264ae7]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 6], [3eb39790678c56bee34bbcdeccf6cdb5], [00000000000000000000000000000000], [858075d536d79ccee571f7d7204b1f67]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 7], [64110a924f0743d500ccadae72c13427], [00000000000000000000000000000000], [35870c6a57e9e92314bcb8087cde72ce]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 8], [18d8126516f8a12ab1a36d9f04d68e51], [00000000000000000000000000000000], [6c68e9be5ec41e22c825b7c7affb4363]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 9], [f530357968578480b398a3c251cd1093], [00000000000000000000000000000000], [f5df39990fc688f1b07224cc03e86cea]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 10], [da84367f325d42d601b4326964802e8e], [00000000000000000000000000000000], [bba071bcb470f8f6586e5d3add18bc66]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 11], [e37b1c6aa2846f6fdb413f238b089f23], [00000000000000000000000000000000], [43c9f7e62f5d288bb27aa40ef8fe1ea8]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 12], [6c002b682483e0cabcc731c253be5674], [00000000000000000000000000000000], [3580d19cff44f1014a7c966a69059de5]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 13], [143ae8ed6555aba96110ab58893a8ae1], [00000000000000000000000000000000], [806da864dd29d48deafbe764f8202aef]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 14], [b69418a85332240dc82492353956ae0c], [00000000000000000000000000000000], [a303d940ded8f0baff6f75414cac5243]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 15], [71b5c08a1993e1362e4d0ce9b22b78d5], [00000000000000000000000000000000], [c2dabd117f8a3ecabfbb11d12194d9d0]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 16], [e234cdca2606b81f29408d5f6da21206], [00000000000000000000000000000000], [fff60a4740086b3b9c56195b98d91a7b]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 17], [13237c49074a3da078dc1d828bb78c6f], [00000000000000000000000000000000], [8146a08e2357f0caa30ca8c94d1a0544]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 18], [3071a2a48fe6cbd04f1a129098e308f8], [00000000000000000000000000000000], [4b98e06d356deb07ebb824e5713f7be3]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 19], [90f42ec0f68385f2ffc5dfc03a654dce], [00000000000000000000000000000000], [7a20a53d460fc9ce0423a7a0764c6cf2]) AES128_CHECK( [NIST KAT ECBKeySbox128e vector 20], [febd9a24d8b65c1c787d50a4ed3619a9], [00000000000000000000000000000000], [f4a70d8af877f9b02b4c40df57d45b17]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/alb.at000066400000000000000000000332171514270232600212140ustar00rootroot00000000000000AT_BANNER([PMD Auto Load Balance]) m4_divert_push([PREPARE_TESTS]) m4_divert_pop([PREPARE_TESTS]) m4_define([DUMMY_NUMA], [--dummy-numa="0,0"]) dnl CHECK_ALB_PARAM([param], [value], [+line]) dnl dnl Waits for ALB logs for 'param' in logs and checks if value matches dnl 'value'. Checking starts from line number 'line' in ovs-vswithd.log. m4_define([CHECK_ALB_PARAM], [ line_st=$3 if [[ -z "$line_st" ]] then line_st="+0" fi OVS_WAIT_UNTIL([tail -n $line_st ovs-vswitchd.log | grep "PMD auto load balance $1 set to"]) AT_CHECK([tail -n $line_st ovs-vswitchd.log dnl | sed -n "s#.*\(PMD auto load balance $1 set to.*\)#\1#p" | tail -1], [0], [dnl PMD auto load balance $1 set to $2 ]) ]) AT_SETUP([ALB - default state]) OVS_VSWITCHD_START OVS_WAIT_UNTIL([grep "PMD auto load balance is disabled" ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - enable/disable]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=1 \ -- set Open_vSwitch . other_config:pmd-cpu-mask=1 \ -- set open_vswitch . other_config:pmd-auto-lb="true"], [], [], [DUMMY_NUMA]) OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="false"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is disabled"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="true"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is enabled"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - min num PMD/RxQ]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=2 \ -- set Open_vSwitch . other_config:pmd-cpu-mask=1 \ -- set open_vswitch . other_config:pmd-auto-lb="true" \ -- set open_vswitch . other_config:pmd-auto-lb-load-threshold=0], [], [], [DUMMY_NUMA]) OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) # 1 pmds 2 rxqs get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance nothing to do, not enough non-isolated PMDs or RxQs."]) # 2 pmds 2 rxq get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x3]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "There are 2 pmd threads on numa node"]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance nothing to do, not enough non-isolated PMDs or RxQs."]) # 2 pmds 3 rxqs get_log_next_line_num AT_CHECK([ovs-vsctl set interface p0 options:n_rxq=3]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) # 1 pmds 3 rxqs get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "There are 1 pmd threads on numa node"]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance nothing to do, not enough non-isolated PMDs or RxQs."]) # Same config as last time get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance nothing to do, no configuration changes since last check."]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - cross-numa]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=4 \ -- set Interface p0 options:numa_id=0 \ -- set Open_vSwitch . other_config:pmd-cpu-mask=0x3 \ -- set open_vswitch . other_config:pmd-rxq-assign=group \ -- set open_vswitch . other_config:pmd-rxq-isolate=false \ -- set open_vswitch . other_config:pmd-auto-lb="true" \ -- set open_vswitch . other_config:pmd-auto-lb-load-threshold=0], [], [], [--dummy-numa 1,2,1,2]) OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) # no pinned rxqs - cross-numa pmd could change get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"]) # all pinned rxqs - cross-numa pmd will not change AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1,3:1']) get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "Variance improvement 0%."]) # mix of pinned (non-isolated) and non-pinned rxqs - cross-numa pmd could change AT_CHECK([ovs-vsctl remove Interface p0 other_config pmd-rxq-affinity]) AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1']) get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"]) # mix of pinned (isolated) and non-pinned rxqs - cross-numa pmd could change AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xf]) AT_CHECK([ovs-vsctl set Interface p0 options:n_rxq=6]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-isolate=true]) get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - PMD/RxQ assignment type]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=3 \ -- set Open_vSwitch . other_config:pmd-cpu-mask=3 \ -- set open_vswitch . other_config:pmd-auto-lb="true" \ -- set open_vswitch . other_config:pmd-auto-lb-load-threshold=0], [], [], [DUMMY_NUMA]) OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log]) # Change assignment type get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'roundrobin'"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="false"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is disabled"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="true"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is enabled"]) # Change back assignment type get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'cycles'"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="false"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is disabled"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="true"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is enabled"]) get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'group'"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="false"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is disabled"]) get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb="true"]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance is enabled"]) # Enable debug and test dryrun config checks AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) # Just to be sure it is still set to group AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) get_log_next_line_num ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'cycles'"]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'roundrobin'"]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance nothing to do, pmd-rxq-assign=roundrobin assignment type configured."]) get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "mode changed to: 'group'"]) ovs-appctl time/warp 600000 10000 OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - interval param]) OVS_VSWITCHD_START OVS_WAIT_UNTIL([grep "PMD auto load balance is disabled" ovs-vswitchd.log]) # Check default CHECK_ALB_PARAM([interval], [1 mins], []) # Set new value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="10"]) CHECK_ALB_PARAM([interval], [10 mins], [+$LINENUM]) # Set min value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="1"]) CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) # Set max value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="20000"]) CHECK_ALB_PARAM([interval], [20000 mins], [+$LINENUM]) # Set below min value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="0"]) CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) # Set new value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="100"]) CHECK_ALB_PARAM([interval], [100 mins], [+$LINENUM]) # Set above max value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="20001"]) CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) # Set new value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="1000"]) CHECK_ALB_PARAM([interval], [1000 mins], [+$LINENUM]) # Set Negative value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-rebal-interval="-1"]) CHECK_ALB_PARAM([interval], [1 mins], [+$LINENUM]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - improvement param]) OVS_VSWITCHD_START OVS_WAIT_UNTIL([grep "PMD auto load balance is disabled" ovs-vswitchd.log]) # Check default CHECK_ALB_PARAM([improvement threshold], [25%], []) # Set new value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-improvement-threshold=60]) CHECK_ALB_PARAM([improvement threshold], [60%], [+$LINENUM]) # Set min value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-improvement-threshold=0]) CHECK_ALB_PARAM([improvement threshold], [0%], [+$LINENUM]) # Set below min value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-improvement-threshold=-1]) CHECK_ALB_PARAM([improvement threshold], [25%], [+$LINENUM]) # Set max value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-improvement-threshold=100]) CHECK_ALB_PARAM([improvement threshold], [100%], [+$LINENUM]) # Set above max value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-improvement-threshold=101]) CHECK_ALB_PARAM([improvement threshold], [25%], [+$LINENUM]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ALB - load param]) OVS_VSWITCHD_START OVS_WAIT_UNTIL([grep "PMD auto load balance is disabled" ovs-vswitchd.log]) # Check default CHECK_ALB_PARAM([load threshold], [95%], []) # Set to new value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-load-threshold=70]) CHECK_ALB_PARAM([load threshold], [70%], [+$LINENUM]) # Set to min value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-load-threshold=0]) CHECK_ALB_PARAM([load threshold], [0%], [+$LINENUM]) # Set to below min get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-load-threshold=-1]) CHECK_ALB_PARAM([load threshold], [95%], [+$LINENUM]) # Set to max get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-load-threshold=100]) CHECK_ALB_PARAM([load threshold], [100%], [+$LINENUM]) # Set above max value get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-auto-lb-load-threshold=101]) CHECK_ALB_PARAM([load threshold], [95%], [+$LINENUM]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/appctl.py000066400000000000000000000076151514270232600217700ustar00rootroot00000000000000# Copyright (c) 2012 Nicira, Inc. # # 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. import argparse import sys import ovs.daemon import ovs.unixctl import ovs.unixctl.client import ovs.util import ovs.vlog from ovs.fatal_signal import signal_alarm def connect_to_target(target): error, str_result = ovs.unixctl.socket_name_from_target(target) if error: ovs.util.ovs_fatal(error, str_result) else: socket_name = str_result error, client = ovs.unixctl.client.UnixctlClient.create(socket_name) if error: ovs.util.ovs_fatal(error, "cannot connect to \"%s\"" % socket_name) return client def reply_to_string(reply, fmt=ovs.unixctl.UnixctlOutputFormat.TEXT, fmt_flags={}): if fmt == ovs.unixctl.UnixctlOutputFormat.TEXT: body = str(reply) else: body = ovs.json.to_string(reply, **fmt_flags) if body and not body.endswith("\n"): body += "\n" return body def main(): parser = argparse.ArgumentParser(description="Python Implementation of" " ovs-appctl.") parser.add_argument("-t", "--target", default="ovs-vswitchd", help="pidfile or socket to contact") parser.add_argument("command", metavar="COMMAND", help="Command to run.") parser.add_argument("argv", metavar="ARG", nargs="*", help="Arguments to the command.") parser.add_argument("-T", "--timeout", metavar="SECS", help="wait at most SECS seconds for a response") parser.add_argument("-f", "--format", metavar="FMT", help="Output format.", default="text", choices=[fmt.name.lower() for fmt in ovs.unixctl.UnixctlOutputFormat], type=str.lower) parser.add_argument("--pretty", action="store_true", help="Format the output in a more readable fashion." " Requires: --format json.") args = parser.parse_args() if (args.format != ovs.unixctl.UnixctlOutputFormat.JSON.name.lower() and args.pretty): ovs.util.ovs_fatal(0, "--pretty is supported with --format json only") signal_alarm(int(args.timeout) if args.timeout else None) ovs.vlog.Vlog.init() target = args.target format = ovs.unixctl.UnixctlOutputFormat[args.format.upper()] format_flags = dict(pretty=True) if args.pretty else {} client = connect_to_target(target) if format != ovs.unixctl.UnixctlOutputFormat.TEXT: err_no, error, _ = client.transact( "set-options", ["--format", args.format]) if err_no: ovs.util.ovs_fatal(err_no, "%s: transaction error" % target) elif error is not None: sys.stderr.write(reply_to_string(error)) ovs.util.ovs_error(0, "%s: server returned an error" % target) sys.exit(2) err_no, error, result = client.transact(args.command, args.argv) client.close() if err_no: ovs.util.ovs_fatal(err_no, "%s: transaction error" % target) elif error is not None: sys.stderr.write(reply_to_string(error)) ovs.util.ovs_error(0, "%s: server returned an error" % target) sys.exit(2) else: assert result is not None sys.stdout.write(reply_to_string(result, format, format_flags)) if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/tests/atlocal.in000066400000000000000000000151071514270232600220750ustar00rootroot00000000000000# -*- shell-script -*- HAVE_OPENSSL='@HAVE_OPENSSL@' HAVE_UNBOUND='@HAVE_UNBOUND@' HAVE_BACKTRACE='@HAVE_BACKTRACE@' HAVE_UNWIND='@HAVE_UNWIND@' EGREP='@EGREP@' PYTHON3='@PYTHON3@' CFLAGS='@CFLAGS@' HAVE_TCA_HTB_RATE64='@HAVE_TCA_HTB_RATE64@' HAVE_TCA_POLICE_PKTRATE64='@HAVE_TCA_POLICE_PKTRATE64@' # PYTHONCOERCECLOCALE=0 disables the Unicode compatibility warning on # stderr that breaks almost any Python3 test (PEP 0538) PYTHONCOERCECLOCALE=0 export PYTHONCOERCECLOCALE PYTHONPATH=$abs_top_srcdir/python:$abs_top_builddir/tests:$PYTHONPATH export PYTHONPATH PYTHONIOENCODING=utf_8 export PYTHONIOENCODING # PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo # files. Creating .py[co] works OK for any given version of Open # vSwitch, but it causes trouble if you switch from a version with # foo/__init__.py into an (older) version with plain foo.py, since # foo/__init__.pyc will cause Python to ignore foo.py. PYTHONDONTWRITEBYTECODE=yes export PYTHONDONTWRITEBYTECODE # Test whether the current working directory name is all ASCII # characters. Some Python code doesn't tolerate non-ASCII characters # in filenames very well, so if the current working directory is # non-ASCII then we skip the tests that run those programs. # # This would be just papering over a real problem, except that the # tests that we skip are launched from initscripts and thus normally # run in system directories with ASCII names. (This problem only came # up at all because the Debian autobuilders do build in a top-level # directory named /«BUILDDIR».) case `pwd | tr -d ' -~'` in '') non_ascii_cwd=false ;; *) non_ascii_cwd=true esac # Enable malloc debugging features. case `uname` in Linux) MALLOC_PERTURB_=165; export MALLOC_PERTURB_ MALLOC_CHECK_=2; export MALLOC_CHECK_ ;; FreeBSD) case `uname -r` in [789].*) MALLOC_CONF=AJ ;; 1[01].*) MALLOC_CONF=abort:true,junk:true,redzone:true ;; *) MALLOC_CONF=abort:true,junk:true ;; esac export MALLOC_CONF esac # The name of loopback interface case `uname` in Linux) LOOPBACK_INTERFACE=lo ;; FreeBSD|NetBSD) LOOPBACK_INTERFACE=lo0 ;; esac # Check for platform. case `uname` in MINGW*|MSYS*) IS_WIN32="yes" IS_BSD="no" ;; FreeBSD|NetBSD) IS_WIN32="no" IS_BSD="yes" ;; *) IS_WIN32="no" IS_BSD="no" ;; esac if test "$IS_WIN32" = yes; then # enables legacy windows unicode printing needed for Python3 compatibility # with the Python2 tests PYTHONLEGACYWINDOWSFSENCODING=true export PYTHONLEGACYWINDOWSFSENCODING PYTHONLEGACYWINDOWSSTDIO=true export PYTHONLEGACYWINDOWSSTDIO fi # Check for CPU architecture case `uname -m` in aarch64) IS_ARM64="yes" ;; *) IS_ARM64="no" ;; esac # Check whether to run IPv6 tests. $PYTHON3 -c ' import errno import socket import sys try: socket.socket(family=socket.AF_INET6).bind(("::1", 0, 0, 0)) except socket.error as e: if e.errno == errno.EAFNOSUPPORT or errno.EADDRNOTAVAIL: sys.exit(2) raise ' case $? in 0) HAVE_IPV6=yes ;; 2) HAVE_IPV6=no ;; *) echo "$0: unexpected error probing $PYTHON3 for IPv6 support" >&2 ;; esac # Look for a python L7 library 'LIB' in the system. If it is found, defines # HAVE_LIB="yes", otherwise HAVE_LIB="no" find_l7_lib() { set +x var=HAVE_`echo "$1" | tr '[a-z]' '[A-Z]'` result=$($PYTHON3 $abs_top_srcdir/tests/test-l7.py --help | grep "$1") if test "x${result}" != x; then eval ${var}="yes" else eval ${var}="no" fi } # HAVE_FTP find_l7_lib ftp # HAVE_TFTP find_l7_lib tftp # Look for a commnand in the system. If it is found, defines # HAVE_COMMAND="yes", otherwise HAVE_COMMAND="no". find_command() { which $1 > /dev/null 2>&1 status=$? var=HAVE_`echo "$1" | tr '[a-z]' '[A-Z]'` if test "$status" = "0"; then eval ${var}="yes" else eval ${var}="no" fi } # Set HAVE_NC find_command nc # Determine correct netcat option to quit on stdin EOF if nc --version 2>&1 | grep -q nmap.org; then # Nmap netcat NC_EOF_OPT="--send-only -w 5" else # BSD netcat NC_EOF_OPT="-q 1 -w 5" fi # Set HAVE_TC find_command tc # Set HAVE_TCPDUMP find_command tcpdump # Set HAVE_LFTP find_command lftp # Set HAVE_ETHTOOL find_command ethtool # Set HAVE_IPTABLES find_command iptables # Set HAVE_NFT find_command nft CURL_OPT="-g -v --max-time 1 --retry 2 --retry-delay 1 --connect-timeout 1" # Determine whether "diff" supports "normal" diffs. (busybox diff does not.) if echo xyzzy | diff /dev/null - | grep '^>' >/dev/null; then DIFF_SUPPORTS_NORMAL_FORMAT=yes else DIFF_SUPPORTS_NORMAL_FORMAT=no fi # Turn off proxies. unset http_proxy unset https_proxy unset ftp_proxy unset no_proxy unset HTTP_PROXY unset HTTPS_PROXY unset FTP_PROXY unset NO_PROXY # Prevent logging to syslog during tests. OVS_SYSLOG_METHOD=null export OVS_SYSLOG_METHOD # Set default timeout for control utils OVS_CTL_TIMEOUT=30 export OVS_CTL_TIMEOUT # Add some default flags to make the tests run better under Address # Sanitizer, if it was used for the build. # # We disable leak detection because otherwise minor leaks that don't # matter break everything. ASAN_OPTIONS=detect_leaks=0:abort_on_error=true:log_path=sanitizers:$ASAN_OPTIONS export ASAN_OPTIONS # Add some default flags for UndefinedBehaviorSanitizer, if it was used # for the build. UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=true:log_path=sanitizers:$UBSAN_OPTIONS export UBSAN_OPTIONS # Check whether Python test requirements are available. REQUIREMENT_PATH=$abs_top_srcdir/python/test_requirements.txt $PYTHON3 -c ' import os import pathlib import sys PACKAGING = True try: from packaging import requirements from importlib import metadata except ModuleNotFoundError: PACKAGING = False import pkg_resources with pathlib.Path(os.path.join(os.getenv("REQUIREMENT_PATH"))).open() as reqs: if PACKAGING: for req in reqs.readlines(): try: r = requirements.Requirement(req.strip()) if metadata.version(r.name) not in r.specifier: raise metadata.PackageNotFoundError except metadata.PackageNotFoundError: sys.exit(2) else: for req in pkg_resources.parse_requirements(reqs): try: pkg_resources.require(str(req)) except pkg_resources.DistributionNotFound: sys.exit(2) ' case $? in 0) HAVE_PYTEST=yes ;; 2) HAVE_PYTEST=no ;; *) HAVE_PYTEST=no echo "$0: unexpected error probing Python unit test requirements" >&2 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/tests/auto-attach.at000066400000000000000000000002401514270232600226560ustar00rootroot00000000000000AT_BANNER([auto-attach unit tests]) AT_SETUP([auto-attach - packets]) AT_KEYWORDS([auto-attach]) AT_CHECK(ovstest test-aa, [], [ignore], [ignore]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/automake.mk000066400000000000000000000536741514270232600223000ustar00rootroot00000000000000EXTRA_DIST += \ $(COMMON_MACROS_AT) \ $(TESTSUITE_AT) \ $(SYSTEM_TESTSUITE_AT) \ $(SYSTEM_KMOD_TESTSUITE_AT) \ $(SYSTEM_USERSPACE_TESTSUITE_AT) \ $(SYSTEM_TSO_TESTSUITE_AT) \ $(SYSTEM_AFXDP_TESTSUITE_AT) \ $(SYSTEM_OFFLOADS_TESTSUITE_AT) \ $(SYSTEM_DPDK_OFFLOADS_TESTSUITE_AT) \ $(SYSTEM_DPDK_TESTSUITE_AT) \ $(OVSDB_CLUSTER_TESTSUITE_AT) \ $(TESTSUITE) \ $(SYSTEM_KMOD_TESTSUITE) \ $(SYSTEM_USERSPACE_TESTSUITE) \ $(SYSTEM_TSO_TESTSUITE) \ $(SYSTEM_AFXDP_TESTSUITE) \ $(SYSTEM_OFFLOADS_TESTSUITE) \ $(SYSTEM_DPDK_OFFLOADS_TESTSUITE) \ $(SYSTEM_DPDK_TESTSUITE) \ $(OVSDB_CLUSTER_TESTSUITE) \ tests/atlocal.in \ $(srcdir)/package.m4 \ $(srcdir)/tests/test-dpparse.py \ $(srcdir)/tests/test-ofparse.py \ $(srcdir)/tests/testsuite \ $(srcdir)/tests/testsuite.patch COMMON_MACROS_AT = \ tests/ovsdb-macros.at \ tests/ovs-macros.at \ tests/ofproto-macros.at TESTSUITE_AT = \ tests/testsuite.at \ tests/completion.at \ tests/checkpatch.at \ tests/library.at \ tests/heap.at \ tests/bundle.at \ tests/classifier.at \ tests/check-structs.at \ tests/daemon.at \ tests/daemon-py.at \ tests/ofp-actions.at \ tests/ofp-print.at \ tests/ofp-util.at \ tests/ofp-errors.at \ tests/ovs-ofctl.at \ tests/fuzz-regression.at \ tests/fuzz-regression-list.at \ tests/odp.at \ tests/mpls-xlate.at \ tests/multipath.at \ tests/bfd.at \ tests/cfm.at \ tests/lacp.at \ tests/lib.at \ tests/learn.at \ tests/vconn.at \ tests/file_name.at \ tests/aes128.at \ tests/unixctl-py.at \ tests/uuid.at \ tests/json.at \ tests/jsonrpc.at \ tests/jsonrpc-py.at \ tests/pmd.at \ tests/alb.at \ tests/tunnel.at \ tests/tunnel-push-pop.at \ tests/tunnel-push-pop-ipv6.at \ tests/ovs-router.at \ tests/lockfile.at \ tests/reconnect.at \ tests/ovs-vswitchd.at \ tests/dpif-netdev.at \ tests/dpctl.at \ tests/ofproto-dpif.at \ tests/bridge.at \ tests/ofproto.at \ tests/netdev-type.at \ tests/ovsdb.at \ tests/ovsdb-log.at \ tests/ovsdb-types.at \ tests/ovsdb-data.at \ tests/ovsdb-column.at \ tests/ovsdb-table.at \ tests/ovsdb-row.at \ tests/ovsdb-schema.at \ tests/ovsdb-condition.at \ tests/ovsdb-mutation.at \ tests/ovsdb-query.at \ tests/ovsdb-transaction.at \ tests/ovsdb-execution.at \ tests/ovsdb-trigger.at \ tests/ovsdb-tool.at \ tests/ovsdb-replication.at \ tests/ovsdb-server.at \ tests/ovsdb-client.at \ tests/ovsdb-monitor.at \ tests/ovsdb-idl.at \ tests/ovsdb-lock.at \ tests/ovsdb-rbac.at \ tests/ovs-vsctl.at \ tests/pytest.at \ tests/stp.at \ tests/rstp.at \ tests/vlog.at \ tests/vtep-ctl.at \ tests/auto-attach.at \ tests/mcast-snooping.at \ tests/packet-type-aware.at \ tests/nsh.at \ tests/drop-stats.at \ tests/learning-switch.at EXTRA_DIST += $(FUZZ_REGRESSION_TESTS) FUZZ_REGRESSION_TESTS = \ tests/fuzz-regression/flow_extract_fuzzer-5112775280951296 \ tests/fuzz-regression/flow_extract_fuzzer-5457710546944000 \ tests/fuzz-regression/json_parser_fuzzer-4790908707930112 \ tests/fuzz-regression/ofp_print_fuzzer-4584019764183040 \ tests/fuzz-regression/ofp_print_fuzzer-4671928750702592 \ tests/fuzz-regression/ofp_print_fuzzer-4730143510626304 \ tests/fuzz-regression/ofp_print_fuzzer-4854119633256448 \ tests/fuzz-regression/ofp_print_fuzzer-5070973479944192 \ tests/fuzz-regression/ofp_print_fuzzer-5072291707748352 \ tests/fuzz-regression/ofp_print_fuzzer-5147430386401280 \ tests/fuzz-regression/ofp_print_fuzzer-5168455220199424 \ tests/fuzz-regression/ofp_print_fuzzer-5190507327127552 \ tests/fuzz-regression/ofp_print_fuzzer-5204186701496320 \ tests/fuzz-regression/ofp_print_fuzzer-5394482341085184 \ tests/fuzz-regression/ofp_print_fuzzer-5395207246839808 \ tests/fuzz-regression/ofp_print_fuzzer-5647458888581120 \ tests/fuzz-regression/ofp_print_fuzzer-5674119268925440 \ tests/fuzz-regression/ofp_print_fuzzer-5674419757252608 \ tests/fuzz-regression/ofp_print_fuzzer-5677588436484096 \ tests/fuzz-regression/ofp_print_fuzzer-5706562554298368 \ tests/fuzz-regression/ofp_print_fuzzer-5722747668791296 \ tests/fuzz-regression/ofp_print_fuzzer-6285128790704128 \ tests/fuzz-regression/ofp_print_fuzzer-6470117922701312 \ tests/fuzz-regression/ofp_print_fuzzer-6502620041576448 \ tests/fuzz-regression/ofp_print_fuzzer-6540965472632832 $(srcdir)/tests/fuzz-regression-list.at: tests/automake.mk $(AM_V_GEN)for name in $(FUZZ_REGRESSION_TESTS); do \ basename=`echo $$name | sed 's,^.*/,,'`; \ echo "TEST_FUZZ_REGRESSION([$$basename])"; \ done > $@.tmp && mv $@.tmp $@ OVSDB_CLUSTER_TESTSUITE_AT = \ tests/ovsdb-cluster-testsuite.at \ tests/ovsdb-execution.at \ tests/ovsdb-cluster.at SYSTEM_KMOD_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-kmod-testsuite.at \ tests/system-kmod-macros.at SYSTEM_USERSPACE_TESTSUITE_AT = \ tests/system-userspace-testsuite.at \ tests/system-userspace-macros.at \ tests/system-userspace-packet-type-aware.at \ tests/system-route.at SYSTEM_TSO_TESTSUITE_AT = \ tests/system-tso-testsuite.at \ tests/system-tap.at \ tests/system-tso-macros.at SYSTEM_AFXDP_TESTSUITE_AT = \ tests/system-userspace-macros.at \ tests/system-afxdp-testsuite.at \ tests/system-afxdp-macros.at \ tests/system-afxdp.at SYSTEM_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-layer3-tunnels.at \ tests/system-traffic.at \ tests/system-ipsec.at \ tests/system-interface.at SYSTEM_OFFLOADS_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-offloads-traffic.at \ tests/system-offloads-testsuite.at \ tests/system-offloads-testsuite-macros.at SYSTEM_DPDK_OFFLOADS_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-dpdk-macros.at \ tests/system-dpdk-offloads.at \ tests/system-dpdk-offloads-macros.at \ tests/system-dpdk-offloads-testsuite.at SYSTEM_DPDK_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-dpdk-macros.at \ tests/system-dpdk-testsuite.at \ tests/system-dpdk.at check_SCRIPTS += tests/atlocal TESTSUITE = $(srcdir)/tests/testsuite TESTSUITE_PATCH = $(srcdir)/tests/testsuite.patch TESTSUITE_DIR = $(abs_top_builddir)/tests/testsuite.dir SYSTEM_KMOD_TESTSUITE = $(srcdir)/tests/system-kmod-testsuite SYSTEM_USERSPACE_TESTSUITE = $(srcdir)/tests/system-userspace-testsuite SYSTEM_TSO_TESTSUITE = $(srcdir)/tests/system-tso-testsuite SYSTEM_AFXDP_TESTSUITE = $(srcdir)/tests/system-afxdp-testsuite SYSTEM_OFFLOADS_TESTSUITE = $(srcdir)/tests/system-offloads-testsuite SYSTEM_DPDK_OFFLOADS_TESTSUITE = $(srcdir)/tests/system-dpdk-offloads-testsuite SYSTEM_DPDK_TESTSUITE = $(srcdir)/tests/system-dpdk-testsuite OVSDB_CLUSTER_TESTSUITE = $(srcdir)/tests/ovsdb-cluster-testsuite DISTCLEANFILES += tests/atconfig tests/atlocal AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:ipsec:$(PTHREAD_WIN32_DIR_DLL):$(SSL_DIR) check-local: set $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH); \ "$$@" $(TESTSUITEFLAGS) || \ (test -z "$$(find $(TESTSUITE_DIR) -name 'sanitizers.*')" && \ test X'$(RECHECK)' = Xyes && "$$@" --recheck) # Python Coverage support. # Requires coverage.py http://nedbatchelder.com/code/coverage/. COVERAGE = coverage COVERAGE_FILE='$(abs_srcdir)/.coverage' check-pycov: all clean-pycov PYTHONDONTWRITEBYTECODE=yes COVERAGE_FILE=$(COVERAGE_FILE) PYTHON3='$(COVERAGE) run -p' $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS) @cd $(srcdir) && $(COVERAGE) combine && COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) annotate @echo @echo '----------------------------------------------------------------------' @echo 'Annotated coverage source has the ",cover" extension.' @echo '----------------------------------------------------------------------' @echo @COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) report # lcov support # Requires build with --enable-coverage and lcov/genhtml in $PATH CLEAN_LOCAL += clean-lcov clean-lcov: rm -fr tests/lcov LCOV_OPTS = -b $(abs_top_builddir) -d $(abs_top_builddir) -q -c --rc lcov_branch_coverage=1 GENHTML_OPTS = -q --branch-coverage --num-spaces 4 check-lcov: all $(check_DATA) clean-lcov find . -name '*.gcda' | xargs -n1 rm -f -set $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH); \ "$$@" $(TESTSUITEFLAGS) || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) $(MKDIR_P) tests/lcov lcov $(LCOV_OPTS) -o tests/lcov/coverage.info genhtml $(GENHTML_OPTS) -o tests/lcov tests/lcov/coverage.info @echo "coverage report generated at tests/lcov/index.html" # valgrind support valgrind_wrappers = \ tests/valgrind/ovs-appctl \ tests/valgrind/ovs-ofctl \ tests/valgrind/ovs-vsctl \ tests/valgrind/ovs-vswitchd \ tests/valgrind/ovsdb-client \ tests/valgrind/ovsdb-server \ tests/valgrind/ovsdb-tool \ tests/valgrind/ovstest \ tests/valgrind/test-ovsdb \ tests/valgrind/test-skiplist \ tests/valgrind/test-strtok_r \ tests/valgrind/test-type-props $(valgrind_wrappers): tests/valgrind-wrapper.in @$(MKDIR_P) tests/valgrind $(AM_V_GEN) sed -e 's,[@]wrap_program[@],$@,' \ $(top_srcdir)/tests/valgrind-wrapper.in > $@.tmp && \ chmod +x $@.tmp && \ mv $@.tmp $@ CLEANFILES += $(valgrind_wrappers) EXTRA_DIST += tests/valgrind-wrapper.in VALGRIND = valgrind --log-file=valgrind.%p \ --leak-check=full --track-origins=yes \ --suppressions=$(abs_top_srcdir)/tests/glibc.supp \ --suppressions=$(abs_top_srcdir)/tests/openssl.supp --num-callers=20 HELGRIND = valgrind --log-file=helgrind.%p --tool=helgrind \ --suppressions=$(abs_top_srcdir)/tests/glibc.supp \ --suppressions=$(abs_top_srcdir)/tests/openssl.supp --num-callers=20 EXTRA_DIST += tests/glibc.supp tests/openssl.supp check-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-ovsdb-cluster-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(OVSDB_CLUSTER_TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/ovsdb-cluster-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-kernel-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_KMOD_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-kmod-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-userspace-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_USERSPACE_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-userspace-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-afxdp-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_AFXDP_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-afxdp-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-offloads-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_OFFLOADS_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-offloads-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-dpdk-offloads-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_DPDK_OFFLOADS_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-dpdk-offloads-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-tso-valgrind: all $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(SYSTEM_TSO_TESTSUITE)' -C tests VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) -j1 @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/system-tso-testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' check-helgrind: all $(valgrind_wrappers) $(check_DATA) -$(SHELL) '$(TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(HELGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) # OFTest support. check-oftest: all $(AM_V_at)srcdir='$(srcdir)' $(SHELL) $(srcdir)/tests/run-oftest EXTRA_DIST += tests/run-oftest # Ryu support. check-ryu: all $(AM_V_at)srcdir='$(srcdir)' $(SHELL) $(srcdir)/tests/run-ryu EXTRA_DIST += tests/run-ryu # Run kmod tests. Assume kernel modules has been installed or linked into the kernel check-kernel: all set $(SHELL) '$(SYSTEM_KMOD_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-system-userspace: all set $(SHELL) '$(SYSTEM_USERSPACE_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-system-tso: all set $(SHELL) '$(SYSTEM_TSO_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-afxdp: all set $(SHELL) '$(SYSTEM_AFXDP_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)' $(TESTSUITEFLAGS) -j1; \ "$$@" || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-offloads: all set $(SHELL) '$(SYSTEM_OFFLOADS_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-dpdk-offloads: all set $(SHELL) '$(SYSTEM_DPDK_OFFLOADS_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-dpdk: all set $(SHELL) '$(SYSTEM_DPDK_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) clean-local: test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean # Run OVSDB cluster tests. check-ovsdb-cluster: all set $(SHELL) '$(OVSDB_CLUSTER_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)'; \ "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) AUTOTEST = $(AUTOM4TE) --language=autotest if WIN32 $(TESTSUITE): package.m4 $(TESTSUITE_AT) $(COMMON_MACROS_AT) $(TESTSUITE_PATCH) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o testsuite.tmp $@.at patch -p0 testsuite.tmp $(TESTSUITE_PATCH) $(AM_V_at)mv testsuite.tmp $@ else $(TESTSUITE): package.m4 $(TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ endif $(SYSTEM_KMOD_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_KMOD_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_USERSPACE_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_USERSPACE_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_TSO_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_TSO_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_AFXDP_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_AFXDP_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_OFFLOADS_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_OFFLOADS_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_DPDK_OFFLOADS_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_DPDK_OFFLOADS_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_DPDK_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_DPDK_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(OVSDB_CLUSTER_TESTSUITE): package.m4 $(OVSDB_CLUSTER_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac $(AM_V_GEN):;{ \ echo '# Signature of the current package.' && \ echo 'm4_define([AT_PACKAGE_NAME], [$(PACKAGE_NAME)])' && \ echo 'm4_define([AT_PACKAGE_TARNAME], [$(PACKAGE_TARNAME)])' && \ echo 'm4_define([AT_PACKAGE_VERSION], [$(PACKAGE_VERSION)])' && \ echo 'm4_define([AT_PACKAGE_STRING], [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT], [$(PACKAGE_BUGREPORT)])'; \ } >'$(srcdir)/package.m4' noinst_PROGRAMS += tests/test-ovsdb tests_test_ovsdb_SOURCES = tests/test-ovsdb.c nodist_tests_test_ovsdb_SOURCES = tests/idltest.c tests/idltest.h tests_test_ovsdb_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la noinst_PROGRAMS += tests/test-lib tests_test_lib_SOURCES = \ tests/test-lib.c tests_test_lib_LDADD = lib/libopenvswitch.la # idltest schema and IDL OVSIDL_BUILT += tests/idltest.c tests/idltest.h tests/idltest.ovsidl IDLTEST_IDL_FILES = tests/idltest.ovsschema tests/idltest.ann EXTRA_DIST += $(IDLTEST_IDL_FILES) tests/idltest2.ovsschema tests/idltest.ovsidl: $(IDLTEST_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) -C $(srcdir) annotate $(IDLTEST_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ tests/idltest.c: tests/idltest.h noinst_PROGRAMS += tests/ovstest tests_ovstest_SOURCES = \ tests/ovstest.c \ tests/ovstest.h \ tests/test-aes128.c \ tests/test-atomic.c \ tests/test-barrier.c \ tests/test-bundle.c \ tests/test-byte-order.c \ tests/test-byteq.c \ tests/test-classifier.c \ tests/test-ccmap.c \ tests/test-cmap.c \ tests/test-conntrack.c \ tests/test-cooperative-multitasking.c \ tests/test-csum.c \ tests/test-flows.c \ tests/test-hash.c \ tests/test-heap.c \ tests/test-hindex.c \ tests/test-hmap.c \ tests/test-id-fpool.c \ tests/test-json.c \ tests/test-jsonrpc.c \ tests/test-list.c \ tests/test-lockfile.c \ tests/test-multipath.c \ tests/test-mpsc-queue.c \ tests/test-netflow.c \ tests/test-odp.c \ tests/test-ofpbuf.c \ tests/test-packets.c \ tests/test-random.c \ tests/test-rcu.c \ tests/test-rculist.c \ tests/test-reconnect.c \ tests/test-rstp.c \ tests/test-sflow.c \ tests/test-sha1.c \ tests/test-skiplist.c \ tests/test-stp.c \ tests/test-unixctl.c \ tests/test-util.c \ tests/test-uuid.c \ tests/test-uuidset.c \ tests/test-bitmap.c \ tests/test-vconn.c \ tests/test-aa.c \ tests/test-stopwatch.c if !WIN32 tests_ovstest_SOURCES += \ tests/test-unix-socket.c endif if LINUX tests_ovstest_SOURCES += \ tests/test-lib-route-table.c \ tests/test-netlink-conntrack.c \ tests/test-netlink-policy.c \ tests/test-psample.c endif tests_ovstest_LDADD = lib/libopenvswitch.la noinst_PROGRAMS += tests/test-stream tests_test_stream_SOURCES = tests/test-stream.c tests_test_stream_LDADD = lib/libopenvswitch.la noinst_PROGRAMS += tests/test-strtok_r tests_test_strtok_r_SOURCES = tests/test-strtok_r.c noinst_PROGRAMS += tests/test-type-props tests_test_type_props_SOURCES = tests/test-type-props.c # Python tests. CHECK_PYFILES = \ tests/appctl.py \ tests/flowgen.py \ tests/genpkts.py \ tests/ovsdb-monitor-sort.py \ tests/system-dpdk-find-device.py \ tests/test-daemon.py \ tests/test-dpparse.py \ tests/test-json.py \ tests/test-jsonrpc.py \ tests/test-l7.py \ tests/test-ofparse.py \ tests/test-ovsdb.py \ tests/test-reconnect.py \ tests/test-stream.py \ tests/test-unix-socket.py \ tests/test-unixctl.py \ tests/test-vlog.py \ tests/uuidfilt.py \ tests/sendpkt.py EXTRA_DIST += $(CHECK_PYFILES) PYCOV_CLEAN_FILES += $(CHECK_PYFILES:.py=.py,cover) .coverage FLAKE8_PYFILES += $(CHECK_PYFILES) if HAVE_OPENSSL TESTPKI_FILES = \ tests/testpki-cacert.pem \ tests/testpki-cert.pem \ tests/testpki-privkey.pem \ tests/testpki-req.pem \ tests/testpki-cert2.pem \ tests/testpki-privkey2.pem \ tests/testpki-req2.pem check_DATA += $(TESTPKI_FILES) CLEANFILES += $(TESTPKI_FILES) tests/testpki-cacert.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/switchca/cacert.pem $@ tests/testpki-cert.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test-cert.pem $@ tests/testpki-req.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test-req.pem $@ tests/testpki-privkey.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test-privkey.pem $@ tests/testpki-cert2.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test2-cert.pem $@ tests/testpki-req2.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test2-req.pem $@ tests/testpki-privkey2.pem: tests/pki/stamp $(AM_V_GEN)cp tests/pki/test2-privkey.pem $@ OVS_PKI = $(SHELL) $(srcdir)/utilities/ovs-pki.in --dir=tests/pki --log=tests/ovs-pki.log tests/pki/stamp: $(AM_V_at)rm -f tests/pki/stamp $(AM_V_at)rm -rf tests/pki $(AM_V_GEN)$(OVS_PKI) init && \ $(OVS_PKI) req+sign tests/pki/test && \ $(OVS_PKI) req+sign tests/pki/test2 && \ : > tests/pki/stamp CLEANFILES += tests/ovs-pki.log CLEAN_LOCAL += clean-pki clean-pki: rm -f tests/pki/stamp rm -rf tests/pki endif include tests/oss-fuzz/automake.mk openvswitch-3.7.0~git20260211.8c6ebf8/tests/bfd.at000066400000000000000000001435751514270232600212220ustar00rootroot00000000000000AT_BANNER([bfd]) m4_define([BFD_CHECK], [ AT_CHECK([ovs-appctl bfd/show $1 | sed -e '/Time:/d' | sed -e '/Discriminator/d' | sed -e '/Interval:/d'| sed -e '/Multiplier/d'],[0], [dnl Forwarding: $2 Concatenated Path Down: $3 Local Flags: $4 Local Session State: $5 Local Diagnostic: $6 Remote Flags: $7 Remote Session State: $8 Remote Diagnostic: $9 ]) ]) m4_define([BFD_CHECK_TX], [ AT_CHECK([ovs-appctl bfd/show $1 | sed -n '/TX Interval/p'],[0], [dnl TX Interval: Approx $2 Local Minimum TX Interval: $3 Remote Minimum TX Interval: $4 ]) ]) m4_define([BFD_CHECK_RX], [ AT_CHECK([ovs-appctl bfd/show $1 | sed -n '/RX Interval/p'],[0], [dnl RX Interval: Approx $2 Local Minimum RX Interval: $3 Remote Minimum RX Interval: $4 ]) ]) m4_define([BFD_VSCTL_LIST_IFACE], [ AT_CHECK([ovs-vsctl list interface $1 | sed -n $2],[0], [dnl $3 ]) ]) m4_define([BFD_CHECK_MULT], [ AT_CHECK([ovs-appctl bfd/show $1 | sed -n '/Detect Multiplier/p'],[0], [dnl Detect Multiplier: $2 Remote Detect Multiplier: $3 ]) ]) AT_SETUP([bfd - basic config on different bridges]) #Create 2 bridges connected by patch ports and enable BFD OVS_VSWITCHD_START( [add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 -- \ set Interface p0 bfd:enable=true -- \ set Interface p1 bfd:enable=true ]) ovs-appctl time/stop ovs-appctl time/warp 4100 100 #Verify that BFD has been enabled on both interfaces. BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) AT_CHECK([ ovs-vsctl set interface p0 bfd:enable=false]) ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) AT_CHECK([ ovs-vsctl set interface p0 bfd:enable=true]) ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired]) ovs-vsctl del-br br0 AT_CHECK([ovs-appctl bfd/show p0], [2],[ignore], [no such bfd object ovs-appctl: ovs-vswitchd: server returned an error ]) ovs-vsctl del-br br1 #Check that the entries are gone. AT_CHECK([ovs-appctl bfd/show p1], [2],[ignore], [no such bfd object ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - Verify tunnel down detection]) #Create 3 bridges - br-bfd0, br-bfd1 and br-sw which is midway between the two. br-sw is #connected to br-bfd0 and br-bfd1 through patch ports p0-sw and p1-sw. Enable BFD on #interfaces in br-bfd0 and br-bfd1. When br-sw is dropping all packets, BFD should detect # that the tunnel is down, and come back up when br-sw is working fine. OVS_VSWITCHD_START( [add-br br-bfd0 -- \ set bridge br-bfd0 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-br br-bfd1 -- \ set bridge br-bfd1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:57:00:00 -- \ add-br br-sw -- \ set bridge br-sw datapath-type=dummy \ other-config:hwaddr=aa:55:aa:58:00:00 -- \ add-port br-sw p1-sw -- set Interface p1-sw type=patch \ options:peer=p1 ofport_request=2 -- \ add-port br-sw p0-sw -- set Interface p0-sw type=patch \ options:peer=p0 ofport_request=1 -- \ add-port br-bfd1 p1 -- set Interface p1 type=patch \ options:peer=p1-sw bfd:enable=true -- \ add-port br-bfd0 p0 -- set Interface p0 type=patch \ options:peer=p0-sw bfd:enable=true --]) ovs-appctl time/stop #Create 2 bridges connected by patch ports and enable BFD AT_CHECK([ovs-ofctl add-flow br-sw 'priority=0,actions=NORMAL']) #Verify that BFD is enabled. ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) #Drop all packets in the br-sw bridge so that the tunnel is down. AT_CHECK([ ovs-ofctl add-flow br-sw 'priority=5,actions=drop' ]) ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) #Delete the added flow AT_CHECK([ovs-ofctl del-flows br-sw], [0]) AT_CHECK([ovs-ofctl add-flow br-sw 'priority=0,actions=NORMAL']) #Verify that BFD is back up again. ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [Control Detection Time Expired]) BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [Control Detection Time Expired]) #Now, Verify one-side tunnel down detection #When br-sw is dropping packets from one end, BFD should detect # that the tunnel is down, and come back up when br-sw is working fine. #Bring down the br-bfd1 - br-sw link. So BFD packets will be sent from p0, # but not received by p1. p0 will receive all BFD packets from p1. AT_CHECK([ ovs-ofctl add-flow br-sw 'in_port=1,priority=5,actions=drop']) ovs-appctl time/warp 4100 100 # Make sure p1 BFD state is down since it received no BFD packets. BFD_CHECK([p1], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) ovs-appctl time/warp 4100 100 # p0 will be in init state once it receives "down" BFD message from p1. BFD_CHECK([p0], [false], [false], [none], [init], [Neighbor Signaled Session Down], [none], [down], [Control Detection Time Expired]) AT_CHECK([ovs-ofctl del-flows br-sw]) AT_CHECK([ovs-ofctl add-flow br-sw 'priority=0,actions=NORMAL']) #Ensure that BFD is back up again. ovs-appctl time/warp 1100 100 #Bring down the br-bfd0 - br-sw link AT_CHECK([ ovs-ofctl add-flow br-sw 'in_port=2,priority=5,actions=drop']) ovs-appctl time/warp 4100 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [false], [false], [none], [init], [Neighbor Signaled Session Down], [none], [down], [Control Detection Time Expired]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - concatenated path down]) #Create 2 bridges connected by patch ports and enable BFD OVS_VSWITCHD_START() ovs-appctl time/stop AT_CHECK([ ovs-vsctl -- add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 ]) AT_CHECK([ ovs-vsctl -- add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ]) AT_CHECK([ ovs-vsctl -- add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ]) AT_CHECK([ ovs-vsctl -- set interface p0 bfd:enable=true ]) AT_CHECK([ ovs-vsctl -- set interface p1 bfd:enable=true ]) ovs-appctl time/warp 4100 100 #Verify that BFD has been enabled on both interfaces. BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) #Set cpath_down to true on one interface, make sure the remote interface updates its values. AT_CHECK([ovs-vsctl set interface p0 bfd:cpath_down=true]) ovs-appctl time/warp 4100 100 BFD_CHECK([p1], [false], [false], [none], [up], [No Diagnostic], [none], [up], [Concatenated Path Down]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - Edit the Min Tx/Rx values]) #Create 2 bridges connected by patch ports and enable BFD OVS_VSWITCHD_START() ovs-appctl time/stop AT_CHECK([ ovs-vsctl -- add-br br1 -- \ set bridge br1 datapath-type=dummy ]) AT_CHECK([ ovs-vsctl -- add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ]) AT_CHECK([ ovs-vsctl -- add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ]) AT_CHECK([ ovs-vsctl -- set interface p0 bfd:enable=true ]) AT_CHECK([ ovs-vsctl -- set interface p1 bfd:enable=true ]) ovs-appctl time/warp 3100 100 #Verify that BFD has been enabled on both interfaces. BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) #Edit the min Tx value. AT_CHECK([ovs-vsctl set interface p0 bfd:min_tx=200]) ovs-appctl time/warp 2100 100 BFD_CHECK_TX([p0], [1000ms], [200ms], [100ms]) BFD_CHECK_TX([p1], [1000ms], [100ms], [200ms]) #Edit the min Rx value. AT_CHECK([ovs-vsctl set interface p1 bfd:min_rx=300]) ovs-appctl time/warp 2100 100 BFD_CHECK_RX([p1], [300ms], [300ms], [1000ms]) BFD_CHECK_RX([p0], [1000ms], [1000ms], [300ms]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - check_tnl_key]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=2.2.2.2 options:key=1 ofport_request=1 -- \ set interface p1 bfd:enable=true -- \ set bridge br0 fail-mode=standalone]) # by default check_tnl_key is false. so we should process a bfd packet with tun_id=1. AT_CHECK([ovs-appctl ofproto/trace --l7-len 0 ovs-dummy 'tunnel(tun_id=0x1,src=2.2.2.2,dst=2.2.2.1,tos=0x0,ttl=64,tp_src=0,tp_dst=0,flags(key)),in_port(1),skb_mark(0/0),eth(src=00:11:22:33:44:55,dst=00:23:20:00:00:01),eth_type(0x0800),ipv4(src=169.254.1.0/0.0.0.0,dst=169.254.1.1/0.0.0.0,proto=17/0xff,tos=0/0,ttl=255/0,frag=no),udp(src=49152/0,dst=3784/0xffff)' -generate], [0], [stdout]) # check that the packet should be handled as BFD packet. AT_CHECK([tail -2 stdout], [0], [dnl This flow is handled by the userspace slow path because it: - Consists of BFD packets. ], []) # turn on the check_tnl_key. AT_CHECK([ovs-vsctl set interface p1 bfd:check_tnl_key=true]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x1,src=2.2.2.2,dst=2.2.2.1,tos=0x0,ttl=64,tp_src=0,tp_dst=0,flags(key)),in_port(1),skb_mark(0/0),eth(src=00:11:22:33:44:55,dst=00:23:20:00:00:01),eth_type(0x0800),ipv4(src=169.254.1.0/0.0.0.0,dst=169.254.1.1/0.0.0.0,proto=17/0xff,tos=0/0,ttl=255/0,frag=no),udp(src=49152/0,dst=3784/0xffff)' -generate], [0], [stdout]) # check that the packet should be handled as normal packet. AT_CHECK([tail -1 stdout], [0],[dnl Datapath actions: 100 ], []) # set the tunnel key to 0. AT_CHECK([ovs-vsctl set interface p1 options:key=0]) AT_CHECK([ovs-appctl ofproto/trace --l7-len 0 ovs-dummy 'tunnel(tun_id=0x0,src=2.2.2.2,dst=2.2.2.1,tos=0x0,ttl=64,tp_src=0,tp_dst=0,flags(key)),in_port(1),skb_mark(0/0),eth(src=00:11:22:33:44:55,dst=00:23:20:00:00:01),eth_type(0x0800),ipv4(src=169.254.1.0/0.0.0.0,dst=169.254.1.1/0.0.0.0,proto=17/0xff,tos=0/0,ttl=255/0,frag=no),udp(src=49152/0,dst=3784/0xffff)' -generate], [0], [stdout]) # check that the packet should be handled as BFD packet. AT_CHECK([tail -2 stdout], [0], [dnl This flow is handled by the userspace slow path because it: - Consists of BFD packets. ], []) OVS_VSWITCHD_STOP AT_CLEANUP # Tests below are for bfd decay features. AT_SETUP([bfd - bfd decay]) AT_SKIP_IF([test "$IS_ARM64" = "yes"]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 -- \ set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500]) ovs-appctl time/stop # wait for a while to stablize everything. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) # Test-1 BFD decay: decay to decay_min_rx AT_CHECK([ovs-vsctl set interface p0 bfd:decay_min_rx=3000]) # bfd:decay_min_rx is set to 3000ms after the local state of p0 goes up, # so for the first 2000ms, there should be no change. ovs-appctl time/warp 2000 500 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) # advance the clock by 5000ms. ovs-appctl time/warp 5000 500 # now, min_rx should decay to 3000ms. BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms]) # advance clock by 5000ms and check the flags are all 'none'. ovs-appctl time/warp 5000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms]) # End of Test-1 ############################################################### # Test-2 BFD decay: go back to min_rx when there is traffic # receive packet at 1/100ms rate for 5000ms. for i in `seq 0 49` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done # after a decay interval (3000ms), the p0 min_rx will go back to # min_rx. BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) # End of Test-2 ############################################################### # Test-3 BFD decay: set decay_min_rx to 1000ms. # this should firstly reset the min_rx and then re-decay to 1000ms. AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=1000]) # advance the clock by 10000ms, decay should have happened. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms]) # End of Test-3 ############################################################### # Test-4 BFD decay: set decay_min_rx to 0 to disable bfd decay. AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=0]) # advance the clock by 5000ms. ovs-appctl time/warp 10000 500 # min_rx is reset. BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) for i in `seq 0 20` do ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) done # End of Test-4 ################################################################ # Test-5 BFD decay: rmt_min_tx is greater than decay_min_rx AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=3000 -- set interface p1 bfd:min_tx=5000]) # advance the clock by 10000ms to stable everything. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) # p0 rx should show 5000ms even if it is in decay. BFD_CHECK_TX([p0], [500ms], [300ms], [5000ms]) BFD_CHECK_RX([p0], [5000ms], [3000ms], [500ms]) # then, there should be no change of status, for i in `seq 0 19` do ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [5000ms]) BFD_CHECK_RX([p0], [5000ms], [3000ms], [500ms]) done # reset the p1's min_tx to 500ms. AT_CHECK([ovs-vsctl set Interface p1 bfd:min_tx=500]) # advance the clock by 20000ms to stable everything. # since p0 has been in decay, now the RX will show 3000ms. ovs-appctl time/warp 20000 500 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms]) # End of Test-5 ############################################################### # Test-6 BFD decay: state up->down->up. # turn bfd off on p1 AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false]) # advance the clock by 15000ms to stable everything. ovs-appctl time/warp 15000 1000 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms]) BFD_CHECK_RX([p0], [300ms], [300ms], [1ms]) # resume the bfd on p1. the bfd should not go to decay mode direclty. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true]) ovs-appctl time/warp 1500 500 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [500ms], [300ms], [500ms]) # since the decay_min_rx is still 3000ms, so after 5000ms, p0 should have decayed. ovs-appctl time/warp 5000 500 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms]) # End of Test-6 ################################################################ OVS_VSWITCHD_STOP AT_CLEANUP # Tests below are for bfd forwarding_if_rx feature. # forwarding_if_rx Test1 # Test1 tests the case when bfd is only enabled on one end of the link. # Under this situation, the forwarding flag should always be false, even # though there is data packet received, since there is no bfd control # packet received during the demand_rx_bfd interval. AT_SETUP([bfd - bfd forwarding_if_rx - bfd on one side]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500 -- \ add-port br1 p2 -- set Interface p2 type=internal ofport_request=3]) ovs-appctl time/stop # check the inital status. BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic]) BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms]) BFD_CHECK_RX([p0], [500ms], [500ms], [1ms]) # enable forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0]) # there should be no change of forwarding flag, since # there is no traffic. for i in `seq 0 3` do ovs-appctl time/warp 500 BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic]) done # receive packet at 1/100ms rate for 2000ms. for i in `seq 0 19` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done # the forwarding flag should be false, due to the demand_rx_bfd. BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic]) AT_CHECK([ovs-vsctl del-br br1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP # forwarding_if_rx Test2 # Test2 is for testing that the enable of forwarding_if_rx will not # affect the normal bfd communication. bfd is enabled on both ends of # the link. AT_SETUP([bfd - bfd forwarding_if_rx - bfd on both sides]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500 -- \ set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 -- \ add-port br1 p2 -- set Interface p2 type=internal ofport_request=3]) ovs-appctl time/stop # advance the clock, to stablize the states. ovs-appctl time/warp 5000 500 # enable forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0]) # there should be no change of the forwarding flag, since # the bfd on both ends is already up. for i in `seq 0 5` do ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) done # stop the bfd on one side. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0]) # for within 1500ms, the detection timer is not out. # there is no change to status. for i in `seq 0 1` do ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) for i in `seq 0 4` do AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done done # at 1500ms, the STATE should go DOWN, due to Control Detection Time Expired. # but forwarding flag should be still true. ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # reset bfd forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=false], [0]) # forwarding flag should turn to false since the STATE is DOWN. BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # re-enable bfd on the other end. the states should be up. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300]) # advance the clock, to stablize the states. ovs-appctl time/warp 5000 500 BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired]) BFD_CHECK_TX([p0], [500ms], [500ms], [300ms]) BFD_CHECK_RX([p0], [500ms], [500ms], [300ms]) AT_CHECK([ovs-vsctl del-br br1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP # forwarding_if_rx Test3 # Test3 is for testing that the enable of forwarding_if_rx will not # affect the bfd decay feature. bfd is enabled on both ends of the link. AT_SETUP([bfd - bfd forwarding_if_rx - with bfd decay]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 bfd:decay_min_rx=3000 -- \ set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500]) ovs-appctl time/stop # advance the clock, to stablize the states. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms]) # enable forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0]) # there should be no change of the forwarding flag, since # the bfd on both ends is already up. for i in `seq 0 9` do ovs-appctl time/warp 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) done # reconfigure the decay_min_rx to 1000ms. AT_CHECK([ovs-vsctl set interface p0 bfd:decay_min_rx=1000]) # wait for 5000ms to decay. ovs-appctl time/warp 5000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms]) # stop the bfd on one side. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0]) # advance clock by 4000ms, while receiving packets. # the STATE should go DOWN, due to Control Detection Time Expired. # but forwarding flag should be still true. for i in `seq 0 7` do ovs-appctl time/warp 500 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # receive packet at 1/100ms rate for 1000ms. for i in `seq 0 9` do AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) ovs-appctl time/warp 100 # the forwarding flag should always be true during this time. BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) done # stop receiving for 5000ms. ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # reset bfd forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=false]) BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # re-enable bfd forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true]) BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # re-enable bfd on the other end. the states should be up. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300]) # advance the clock, to stablize the states. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired]) BFD_CHECK_TX([p0], [300ms], [300ms], [300ms]) BFD_CHECK_RX([p0], [1000ms], [1000ms], [300ms]) AT_CHECK([ovs-vsctl del-br br1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP # forwarding_if_rx Test4 # Test4 is for testing the demand_rx_bfd feature. # bfd is enabled on both ends of the link. AT_SETUP([bfd - bfd forwarding_if_rx - demand_rx_bfd]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 bfd:forwarding_if_rx=true -- \ set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500]) ovs-appctl time/stop # advance the clock, to stablize the states. ovs-appctl time/warp 10000 500 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) # disable the bfd on p1. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0]) # advance clock by 4000ms, while receiving packets. # the STATE should go DOWN, due to Control Detection Time Expired. # but forwarding flag should be still true. for i in `seq 0 7` do ovs-appctl time/warp 500 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # advance clock long enough to trigger the demand_bfd_rx interval # (100 * bfd->cfm_min_rx), forwarding flag should go down since there # is no bfd control packet received during the demand_rx_bfd. for i in `seq 0 120` do ovs-appctl time/warp 300 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # now enable the bfd on p1 again. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true], [0]) # advance clock by 5000ms. and p1 and p0 should be all up. ovs-appctl time/warp 5000 500 BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired]) BFD_CHECK_TX([p0], [500ms], [300ms], [500ms]) # disable the bfd on p1 again. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0]) # advance clock long enough to trigger the demand_rx_bfd, # forwarding flag should go down since there is no bfd control packet # received during the demand_rx_bfd. for i in `seq 0 120` do ovs-appctl time/warp 300 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) AT_CHECK([ovs-vsctl del-br br1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP # test bfd:flap_count. # This test contains three part: # part 1. tests the flap_count on normal bfd monitored link. # part 2. tests the flap_count when forwarding override is used. # part 3. tests the flap_count when forwarding_if_rx is enabled. AT_SETUP([bfd - flap_count]) #Create 2 bridges connected by patch ports and enable bfd OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100 -- \ set Interface p1 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100]) ovs-appctl time/stop # Disable the stats update to prevent the race between ovsdb updating # stats and ovs-vsctl cmd closing the jsonrpc session. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:stats-update-interval=50000000]) # Part-1 wait for a while to stablize bfd. ovs-appctl time/warp 10100 100 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [100ms], [100ms], [100ms]) BFD_CHECK_RX([p0], [100ms], [100ms], [100ms]) # both p0 and p1 should have flap_count = "1". since down->up. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) # turn bfd on p1 off, should increment the bfd:flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["2"]) AT_CHECK([ovs-vsctl list interface p1 | sed -n "s/^.*flap_count=\(.*\), forwarding.*$/\1/p"]) # turn bfd on p1 on again, should increment the bfd:flap_count on p0. # p1 should still have flap_count = "1", since it is reset. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) # Part-2 now turn on the forwarding_override. AT_CHECK([ovs-appctl bfd/set-forwarding p0 true], [0], [dnl OK ]) # turn bfd on p1 off, should not increment the bfd:flap_count on p0, since forwarding_override is on. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) AT_CHECK([ovs-vsctl list interface p1 | sed -n "s/^.*flap_count=\(.*\), forwarding.*$/\1/p"]) # turn bfd on p1 on again, should not increment the bfd:flap_count on p0, since forwarding override is on. # p1 should still have flap_count = "1", since it is reset. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) # turn the forwarding_override back to normal. AT_CHECK([ovs-appctl bfd/set-forwarding p0 normal], [0], [dnl OK ]) # turn bfd on p1 off and on, should increment the bfd:flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["5"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) # Part-3 now turn on forwarding_if_rx. AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0]) ovs-appctl time/warp 1100 100 # disable the bfd on p1. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0]) # advance clock by 4000ms, while receiving packets. # the STATE should go DOWN, due to Control Detection Time Expired. # but forwarding flag should be true. for i in `seq 0 39` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) # flap_count should remain unchanged. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["5"]) # stop the traffic for more than 100 * bfd->cfm_min_rx ms, the forwarding flag of p0 should turn false. # and there should be the increment of flap_count. ovs-appctl time/warp 12100 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["6"]) # advance clock by 4000ms, and resume the traffic. for i in `seq 0 39` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done # forwarding should be false, since there is still no bfd control packet received. BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["6"]) # turn on the bfd on p1. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 # even though there is no data traffic, since p1 bfd is on again, should increment the flap_count. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["7"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - check that BFD works together with RSTP]) # Create br0 with interfaces p1 # and br1 with interfaces p2 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [set bridge br0 rstp_enable=true -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy -- \ set bridge br1 rstp_enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br0 p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock bfd:enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p2 -- \ set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock bfd:enable=true -- \ ]) ovs-appctl time/stop ovs-appctl time/warp 4100 100 # Forwarding should be true BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p2], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) # Disable bfd on p2, forwarding on p1 should go to false AT_CHECK([ovs-vsctl set interface p2 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p1], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) OVS_VSWITCHD_STOP AT_CLEANUP # test bfd: liveness propagation - OF1.3. AT_SETUP([bfd - liveness propagation - OF1.3]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow13 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.3): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 #Create 2 bridges connected by patch ports and enable bfd AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100 -- \ set Interface p1 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100]) ovs-appctl time/stop # Disable the stats update to prevent the race between ovsdb updating # stats and ovs-vsctl cmd closing the jsonrpc session. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:stats-update-interval=50000000]) # wait for a while to stablize bfd. ovs-appctl time/warp 10100 100 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [100ms], [100ms], [100ms]) BFD_CHECK_RX([p0], [100ms], [100ms], [100ms]) # both p0 and p1 should have flap_count = "1". since down->up. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 1 LIVE # turn bfd on p1 off, should increment the bfd:flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["2"]) AT_CHECK([ovs-vsctl list interface p1 | sed -n "s/^.*flap_count=\(.*\), forwarding.*$/\1/p"]) check_liveness 2 0 # turn bfd on p1 on again, should increment the bfd:flap_count on p0. # p1 should still have flap_count = "1", since it is reset. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP # test bfd: liveness propagation - OF1.4. AT_SETUP([bfd - liveness propagation - OF1.4]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow14 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.4): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0509000c0123456700000080 #Create 2 bridges connected by patch ports and enable bfd AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100 -- \ set Interface p1 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100]) ovs-appctl time/stop # Disable the stats update to prevent the race between ovsdb updating # stats and ovs-vsctl cmd closing the jsonrpc session. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:stats-update-interval=50000000]) # wait for a while to stablize bfd. ovs-appctl time/warp 10100 100 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [100ms], [100ms], [100ms]) BFD_CHECK_RX([p0], [100ms], [100ms], [100ms]) # both p0 and p1 should have flap_count = "1". since down->up. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 1 LIVE # turn bfd on p1 off, should increment the bfd:flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["2"]) AT_CHECK([ovs-vsctl list interface p1 | sed -n "s/^.*flap_count=\(.*\), forwarding.*$/\1/p"]) check_liveness 2 0 # turn bfd on p1 on again, should increment the bfd:flap_count on p0. # p1 should still have flap_count = "1", since it is reset. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP # test bfd: liveness propagation - OF1.5. AT_SETUP([bfd - liveness propagation - OF1.5]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.5): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 #Create 2 bridges connected by patch ports and enable bfd AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100 -- \ set Interface p1 bfd:enable=true bfd:min_tx=100 bfd:min_rx=100]) ovs-appctl time/stop # Disable the stats update to prevent the race between ovsdb updating # stats and ovs-vsctl cmd closing the jsonrpc session. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:stats-update-interval=50000000]) # wait for a while to stablize bfd. ovs-appctl time/warp 10100 100 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK_TX([p0], [100ms], [100ms], [100ms]) BFD_CHECK_RX([p0], [100ms], [100ms], [100ms]) # both p0 and p1 should have flap_count = "1". since down->up. BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 1 LIVE # turn bfd on p1 off, should increment the bfd:flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=false]) ovs-appctl time/warp 5000 100 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic]) BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["2"]) AT_CHECK([ovs-vsctl list interface p1 | sed -n "s/^.*flap_count=\(.*\), forwarding.*$/\1/p"]) check_liveness 2 0 # turn bfd on p1 on again, should increment the bfd:flap_count on p0. # p1 should still have flap_count = "1", since it is reset. AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true]) ovs-appctl time/warp 5000 100 BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["3"]) BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"]) check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - Edit the Detect Mult values]) #Create 2 bridges connected by patch ports and enable BFD OVS_VSWITCHD_START() ovs-appctl time/stop AT_CHECK([ ovs-vsctl -- add-br br1 -- \ set bridge br1 datapath-type=dummy ]) AT_CHECK([ ovs-vsctl -- add-port br1 p1 -- set Interface p1 type=patch\ options:peer=p0 ]) AT_CHECK([ ovs-vsctl -- add-port br0 p0 -- set Interface p0 type=patch\ options:peer=p1 ]) AT_CHECK([ ovs-vsctl -- set interface p0 bfd:enable=true ]) AT_CHECK([ ovs-vsctl -- set interface p1 bfd:enable=true ]) ovs-appctl time/warp 3100 100 #Verify that BFD has been enabled on both interfaces. BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic]) #Verify that default mult values are 3. BFD_CHECK_MULT([p0], [3], [3]) BFD_CHECK_MULT([p1], [3], [3]) #Set the mult values to valid range border mult(p0)=1 mult(p1)=255. AT_CHECK([ovs-vsctl set interface p0 bfd:mult=1]) AT_CHECK([ovs-vsctl set interface p1 bfd:mult=255]) ovs-appctl time/warp 3100 100 BFD_CHECK_MULT([p0], [1], [255]) BFD_CHECK_MULT([p1], [255], [1]) #Set the mult values out valid range border mult(p0)=0 mult(p1)=256. AT_CHECK([ovs-vsctl set interface p0 bfd:mult=0]) AT_CHECK([ovs-vsctl set interface p1 bfd:mult=256]) ovs-appctl time/warp 3100 100 BFD_CHECK_MULT([p0], [3], [3]) BFD_CHECK_MULT([p1], [3], [3]) #Set valid non default mult values mult(p0)=8 mult(p1)=125. AT_CHECK([ovs-vsctl set interface p0 bfd:mult=8]) AT_CHECK([ovs-vsctl set interface p1 bfd:mult=125]) ovs-appctl time/warp 3100 100 BFD_CHECK_MULT([p0], [8], [125]) BFD_CHECK_MULT([p1], [125], [8]) #Clear mult values. Detect mult values shall be default 3 again. AT_CHECK([ovs-vsctl remove interface p0 bfd mult]) AT_CHECK([ovs-vsctl remove interface p1 bfd mult]) ovs-appctl time/warp 3100 100 BFD_CHECK_MULT([p0], [3], [3]) BFD_CHECK_MULT([p1], [3], [3]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bfd - overlay]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=2.2.2.2 ofport_request=1 -- \ set interface p1 bfd:enable=true bfd:bfd_src_ip=2.2.2.1 -- \ set bridge br0 fail-mode=standalone]) # Userspace slow path handles normal BFD packets. AT_CHECK([ovs-appctl ofproto/trace --l7-len 0 ovs-dummy 'tunnel(tun_id=0x0,src=2.2.2.2,dst=2.2.2.1,tos=0x0,ttl=64,tp_src=0,tp_dst=0,flags()),in_port(1),skb_mark(0/0),eth(src=00:11:22:33:44:55,dst=00:23:20:00:00:01),eth_type(0x0800),ipv4(src=2.2.2.2/0.0.0.0,dst=2.2.2.1/0.0.0.0,proto=17/0xff,tos=0/0,ttl=255/0,frag=no),udp(src=49152/0,dst=3784/0xffff)' -generate], [0], [stdout]) # check that the packet should be handled as BFD packet. AT_CHECK([tail -2 stdout], [0], [dnl This flow is handled by the userspace slow path because it: - Consists of BFD packets. ], []) # Userspace slow path won't handle overlay BFD packets. Instead, other OVS flows, if configured, will handle them. AT_CHECK([ovs-appctl ofproto/trace --l7-len 0 ovs-dummy 'tunnel(tun_id=0x0,src=2.2.2.2,dst=2.2.2.1,tos=0x0,ttl=64,tp_src=0,tp_dst=0,flags()),in_port(1),skb_mark(0/0),eth(src=00:11:22:33:44:66,dst=00:23:20:00:00:77),eth_type(0x0800),ipv4(src=192.168.2.2/0.0.0.0,dst=192.168.2.1/0.0.0.0,proto=17/0xff,tos=0/0,ttl=255/0,frag=no),udp(src=49152/0,dst=3784/0xffff)' -generate], [0], [stdout]) AT_CHECK([tail -10 stdout], [0], [dnl bridge("br0") ------------- 0. priority 0 NORMAL -> learned that 00:11:22:33:44:66 is on port p1 in VLAN 0 -> no learned MAC for destination, flooding Final flow: unchanged Megaflow: recirc_id=0,eth,udp,tun_id=0,tun_src=2.2.2.2,tun_dst=2.2.2.1,tun_tos=0,tun_flags=+key,in_port=1,dl_src=00:11:22:33:44:66,dl_dst=00:23:20:00:00:77,nw_frag=no,tp_dst=3784 Datapath actions: 100 ], []) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/bridge.at000066400000000000000000000100521514270232600217020ustar00rootroot00000000000000AT_BANNER([bridge]) dnl When a port disappears from a datapath, e.g. because an admin used dnl "ovs-dpctl del-port", the bridge code should be resilient enough to dnl notice and add it back the next time we reconfigure. A prior version dnl of the code failed to do this, so this test guards against regression. AT_SETUP([bridge - ports that disappear get added back]) OVS_VSWITCHD_START # Add some ports and make sure that they show up in the datapath. add_of_ports br0 1 2 AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/2: (dummy) ]) # Delete p1 from the datapath with "ovs-dpctl del-if" # and check that it disappeared. AT_CHECK([ovs-appctl dpctl/del-if dummy@ovs-dummy p1]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p2 2/2: (dummy) ]) # Force reconfiguration and make sure that p1 got added back. AT_CHECK([ovs-vsctl del-port p2]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) ]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP dnl When multiple bridges are connected to the same controller, make dnl sure their status are tracked independently. AT_SETUP([bridge - multiple bridges share a controller]) OVS_VSWITCHD_START( [add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 ]) dnl Start ovs-testcontroller AT_CHECK([ovs-testcontroller --detach --no-chdir punix:controller --pidfile], [0], [ignore]) on_exit 'kill `cat ovs-testcontroller.pid`' OVS_WAIT_UNTIL([test -e controller]) dnl Add the controller to both bridges, 5 seconds apart. AT_CHECK([ovs-vsctl set-controller br0 unix:controller]) AT_CHECK([ovs-vsctl set-fail-mode br0 secure]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-vsctl set-controller br1 unix:controller]) AT_CHECK([ovs-vsctl set-fail-mode br1 secure]) dnl Wait for the controller connectionsi to be up for i in `seq 0 19` do if ovs-vsctl --columns=is_connected list controller |grep "false"; then : else break fi ovs-appctl time/warp 1100 done dnl Make sure the connection status have two records and they are different. dnl (The exact output contains timing information that are machine dependent.) AT_CHECK([ovs-vsctl --columns=status list controller | dnl grep "status" | sort -u |wc -l], [0], [2 ]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([bridge - add port after stopping controller]) OVS_VSWITCHD_START dnl Start ovs-testcontroller AT_CHECK([ovs-testcontroller --no-chdir --detach punix:controller --pidfile], [0], [ignore]) OVS_WAIT_UNTIL([test -e controller]) AT_CHECK([ovs-vsctl set-controller br0 unix:controller]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=internal], [0], [ignore]) AT_CHECK([ovs-appctl -t ovs-vswitchd version], [0], [ignore]) # Now kill the ovs-testcontroller kill `cat ovs-testcontroller.pid` if test "$IS_WIN32" = "yes"; then AT_CHECK([rm controller], [0], [ignore]) fi OVS_WAIT_UNTIL([! test -e controller]) AT_CHECK([ovs-vsctl --no-wait add-port br0 p2 -- set Interface p2 type=internal], [0], [ignore]) AT_CHECK([ovs-appctl -t ovs-vswitchd version], [0], [ignore]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP AT_SETUP([bridge - change ofproto versions]) dnl Start vswitch and add a version test bridge OVS_VSWITCHD_START( [add-br vr_test0 -- \ set bridge vr_test0 datapath-type=dummy \ protocols=OpenFlow10]) dnl set the version to include, say, OpenFlow14 AT_CHECK([ovs-vsctl set bridge vr_test0 protocols=OpenFlow10,OpenFlow14]) dnl now try to use bundle action on a flow AT_CHECK([ovs-ofctl add-flow vr_test0 --bundle actions=normal]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/bundle.at000066400000000000000000000637101514270232600217300ustar00rootroot00000000000000AT_BANNER([bundle link selection]) # The test-bundle program prints a lot of output on stdout, but each of the # tests below ignores it because it will vary a bit depending on endianness and # floating point precision. test-bundle will output an error message on # stderr and return with exit code 1 if anything really goes wrong. In each # case, we list the (approximate) expected output in a comment to aid debugging # if the test does fail. AT_SETUP([hrw bundle link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],members:1,2,3,4,5']], [0], [ignore]) # 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 # 110000: disruption=0.50 (perfect=0.50) 0.50 0.50 0.00 0.00 0.00 0.00 # 010000: disruption=0.50 (perfect=0.50) 0.00 1.00 0.00 0.00 0.00 0.00 # 011000: disruption=0.50 (perfect=0.50) 0.00 0.50 0.50 0.00 0.00 0.00 # 111000: disruption=0.33 (perfect=0.33) 0.33 0.33 0.34 0.00 0.00 0.00 # 101000: disruption=0.33 (perfect=0.33) 0.50 0.00 0.50 0.00 0.00 0.00 # 001000: disruption=0.50 (perfect=0.50) 0.00 0.00 1.00 0.00 0.00 0.00 # 001100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.50 0.50 0.00 0.00 # 101100: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.33 0.00 0.00 # 111100: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.25 0.00 0.00 # 011100: disruption=0.25 (perfect=0.25) 0.00 0.33 0.33 0.33 0.00 0.00 # 010100: disruption=0.33 (perfect=0.33) 0.00 0.50 0.00 0.50 0.00 0.00 # 110100: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.34 0.00 0.00 # 100100: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.50 0.00 0.00 # 000100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 1.00 0.00 0.00 # 000110: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.50 0.50 0.00 # 100110: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.33 0.00 # 110110: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.25 0.00 # 010110: disruption=0.25 (perfect=0.25) 0.00 0.34 0.00 0.33 0.33 0.00 # 011110: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.25 0.00 # 111110: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.20 0.00 # 101110: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.25 0.00 # 001110: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.33 0.33 0.00 # 001010: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.50 0.00 # 101010: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.00 0.33 0.00 # 111010: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.25 0.00 # 011010: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.33 0.00 # 010010: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.50 0.00 # 110010: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.33 0.00 # 100010: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.50 0.00 # 000010: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 1.00 0.00 # 000011: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.50 0.50 # 100011: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.00 0.33 0.33 # 110011: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.00 0.25 0.25 # 010011: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.00 0.33 0.33 # 011011: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.00 0.25 0.25 # 111011: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.00 0.20 0.20 # 101011: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.00 0.25 0.25 # 001011: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.00 0.33 0.33 # 001111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.25 0.25 0.25 0.25 # 101111: disruption=0.20 (perfect=0.20) 0.20 0.00 0.20 0.20 0.20 0.20 # 111111: disruption=0.17 (perfect=0.17) 0.17 0.17 0.17 0.17 0.17 0.17 # 011111: disruption=0.17 (perfect=0.17) 0.00 0.20 0.20 0.20 0.20 0.20 # 010111: disruption=0.20 (perfect=0.20) 0.00 0.25 0.00 0.25 0.25 0.25 # 110111: disruption=0.20 (perfect=0.20) 0.20 0.20 0.00 0.20 0.20 0.20 # 100111: disruption=0.20 (perfect=0.20) 0.25 0.00 0.00 0.25 0.25 0.25 # 000111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.00 0.33 0.33 0.33 # 000101: disruption=0.33 (perfect=0.33) 0.00 0.00 0.00 0.50 0.00 0.50 # 100101: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.00 0.33 # 110101: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.00 0.25 # 010101: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.33 0.00 0.33 # 011101: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.00 0.25 # 111101: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.00 0.20 # 101101: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.00 0.25 # 001101: disruption=0.25 (perfect=0.25) 0.00 0.00 0.33 0.33 0.00 0.33 # 001001: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.00 0.50 # 101001: disruption=0.33 (perfect=0.33) 0.33 0.00 0.33 0.00 0.00 0.33 # 111001: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.00 0.25 # 011001: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.00 0.33 # 010001: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.00 0.50 # 110001: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.00 0.34 # 100001: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.00 0.50 # 000001: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.00 1.00 # 000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00 # 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 AT_CLEANUP AT_SETUP([active_backup bundle link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,active_backup,ofport,NXM_NX_REG0[],members:1,2,3,4,5,6']], [0], [100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010000: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011000: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001000: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001100: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011100: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010100: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000100: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00 000110: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00 100110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010110: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011110: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001110: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001010: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011010: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010010: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000010: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 1.00 0.00 000011: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 0.00 1.00 0.00 100011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010011: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011011: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001011: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001111: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011111: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010111: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000111: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00 000101: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00 100101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010101: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011101: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001101: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001001: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011001: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010001: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000001: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 1.00 000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 ]) AT_CLEANUP AT_SETUP([hrw bundle single link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],members:1']], [0], [ignore]) # 1: disruption=1.00 (perfect=1.00) 1.00 # 0: disruption=1.00 (perfect=1.00) 0.00 # 1: disruption=1.00 (perfect=1.00) 1.00 AT_CLEANUP AT_SETUP([hrw bundle no link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l4,60,hrw,ofport,NXM_NX_REG0[],members:']], [0], [ignore]) AT_CLEANUP #: disruption=0.00 (perfect=0.00) #: disruption=0.00 (perfect=0.00) AT_SETUP([bundle action missing argument]) AT_KEYWORDS([bundle_action]) AT_CHECK([ovs-ofctl parse-flow actions=bundle], [1], [], [ovs-ofctl: : not enough arguments to bundle action ]) AT_CLEANUP AT_SETUP([bundle action bad fields]) AT_KEYWORDS([bundle_action]) AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(xyzzy,60,hrw,ofport,members:1,2))'], [1], [], [ovs-ofctl: xyzzy,60,hrw,ofport,members:1,2: unknown fields `xyzzy' ]) AT_CLEANUP AT_SETUP([bundle action bad algorithm]) AT_KEYWORDS([bundle_action]) AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,fubar,ofport,members:1,2))'], [1], [], [ovs-ofctl: symmetric_l4,60,fubar,ofport,members:1,2: unknown algorithm `fubar' ]) AT_CLEANUP AT_SETUP([bundle action bad member type]) AT_KEYWORDS([bundle_action]) AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,hrw,robot,members:1,2))'], [1], [], [ovs-ofctl: symmetric_l4,60,hrw,robot,members:1,2: unknown member_type `robot' ]) AT_CLEANUP AT_SETUP([bundle action bad member delimiter]) AT_KEYWORDS([bundle_action]) AT_CHECK([ovs-ofctl parse-flow 'actions=bundle(symmetric_l4,60,hrw,ofport,robot:1,2))'], [1], [], [ovs-ofctl: symmetric_l4,60,hrw,ofport,robot:1,2: missing member delimiter, expected `members', got `robot' ]) AT_CLEANUP dnl Bundle actions with <= 2 ports typically align nicely within ofpbuf memory dnl allocation, so will not trigger reallocation codepaths. This test was dnl introduced to ensure that when bundle actions with a larger number of ports dnl are used, the encode/decode still works correctly. By placing the bundle dnl action deep within a list of actions, this test was able to trigger dnl Valgrind warnings for use-after-free bugs. AT_SETUP([bundle action with many ports]) AT_KEYWORDS([bundle_action]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 'actions=set_field:0x1->metadata,set_field:0x2->metadata,set_field:0x3->metadata,set_field:0x4->metadata,bundle(symmetric_l4,0,hrw,ofport,members:[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]])']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats], [0], [dnl actions=load:0x1->OXM_OF_METADATA[[]],load:0x2->OXM_OF_METADATA[[]],load:0x3->OXM_OF_METADATA[[]],load:0x4->OXM_OF_METADATA[[]],bundle(symmetric_l4,0,hrw,ofport,members:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bundle action with ports up and down]) AT_KEYWORDS([bundle_action]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy -- \ set Interface p1 ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy -- \ set Interface p2 ofport_request=2 ]) AT_CHECK([ovs-ofctl add-flow br0 'actions=bundle(eth_src,50,active_backup,ofport,members:1,2)']) AT_CHECK([ovs-ofctl mod-port br0 p1 up]) AT_CHECK([ovs-ofctl mod-port br0 p2 up]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) AT_CHECK([ovs-ofctl mod-port br0 p1 down]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_CHECK([ovs-ofctl mod-port br0 p2 down]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: drop ]) AT_CHECK([ovs-ofctl mod-port br0 p1 up]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) AT_CHECK([ovs-ofctl mod-port br0 p2 up]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([bundle_load action with ports down]) AT_KEYWORDS([bundle_action]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy -- \ set Interface p1 ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy -- \ set Interface p2 ofport_request=2 ]) AT_CHECK([ovs-ofctl add-flow br0 'actions=bundle_load(eth_src,50,hrw,ofport,OXM_OF_ETH_SRC[[0..15]],members:1,2)']) AT_CHECK([ovs-ofctl mod-port br0 p1 down]) AT_CHECK([ovs-ofctl mod-port br0 p2 down]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout]) AT_CHECK([grep Final stdout], [0], [Final flow: in_port=LOCAL,vlan_tci=0x0000,dl_src=50:54:00:00:ff:ff,dl_dst=50:54:00:00:00:06,dl_type=0x0000 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([hrw bundle symmetric_l3 link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],members:1,2,3,4,5']], [0], [ignore]) # 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 # 110000: disruption=0.50 (perfect=0.50) 0.50 0.50 0.00 0.00 0.00 0.00 # 010000: disruption=0.50 (perfect=0.50) 0.00 1.00 0.00 0.00 0.00 0.00 # 011000: disruption=0.50 (perfect=0.50) 0.00 0.50 0.50 0.00 0.00 0.00 # 111000: disruption=0.33 (perfect=0.33) 0.33 0.33 0.34 0.00 0.00 0.00 # 101000: disruption=0.33 (perfect=0.33) 0.50 0.00 0.50 0.00 0.00 0.00 # 001000: disruption=0.50 (perfect=0.50) 0.00 0.00 1.00 0.00 0.00 0.00 # 001100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.50 0.50 0.00 0.00 # 101100: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.33 0.00 0.00 # 111100: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.25 0.00 0.00 # 011100: disruption=0.25 (perfect=0.25) 0.00 0.33 0.33 0.33 0.00 0.00 # 010100: disruption=0.33 (perfect=0.33) 0.00 0.50 0.00 0.50 0.00 0.00 # 110100: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.34 0.00 0.00 # 100100: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.50 0.00 0.00 # 000100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 1.00 0.00 0.00 # 000110: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.50 0.50 0.00 # 100110: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.33 0.00 # 110110: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.25 0.00 # 010110: disruption=0.25 (perfect=0.25) 0.00 0.34 0.00 0.33 0.33 0.00 # 011110: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.25 0.00 # 111110: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.20 0.00 # 101110: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.25 0.00 # 001110: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.33 0.33 0.00 # 001010: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.50 0.00 # 101010: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.00 0.33 0.00 # 111010: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.25 0.00 # 011010: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.33 0.00 # 010010: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.50 0.00 # 110010: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.33 0.00 # 100010: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.50 0.00 # 000010: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 1.00 0.00 # 000011: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.50 0.50 # 100011: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.00 0.33 0.33 # 110011: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.00 0.25 0.25 # 010011: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.00 0.33 0.33 # 011011: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.00 0.25 0.25 # 111011: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.00 0.20 0.20 # 101011: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.00 0.25 0.25 # 001011: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.00 0.33 0.33 # 001111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.25 0.25 0.25 0.25 # 101111: disruption=0.20 (perfect=0.20) 0.20 0.00 0.20 0.20 0.20 0.20 # 111111: disruption=0.17 (perfect=0.17) 0.17 0.17 0.17 0.17 0.17 0.17 # 011111: disruption=0.17 (perfect=0.17) 0.00 0.20 0.20 0.20 0.20 0.20 # 010111: disruption=0.20 (perfect=0.20) 0.00 0.25 0.00 0.25 0.25 0.25 # 110111: disruption=0.20 (perfect=0.20) 0.20 0.20 0.00 0.20 0.20 0.20 # 100111: disruption=0.20 (perfect=0.20) 0.25 0.00 0.00 0.25 0.25 0.25 # 000111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.00 0.33 0.33 0.33 # 000101: disruption=0.33 (perfect=0.33) 0.00 0.00 0.00 0.50 0.00 0.50 # 100101: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.00 0.33 # 110101: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.00 0.25 # 010101: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.33 0.00 0.33 # 011101: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.00 0.25 # 111101: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.00 0.20 # 101101: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.00 0.25 # 001101: disruption=0.25 (perfect=0.25) 0.00 0.00 0.33 0.33 0.00 0.33 # 001001: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.00 0.50 # 101001: disruption=0.33 (perfect=0.33) 0.33 0.00 0.33 0.00 0.00 0.33 # 111001: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.00 0.25 # 011001: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.00 0.33 # 010001: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.00 0.50 # 110001: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.00 0.34 # 100001: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.00 0.50 # 000001: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.00 1.00 # 000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00 # 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 AT_CLEANUP AT_SETUP([active_backup bundle symmetric_l3 link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,active_backup,ofport,NXM_NX_REG0[],members:1,2,3,4,5,6']], [0], [100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010000: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011000: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001000: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001100: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011100: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010100: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000100: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00 000110: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00 100110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010110: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011110: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001110: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001010: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011010: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010010: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000010: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 1.00 0.00 000011: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 0.00 1.00 0.00 100011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010011: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011011: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001011: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001111: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011111: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010111: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000111: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00 000101: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00 100101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 110101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 010101: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 011101: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 111101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 101101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 001101: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00 001001: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00 101001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 111001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 011001: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00 010001: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00 110001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 100001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00 000001: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 1.00 000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00 ]) AT_CLEANUP AT_SETUP([hrw bundle symmetric_l3 single link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],members:1']], [0], [ignore]) # 1: disruption=1.00 (perfect=1.00) 1.00 # 0: disruption=1.00 (perfect=1.00) 0.00 # 1: disruption=1.00 (perfect=1.00) 1.00 AT_CLEANUP AT_SETUP([hrw bundle symmetric_l3 single link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],members:1']], [0], [ignore]) # 1: disruption=1.00 (perfect=1.00) 1.00 # 0: disruption=1.00 (perfect=1.00) 0.00 # 1: disruption=1.00 (perfect=1.00) 1.00 AT_CLEANUP AT_SETUP([hrw bundle symmetric_l3 no link selection]) AT_KEYWORDS([bundle_action]) AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],members:']], [0], [ignore]) AT_CLEANUP #: disruption=0.00 (perfect=0.00) #: disruption=0.00 (perfect=0.00) AT_SETUP([bundle symmetric_l3 action with many ports]) AT_KEYWORDS([bundle_action]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 'actions=set_field:0x1->metadata,set_field:0x2->metadata,set_field:0x3->metadata,set_field:0x4->metadata,bundle(symmetric_l3,0,hrw,ofport,members:[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]])']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats], [0], [dnl actions=load:0x1->OXM_OF_METADATA[[]],load:0x2->OXM_OF_METADATA[[]],load:0x3->OXM_OF_METADATA[[]],load:0x4->OXM_OF_METADATA[[]],bundle(symmetric_l3,0,hrw,ofport,members:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40) ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/cfm.at000066400000000000000000000462361514270232600212300ustar00rootroot00000000000000AT_BANNER([cfm]) m4_define([CFM_CHECK_EXTENDED], [ AT_CHECK([ovs-appctl cfm/show $1 | sed -e '/next CCM tx:/d' | sed -e '/next fault check:/d' | sed -e '/recv since check:/d'],[0], [dnl ---- $1 ---- MPID $2: extended average health: $3 opstate: $4 remote_opstate: $5 interval: $6 Remote MPID $7 opstate: $8 ]) ]) m4_define([CFM_CHECK_EXTENDED_FAULT], [ AT_CHECK([ovs-appctl cfm/show $1 | sed -e '/next CCM tx:/d' | sed -e '/next fault check:/d' | sed -e '/recv since check:/d'],[0], [dnl ---- $1 ---- MPID $2: extended fault: $3 average health: $4 opstate: $5 remote_opstate: $6 interval: $7 ]) ]) m4_define([CFM_VSCTL_LIST_IFACE], [ AT_CHECK([ovs-vsctl list interface $1 | sed -n '/$2 /p'],[0], [dnl $3 ]) ]) m4_define([CFM_CHECK_DB], [ CFM_VSCTL_LIST_IFACE([$1], [cfm_fault], [cfm_fault : $2]) CFM_VSCTL_LIST_IFACE([$1], [cfm_fault_status], [cfm_fault_status : [[$3]]]) CFM_VSCTL_LIST_IFACE([$1], [cfm_flap_count], [cfm_flap_count : $4]) CFM_VSCTL_LIST_IFACE([$1], [cfm_health], [cfm_health : [[$5]]]) CFM_VSCTL_LIST_IFACE([$1], [cfm_remote_mpids], [cfm_remote_mpids : [[$6]]]) CFM_VSCTL_LIST_IFACE([$1], [cfm_remote_opstate], [cfm_remote_opstate : $7]) ]) # These two tests check the update of cfm status at different scenarios. # Test cfm status update at startup and removal. AT_SETUP([cfm - check update ovsdb 1]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=gre \ options:remote_ip=1.2.3.4 -- \ set Interface p0 other_config:cfm_interval=300 other_config:cfm_extended=true]) ovs-appctl time/stop AT_CHECK([ovs-appctl ofproto/list-tunnels], [0], [dnl port 1: p0 (gre: ::->1.2.3.4, key=0, legacy_l2, dp port=1, ttl=64) ]) AT_CHECK([ovs-vsctl set Interface p0 cfm_mpid=1]) # at beginning, since the first fault check timeout is not reached # cfm_fault should be false. for i in `seq 0 4`; do ovs-appctl time/warp 100 CFM_CHECK_DB([p0], [false], [], [0], [], [], [up]) done # advance clock to pass the fault check timeout and check cfm # status update in OVSDB. ovs-appctl time/warp 1500 100 CFM_CHECK_DB([p0], [true], [recv], [1], [], [], [up]) # remove the cfm on p0 and status should be all empty. AT_CHECK([ovs-vsctl remove int p0 cfm_mpid 1]) ovs-appctl time/warp 500 100 CFM_CHECK_DB([p0], [[[]]], [], [[[]]], [], [], [[[]]]) OVS_VSWITCHD_STOP AT_CLEANUP # Test cfm status update in normal case. AT_SETUP([cfm - check update ovsdb 2]) #Create 2 bridges connected by patch ports and enable cfm OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 -- \ set Interface p0 other_config:cfm_interval=300 other_config:cfm_extended=true -- \ set Interface p1 other_config:cfm_interval=300 other_config:cfm_extended=true]) ovs-appctl time/stop AT_CHECK([ovs-vsctl set Interface p0 cfm_mpid=1]) # check cfm status update in OVSDB. ovs-appctl time/warp 1500 100 CFM_CHECK_DB([p0], [true], [recv], [1], [], [], [up]) # turn cfm on p1 on, cfm status of p0 and p1 should all go up. AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2]) ovs-appctl time/warp 1500 100 CFM_CHECK_DB([p0], [false], [], [2], [], [2], [up]) CFM_CHECK_DB([p1], [false], [], [0], [], [1], [up]) # turn cfm on p1 off, cfm status of p0 should go down again. AT_CHECK([ovs-vsctl remove int p1 cfm_mpid 2]) ovs-appctl time/warp 1500 100 CFM_CHECK_DB([p0], [true], [recv], [3], [], [], [up]) OVS_VSWITCHD_STOP AT_CLEANUP # test cfm under demand mode. AT_SETUP([cfm - demand mode]) #Create 2 bridges connected by patch ports and enable cfm OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 -- \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=300 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=300 other_config:cfm_extended=true ]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) # turn on demand mode on one end. AT_CHECK([ovs-vsctl set interface p0 other_config:cfm_demand=true]) # cfm should never go down. for i in `seq 0 100` do ovs-appctl time/warp 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) done # turn on demand mode on the other end. AT_CHECK([ovs-vsctl set interface p1 other_config:cfm_demand=true]) for i in `seq 0 100` do ovs-appctl time/warp 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) done OVS_VSWITCHD_STOP AT_CLEANUP # test demand_rx_ccm under demand mode. AT_SETUP([cfm - demand_rx_ccm]) #Create 2 bridges connected by patch ports and enable cfm OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 ofport_request=2 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 ofport_request=1 -- \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=300 other_config:cfm_extended=true other_config:cfm_demand=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=300 other_config:cfm_extended=true other_config:cfm_demand=true]) ovs-appctl time/stop # wait for a while to stablize cfm. (need a longer time, since in demand mode # the fault interval is (MAX(ccm_interval_ms, 500) * 3.5) ms) ovs-appctl time/warp 20100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) # turn off the cfm on p1. AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid]) # cfm should never go down while receiving data packets. for i in `seq 0 200` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done CFM_CHECK_EXTENDED([p0], [1], [0], [up], [up], [300ms], [2], [up]) # wait longer, since the demand_rx_ccm interval is 100 * 300 ms. # since there is no ccm received, the [recv] fault should be raised. for i in `seq 0 200` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms]) # now turn on the cfm on p1 again, AT_CHECK([ovs-vsctl set Interface p1 cfm_mpid=2]) # cfm should be up for both p0 and p1 ovs-appctl time/warp 20100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) # now turn off the cfm on p1 again AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid]) # since there is no ccm received, the [recv] fault should be raised. for i in `seq 0 400` do ovs-appctl time/warp 100 AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"], [0], [stdout], []) done CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms]) OVS_VSWITCHD_STOP AT_CLEANUP # test cfm_flap_count. AT_SETUP([cfm - flap_count]) #Create 2 bridges connected by patch ports and enable cfm OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 -- \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up]) # turn cfm on p1 off, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2]) ovs-appctl time/warp 1100 100 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 1]) CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count : [[]]]) # turn cfm on p1 on again, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2]) ovs-appctl time/warp 1100 100 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 2]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([cfm - fault_override]) OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1 -- \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up]) AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore]) CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status : [[]]]) # set a fault and see that this is shown in the CFM module and the database AT_CHECK([ovs-appctl cfm/set-fault p1 true], [0], [OK ]) AT_CHECK([ovs-appctl time/warp 100], [0], [ignore]) AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override' | sed -e 's/MPID [[0-9]]*: extended //'], [0], [dnl fault_override ]) CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status : [[override]]]) # reset and see that it returned to normal AT_CHECK([ovs-appctl cfm/set-fault normal], [0], [OK ]) AT_CHECK([ovs-appctl time/warp 100], [0], [ignore]) AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore]) CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status : [[]]]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([cfm - check that CFM works together with RSTP]) # Create br0 with interfaces p1 # and br1 with interfaces p2 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [set bridge br0 rstp_enable=true -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy -- \ set bridge br1 rstp_enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br0 p0 -- \ set interface p0 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock cfm_mpid=1 other_config:cfm_interval=300 other_config:cfm_extended=true -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p1 -- \ set interface p1 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock cfm_mpid=2 other_config:cfm_interval=300 other_config:cfm_extended=true -- \ ]) ovs-appctl time/stop ovs-appctl time/warp 10100 100 # Forwarding should be true CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up]) # Disable cfm on p1, cfm should receive fault. AT_CHECK([ovs-vsctl set interface p1 other_config:cfm_extended=false]) ovs-appctl time/warp 5000 100 CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms]) OVS_VSWITCHD_STOP AT_CLEANUP # test cfm liveness propagation - OF1.3. AT_SETUP([cfm - liveness propagation - OF1.3]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow13 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.3): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 #Create 2 bridges connected by patch ports and enable cfm AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1]) check_liveness 1 LIVE AT_CHECK([ovs-vsctl \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up]) # turn cfm on p1 off, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2]) ovs-appctl time/warp 1100 100 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 1]) CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count : [[]]]) check_liveness 2 0 # turn cfm on p1 on again, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2]) ovs-appctl time/warp 1100 100 check_liveness 3 LIVE CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 2]) OVS_VSWITCHD_STOP AT_CLEANUP # test cfm liveness propagation - OF1.4. AT_SETUP([cfm - liveness propagation - OF1.4]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow14 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.4): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0509000c0123456700000080 #Create 2 bridges connected by patch ports and enable cfm AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1]) check_liveness 1 LIVE AT_CHECK([ovs-vsctl \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up]) # turn cfm on p1 off, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2]) ovs-appctl time/warp 1100 100 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 1]) CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count : [[]]]) check_liveness 2 0 # turn cfm on p1 on again, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2]) ovs-appctl time/warp 1100 100 check_liveness 3 LIVE CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 2]) OVS_VSWITCHD_STOP AT_CLEANUP # test cfm liveness propagation - OF1.5. AT_SETUP([cfm - liveness propagation - OF1.5]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.5): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 #Create 2 bridges connected by patch ports and enable cfm AT_CHECK([ovs-vsctl add-br br1 -- \ set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:56:00:00 -- \ add-port br1 p1 -- set Interface p1 type=patch \ options:peer=p0 -- \ add-port br0 p0 -- set Interface p0 type=patch \ options:peer=p1]) check_liveness 1 LIVE AT_CHECK([ovs-vsctl \ set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \ set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true]) ovs-appctl time/stop # wait for a while to stablize cfm. ovs-appctl time/warp 10100 100 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up]) CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up]) # turn cfm on p1 off, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2]) ovs-appctl time/warp 1100 100 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 1]) CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count : [[]]]) check_liveness 2 0 # turn cfm on p1 on again, should increment the cfm_flap_count on p0. AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2]) ovs-appctl time/warp 1100 100 check_liveness 3 LIVE CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count : 2]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/check-structs.at000066400000000000000000000023551514270232600232370ustar00rootroot00000000000000AT_BANNER([struct alignment checker unit tests]) m4_define([check_structs], [$top_srcdir/build-aux/check-structs]) m4_define([RUN_STRUCT_CHECKER], [AT_KEYWORDS([check-structs]) AT_DATA([test.h], [$1 ]) AT_CHECK_UNQUOTED([$PYTHON3 check_structs test.h], [$2], [$3], [$4])]) AT_SETUP([check struct tail padding]) RUN_STRUCT_CHECKER( [struct xyz { ovs_be16 x; };], [1], [], [test.h:3: warning: struct xyz needs 2 bytes of tail padding ]) AT_CLEANUP AT_SETUP([check struct internal alignment]) RUN_STRUCT_CHECKER( [struct xyzzy { ovs_be16 x; ovs_be32 y; };], [1], [], [test.h:3: warning: struct xyzzy member y is 2 bytes short of 4-byte alignment ]) AT_CLEANUP AT_SETUP([check struct declared size]) RUN_STRUCT_CHECKER( [struct wibble { ovs_be64 z; }; OFP_ASSERT(sizeof(struct wibble) == 12); ], [1], [], [test.h:4: warning: struct wibble is 8 bytes long but declared as 12 ]) AT_CLEANUP AT_SETUP([check wrong struct's declared size]) RUN_STRUCT_CHECKER( [struct moo { ovs_be64 bar; }; OFP_ASSERT(sizeof(struct moo) == 8); struct wibble { ovs_be64 z; }; OFP_ASSERT(sizeof(struct moo) == 8); ], [1], [], [test.h:8: warning: checking size of struct moo but struct wibble was most recently defined ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/checkpatch.at000077500000000000000000000441031514270232600225520ustar00rootroot00000000000000AT_BANNER([checkpatch]) OVS_START_SHELL_HELPERS # try_checkpatch PATCH [ERRORS] [checkpatch-args] # # Runs checkpatch, if installed, on the given PATCH, expecting the # specified set of ERRORS (and warnings). try_checkpatch() { # Take the patch to test from $1. Remove an initial four-space indent # from it and, if it is just headers with no body, add a null body. # If it does not have a 'Subject', add a valid one. echo "$1" | sed 's/^ //' > test.patch if grep 'Subject\:' test.patch >/dev/null 2>&1; then : else sed -i'' -e '1i\ Subject: Patch this is. ' test.patch fi if grep '---' expout >/dev/null 2>&1; then : else printf '\n---\n' >> test.patch fi # Take expected output from $2. if test -n "$2"; then echo "$2" | sed 's/^ //' > expout else : > expout fi if test -s expout; then AT_CHECK([OVS_SRC_DIR=$top_srcdir $PYTHON3 \ $top_srcdir/utilities/checkpatch.py $3 -q test.patch], [1], [stdout]) AT_CHECK([sed '/^Lines checked:/,$d' stdout], [0], [expout]) else AT_CHECK([OVS_SRC_DIR=$top_srcdir $PYTHON3 \ $top_srcdir/utilities/checkpatch.py $3 -q test.patch]) fi } # try_checkpatch_file FILENAME SOURCE [ERRORS] [CHECKPATCH-ARGS] [FILTER] # # Runs checkpatch against FILENAME with SOURCE contents, expecting the set # of specified ERRORS (and warnings). Optionally, sets [CHECKPATCH-ARGS]. # The optional FILTER can be used to adjust the expected output before # processing. try_checkpatch_file() { local FILENAME="$1" echo "$2" | sed 's/^ //' > "$FILENAME" # Take expected output from $3. if test -n "$3"; then echo "$3" | sed 's/^ //' > expout else : > expout fi if test -s expout; then AT_CHECK([OVS_SRC_DIR=$top_srcdir $PYTHON3 \ $top_srcdir/utilities/checkpatch.py $4 -q -f "$FILENAME"], [1], [stdout]) AT_CHECK([sed "$5 /^Lines checked:/d /^$/d" stdout], [0], [expout]) else AT_CHECK([OVS_SRC_DIR=$top_srcdir $PYTHON3 \ $top_srcdir/utilities/checkpatch.py $4 -q -f "$FILENAME"]) fi } # try_checkpatch_c_file SOURCE [ERRORS] [CHECKPATCH-ARGS] [FILTER] # # Runs checkpatch against "C" SOURCE contents, expecting the set of # specified ERRORS (and warnings). Optionally, sets [CHECKPATCH-ARGS]. # The optional FILTER can be used to adjust the expected output before # processing. try_checkpatch_c_file() { try_checkpatch_file "test.c" "$@" } # try_checkpatch_py_file SOURCE [ERRORS] [CHECKPATCH-ARGS] [FILTER] # # Runs checkpatch against "Python" SOURCE contents, expecting the set of # specified ERRORS (and warnings). Optionally, sets [CHECKPATCH-ARGS]. # The optional FILTER can be used to adjust the expected output before # processing. try_checkpatch_py_file() { try_checkpatch_file "test.py" "$@" } OVS_END_SHELL_HELPERS AT_SETUP([checkpatch - sign-offs]) # Sign-off for single author who is also the committer. try_checkpatch \ "Author: A Commit: A Signed-off-by: A" try_checkpatch \ "Author: A Commit: A" \ "ERROR: Author A needs to sign off." # Single author but somehow the mailing list is the author. try_checkpatch \ "Author: Foo Bar via dev Commit: A Signed-off-by: A" \ "ERROR: Author should not be mailing list." # Sign-off for single author and different committer. try_checkpatch \ "Author: A Commit: B Signed-off-by: A Signed-off-by: B" try_checkpatch \ "Author: A Commit: B" \ "ERROR: Author A needs to sign off. ERROR: Committer B needs to sign off." # Sign-off for multiple authors with one author also the committer. try_checkpatch \ "Author: A Commit: A Signed-off-by: A Co-authored-by: B Signed-off-by: B" try_checkpatch \ "Author: A Commit: A Co-authored-by: B Signed-off-by: B" \ "ERROR: Author A needs to sign off." try_checkpatch \ "Author: A Commit: A Signed-off-by: A Co-authored-by: B" \ "ERROR: Co-author B needs to sign off." try_checkpatch \ "Author: A Commit: A Co-authored-by: B" \ "ERROR: Author A needs to sign off. ERROR: Co-author B needs to sign off." # Sign-off for multiple authors and separate committer. try_checkpatch \ "Author: A Commit: C Signed-off-by: A Co-authored-by: B Signed-off-by: B Signed-off-by: C" try_checkpatch \ "Author: A Commit: C Signed-off-by: A Co-authored-by: B Signed-off-by: B" \ "ERROR: Committer C needs to sign off." # Extra sign-offs: # # - If we know the committer, one extra sign-off raises a warning. # # - If we do not know the committer, two extra sign-offs raise a warning. try_checkpatch \ "Author: A Commit: C Signed-off-by: A Co-authored-by: B Signed-off-by: B Signed-off-by: C Signed-off-by: D" \ "WARNING: Unexpected sign-offs from developers who are not authors or co-authors or committers: D" try_checkpatch \ "Author: A Signed-off-by: A Co-authored-by: B Signed-off-by: B Signed-off-by: C" try_checkpatch \ "Author: A Signed-off-by: A Co-authored-by: B Signed-off-by: B Signed-off-by: C Signed-off-by: D" \ "WARNING: Unexpected sign-offs from developers who are not authors or co-authors or committers: C, D" # Missing committer is OK, missing author is an error. try_checkpatch \ "Author: A Signed-off-by: A" try_checkpatch \ "Commit: A Signed-off-by: A" \ "ERROR: Patch lacks author." AT_CLEANUP m4_define([COMMON_PATCH_HEADER], [dnl Author: A Signed-off-by: A --- diff --git a/$([[ ! -z "$1" ]] && echo "$1" || echo "A.c") b/$([[ ! -z "$1" ]] && echo "$1" || echo "A.c") index 0000000..1111111 100644 --- a/$([[ ! -z "$1" ]] && echo "$1" || echo "A.c") +++ b/$([[ ! -z "$1" ]] && echo "$1" || echo "A.c") @@ -1,1 +1,1 @@]) AT_SETUP([checkpatch - parenthesized constructs]) for ctr in 'if' 'while' 'switch' 'HMAP_FOR_EACH' 'BITMAP_FOR_EACH_1'; do try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (first_run) { " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr ( first_run) { " \ "ERROR: Improper whitespace around control block #8 FILE: A.c:1: $ctr ( first_run) { " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (first_run ) { " \ "ERROR: Inappropriate bracing around statement #8 FILE: A.c:1: $ctr (first_run ) { " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (first_run) " \ "ERROR: Inappropriate bracing around statement #8 FILE: A.c:1: $ctr (first_run) " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr(first_run) " \ "ERROR: Improper whitespace around control block #8 FILE: A.c:1: $ctr(first_run) " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (first_run) { /* foo */ " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (first_run) { \\ " try_checkpatch \ "COMMON_PATCH_HEADER + $ctr (a) { \\ " done AT_CLEANUP AT_SETUP([checkpatch - catastrophic backtracking]) dnl Special case this rather than using the above construct because sometimes a dnl warning needs to be generated for line lengths (f.e. when the 'while' dnl keyword is used). try_checkpatch \ "COMMON_PATCH_HEADER + if (!b_ctx_in->chassis_rec || !b_ctx_in->br_int || !b_ctx_in->ovs_idl_txn) " \ "ERROR: Inappropriate bracing around statement #8 FILE: A.c:1: if (!b_ctx_in->chassis_rec || !b_ctx_in->br_int || !b_ctx_in->ovs_idl_txn) " AT_CLEANUP AT_SETUP([checkpatch - parenthesized constructs - for]) try_checkpatch \ "COMMON_PATCH_HEADER + for (init; condition; increment) { " try_checkpatch \ "COMMON_PATCH_HEADER + for ( init; condition; increment) { " \ "ERROR: Improper whitespace around control block #8 FILE: A.c:1: for ( init; condition; increment) { " try_checkpatch \ "COMMON_PATCH_HEADER + for (init; condition; increment ) { " \ "ERROR: Inappropriate bracing around statement #8 FILE: A.c:1: for (init; condition; increment ) { " try_checkpatch \ "COMMON_PATCH_HEADER + for (init; condition; increment) " \ "ERROR: Inappropriate bracing around statement #8 FILE: A.c:1: for (init; condition; increment) " try_checkpatch \ "COMMON_PATCH_HEADER + for(init; condition; increment) " \ "ERROR: Improper whitespace around control block #8 FILE: A.c:1: for(init; condition; increment) " try_checkpatch \ "COMMON_PATCH_HEADER + for (init; condition; increment) { /* foo */ " try_checkpatch \ "COMMON_PATCH_HEADER + for (init; condition; increment) { \\ " try_checkpatch \ "COMMON_PATCH_HEADER +#define SOME_FOR_EACH(a, b, c) /* Foo. */ " AT_CLEANUP AT_SETUP([checkpatch - comments]) try_checkpatch \ "COMMON_PATCH_HEADER + a = 1; /* C style comment. */ " try_checkpatch \ "COMMON_PATCH_HEADER + /* http://URL/inside/the/comment.html */ " try_checkpatch \ "COMMON_PATCH_HEADER + a = 1; // C99 style comment. " \ "ERROR: C99 style comment #8 FILE: A.c:1: a = 1; // C99 style comment. " AT_CLEANUP AT_SETUP([checkpatch - whitespace around operator]) try_checkpatch \ "COMMON_PATCH_HEADER + if (--mcs->n_refs == 0) { " try_checkpatch \ "COMMON_PATCH_HEADER + if (--mcs->n_refs==0) { " \ "WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #8 FILE: A.c:1: if (--mcs->n_refs==0) { " try_checkpatch \ "COMMON_PATCH_HEADER +char *string; +char **list; +char ***ptr_list; " try_checkpatch \ "COMMON_PATCH_HEADER +char** list; " \ "WARNING: Line lacks whitespace around operator #8 FILE: A.c:1: char** list; " try_checkpatch \ "COMMON_PATCH_HEADER +char*** list; " \ "WARNING: Line lacks whitespace around operator #8 FILE: A.c:1: char*** list; " AT_CLEANUP AT_SETUP([checkpatch - check misuse APIs]) try_checkpatch \ "COMMON_PATCH_HEADER([a.c]) + ovsrcu_barrier(); " \ "WARNING: Are you sure you need to use ovsrcu_barrier(), "\ "in most cases ovsrcu_synchronize() will be fine? #8 FILE: a.c:1: ovsrcu_barrier(); " try_checkpatch \ "COMMON_PATCH_HEADER([lib/ovs-rcu.c]) + ovsrcu_barrier(); " AT_CLEANUP AT_SETUP([checkpatch - check egrep / fgrep]) try_checkpatch \ "COMMON_PATCH_HEADER([tests/something.at]) +C_H_E_C_K([[ovs-vsctl show | grep -E 'my-port.*[[0-9]]$' | grep -F 'port']]) " try_checkpatch \ "COMMON_PATCH_HEADER([tests/something.at]) +C_H_E_C_K([[ovs-vsctl show | egrep 'my-port.*[[0-9]]$']]) " \ "ERROR: grep -E/-F should be used instead of egrep/fgrep #8 FILE: tests/something.at:1: C_H_E_C_K([[ovs-vsctl show | egrep 'my-port.*[[0-9]]$']]) " try_checkpatch \ "COMMON_PATCH_HEADER([tests/something.at]) +C_H_E_C_K([[ovs-vsctl show | fgrep 'my-port.*[[0-9]]$']]) " \ "ERROR: grep -E/-F should be used instead of egrep/fgrep #8 FILE: tests/something.at:1: C_H_E_C_K([[ovs-vsctl show | fgrep 'my-port.*[[0-9]]$']]) " AT_CLEANUP AT_SETUP([checkpatch - whitespace around cast]) try_checkpatch \ "COMMON_PATCH_HEADER + (int) a; " try_checkpatch \ "COMMON_PATCH_HEADER + (int)a; " \ "ERROR: Inappropriate spacing around cast #8 FILE: A.c:1: (int)a; " AT_CLEANUP AT_SETUP([checkpatch - malformed tags]) try_checkpatch \ " Author: A Acked by: foo... Signed-off-by: A" \ "ERROR: Acked-by tag is malformed. 1: Acked by: foo... " try_checkpatch \ " Author: A Reported at: foo... Signed-off-by: A" \ "ERROR: Reported-at tag is malformed. 1: Reported at: foo... " try_checkpatch \ " Author: A Reported by: foo... Signed-off-by: A" \ "ERROR: Reported-by tag is malformed. 1: Reported by: foo... " try_checkpatch \ " Author: A Requested by: foo... Signed-off-by: A" \ "ERROR: Requested-by tag is malformed. 1: Requested by: foo... " try_checkpatch \ " Author: A Reviewed by: foo... Signed-off-by: A" \ "ERROR: Reviewed-by tag is malformed. 1: Reviewed by: foo... " try_checkpatch \ " Author: A Submitted at: foo... Signed-off-by: A" \ "ERROR: Submitted-at tag is malformed. 1: Submitted at: foo... " try_checkpatch \ " Author: A Suggested by: foo... Signed-off-by: A" \ "ERROR: Suggested-by tag is malformed. 1: Suggested by: foo... " AT_CLEANUP AT_SETUP([checkpatch - Unicode code]) try_checkpatch \ "COMMON_PATCH_HEADER + if (snowman == ☃️) { /* Emoji + void НelloWorld() { /* Homoglyph + ة /* ;C++ /* BiDi " \ "ERROR: Inappropriate non-ascii characters detected. #8 FILE: A.c:1: if (snowman == ☃️) { /* Emoji ERROR: Inappropriate non-ascii characters detected. #9 FILE: A.c:2: void НelloWorld() { /* Homoglyph ERROR: Inappropriate non-ascii characters detected. #10 FILE: A.c:3: ة /* ;C++ /* BiDi " AT_CLEANUP m4_define([FIXES_TAG_ERROR], [dnl ERROR: \"Fixes\" tag is malformed. Use the following format: git log -1 --pretty=format:\"Fixes: %h (\\\"%s\\\")\" --abbrev=12 COMMIT_REF ]) AT_SETUP([checkpatch - Fixes tag]) try_checkpatch \ "Author: A Commit: A Fixes: 123456789abc (\"commit name\") Signed-off-by: A" try_checkpatch \ "Author: A Commit: A fixes: 123456789abc (\"commit name\") Signed-off-by: A" \ "FIXES_TAG_ERROR 1: fixes: 123456789abc (\"commit name\") " try_checkpatch \ "Author: A Commit: A Fixes: 12345678 (\"commit name\") Signed-off-by: A" \ "FIXES_TAG_ERROR 1: Fixes: 12345678 (\"commit name\") " try_checkpatch \ "Author: A Commit: A Fixes: 1234567890abcdef1234567890abcdef12345678 (\"commit name\") Signed-off-by: A" \ "FIXES_TAG_ERROR 1: Fixes: 1234567890abcdef1234567890abcdef12345678 (\"commit name\") " try_checkpatch \ "Author: A Commit: A Fixes: 123456789abc \"commit name\" Signed-off-by: A" \ "FIXES_TAG_ERROR 1: Fixes: 123456789abc \"commit name\" " try_checkpatch \ "Author: A Commit: A Fixes: 123456789abc (\"some very long commit name that doesn\'t fit into a single line, but should not be wrapped.\") Signed-off-by: A" \ "FIXES_TAG_ERROR 1: Fixes: 123456789abc (\"some very long commit name that doesn\'t fit into " AT_CLEANUP AT_SETUP([checkpatch - subject]) try_checkpatch \ "Author: A Commit: A Subject: netdev: invalid case and dot ending Signed-off-by: A" \ "WARNING: The subject summary should start with a capital. WARNING: The subject summary should end with a dot. Subject: netdev: invalid case and dot ending" try_checkpatch \ "Author: A Commit: A Subject: netdev: This is a way to long commit summary and therefor it should report a WARNING! Signed-off-by: A" \ "WARNING: The subject, ': ', is over 70 characters, i.e., 85. Subject: netdev: This is a way to long commit summary and therefor it should report a WARNING!" AT_CLEANUP AT_SETUP([checkpatch - ignore committer as signoff]) try_checkpatch \ "Author: A Commit: B Subject: netdev: Subject. Signed-off-by: A" \ "ERROR: Committer B needs to sign off." try_checkpatch \ "Author: A Commit: B Subject: netdev: Subject. Signed-off-by: A" \ "" \ "--skip-committer-signoff" AT_CLEANUP AT_SETUP([checkpatch - AUTHORS.rst existence]) try_checkpatch \ "Author: A Commit: A Subject: netdev: Subject. Signed-off-by: A " \ "" try_checkpatch \ "Author: A Commit: A Subject: netdev: Subject. Signed-off-by: A " \ "WARNING: Author 'A ' is not in the AUTHORS.rst file!" \ "-a" AT_CLEANUP AT_SETUP([checkpatch - file contents checks - bare return]) try_checkpatch_c_file \ "#include #include void foo() { return; }" \ "WARNING: Empty return followed by brace, consider omitting test.c:6: }" AT_CLEANUP AT_SETUP([checkpatch - file contents checks - parenthesized constructs]) for ctr in 'if' 'while' 'switch' 'HMAP_FOR_EACH' 'BITMAP_FOR_EACH_1'; do try_checkpatch_c_file \ "#include #include void foo() { $ctr (check_node) { something(check_node); } } " try_checkpatch_c_file \ "#include #include void foo() { $ctr ( first_run) { something(check_node); } }" \ "ERROR: Improper whitespace around control block test.c:5: $ctr ( first_run) {" done AT_CLEANUP AT_SETUP([checkpatch - spelling checks]) AT_SKIP_IF([! $PYTHON3 -c 'import enchant' >/dev/null 2>&1]) dnl First check with some default words try_checkpatch_c_file \ "#include #include /* naintenance mode */ void foo() { $ctr ( first_run ) { something(check_node); } }" \ "WARNING: Possible misspelled word: \"naintenance\" test.c:4: /* naintenance mode */" \ "-S" \ "/^Did you mean:/d" try_checkpatch_c_file \ "#include #include /* hugepage mode */ void foo() { $ctr ( first_run ) { something(check_node); } }" \ "" \ "-S" dnl Second check with some different words try_checkpatch_c_file \ "/* This code is for my private prooperty. */ " \ "WARNING: Possible misspelled word: \"prooperty\" test.c:1: /* This code is for my private prooperty. */" \ "-S" \ "/^Did you mean:/d" try_checkpatch_py_file \ "# This is a python file with an intentionaly misspelt word. # The user wants to check if it's working." \ "WARNING: Possible misspelled word: \"intentionaly\" test.py:1: # This is a python file with an intentionaly misspelt word." \ "-S" \ "/^Did you mean:/d" AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/classifier.at000066400000000000000000000677251514270232600226150ustar00rootroot00000000000000AT_BANNER([flow classifier unit tests]) m4_foreach( [testname], [[empty], [destroy-null], [single-rule], [rule-replacement], [many-rules-in-one-list], [versioned many-rules-in-one-list], [many-rules-in-one-table], [versioned many-rules-in-one-table], [many-rules-in-two-tables], [versioned many-rules-in-two-tables], [many-rules-in-five-tables], [versioned many-rules-in-five-tables]], [AT_SETUP([flow classifier - m4_bpatsubst(testname, [-], [ ])]) AT_CHECK([ovstest test-classifier m4_bpatsubst(testname, [versioned], [--versioned])], [0], [], []) AT_CLEANUP])]) AT_BANNER([flow classifier stress tests]) AT_SETUP([flow classifier - prefixes reconfiguration stress test]) AT_CHECK([ovstest test-classifier stress-prefixes], [0], [stdout]) AT_CLEANUP AT_BANNER([miniflow unit tests]) m4_foreach( [testname], [[miniflow], [minimask_has_extra], [minimask_combine]], [AT_SETUP([miniflow - m4_bpatsubst(testname, [-], [ ])]) AT_CHECK([ovstest test-classifier testname], [0], [], []) AT_CLEANUP])]) AT_BANNER([flow classifier lookup segmentation]) AT_SETUP([flow classifier - lookup segmentation]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=16,tcp,nw_dst=10.1.0.0/255.255.0.0,action=output(3) table=0 in_port=1 priority=32,tcp,nw_dst=10.1.2.15,action=output(2) table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=80,action=drop table=0 in_port=1 priority=0,ip,action=drop table=0 in_port=2 priority=16,tcp,nw_dst=192.168.0.0/255.255.0.0,action=output(1) table=0 in_port=2 priority=0,ip,action=drop table=0 in_port=3 priority=16,tcp,nw_src=10.1.0.0/255.255.0.0,action=output(1) table=0 in_port=3 priority=0,ip,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=2,nw_dst=192.168.0.0/16,nw_frag=no Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=11.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_dst=11.0.0.0/8,nw_frag=no Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80 Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=0x40/0xfff0 Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([flow classifier - lookup segmentation - final stage]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=33,tcp,tp_dst=80,tcp_flags=+psh,action=output(2) table=0 in_port=1 priority=0,ip,action=drop table=0 in_port=2 priority=16,icmp6,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=1000::1 ,action=output(1) table=0 in_port=2 priority=0,ip,action=drop table=0 in_port=3 action=resubmit(,1) table=1 in_port=3 priority=45,ct_state=+trk+rpl,ct_nw_proto=6,ct_tp_src=3/0x1,tcp,tp_dst=80,tcp_flags=+psh,action=output(2) table=1 in_port=3 priority=10,ip,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=syn'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=syn|ack'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=ack|psh'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=+psh Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=0x40/0xfff0,tcp_flags=-psh Datapath actions: drop ]) dnl Having both the port and the tcp flags in the resulting megaflow below dnl is redundant, but that is how ports trie logic is implemented. AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=81'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=81,tcp_flags=-psh Datapath actions: drop ]) dnl nd_target is redundant in the megaflow below and it is also not relevant dnl for an icmp reply. Datapath may discard that match, but it is OK as long dnl as we have prerequisites (icmp_type) in the match as well. AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=128,icmpv6_code=0"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x80/0xfc,nd_target=:: Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=:: Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0,nd_target=1000::1"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=1000::1 Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0,nd_target=1000::2"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=1000::2 Datapath actions: drop ]) dnl Check that ports' mask doesn't affect ct ports. AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=3,ct_state=trk|rpl,ct_nw_proto=6,ct_tp_src=3,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=psh'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,ct_state=+rpl+trk,ct_nw_proto=6,ct_tp_src=0x1/0x1,eth,tcp,in_port=3,nw_frag=no,tp_dst=80,tcp_flags=+psh Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=3,ct_state=trk|rpl,ct_nw_proto=6,ct_tp_src=3,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79,tcp_flags=psh'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,ct_state=+rpl+trk,ct_nw_proto=6,ct_tp_src=0x1/0x1,eth,tcp,in_port=3,nw_frag=no,tp_dst=0x40/0xfff0,tcp_flags=+psh Datapath actions: drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_BANNER([flow classifier prefix lookup]) AT_SETUP([flow classifier - prefix lookup]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0], [0], [ignore], []) AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=16,tcp,nw_dst=10.1.0.0/255.255.0.0,action=output(3) table=0 in_port=1 priority=32,tcp,nw_dst=10.1.2.0/255.255.255.0,tp_src=79,action=output(2) table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=80,action=drop table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=8080,action=output(2) table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=192,action=output(2) table=0 in_port=1 priority=0,ip,action=drop table=0 in_port=2 priority=16,tcp,nw_dst=192.168.0.0/255.255.0.0,action=output(1) table=0 in_port=2 priority=0,ip,action=drop table=0 in_port=3 priority=16,tcp,nw_src=10.1.0.0/255.255.0.0,action=output(1) table=0 in_port=3 priority=0,ip,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # nw_dst and nw_src should be on by default AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=192.168.0.0/16,nw_frag=no Datapath actions: drop ]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=ipv6_label], [0]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst,nw_src,tun_dst,tun_src,ipv6_src], [1], [], [ovs-vsctl: nw_dst,nw_src,tun_dst,tun_src,ipv6_src: 5 value(s) specified but the maximum number is 4 ]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst,nw_dst], [1], [], [ovs-vsctl: nw_dst,nw_dst: set contains duplicate value ]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst], [0]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=192.168.0.0/16,nw_frag=no Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=2,nw_dst=192.168.0.0/16,nw_frag=no Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80 Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_src=0x0/0xffc0,tp_dst=0x40/0xfff0 Datapath actions: 3 ]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=none], [0]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.3.16,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.3.16,nw_frag=no Datapath actions: 3 ]) OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"]) AT_CLEANUP AT_SETUP([flow classifier - prefix lookup defaults]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=16,tcp,nw_src=10.2.0.0/16,nw_dst=10.1.0.0/255.255.0.0,action=output(3) table=0 in_port=1 priority=33,tcp,nw_src=10.2.2.14,nw_dst=10.1.2.15,tp_dst=80,action=drop table=0 in_port=1 priority=33,tcp,nw_src=10.2.2.14,nw_dst=10.1.2.15,tp_dst=8080,action=output(2) table=0 in_port=1 priority=16,tcp,nw_src=192.168.0.0/15,nw_dst=192.168.0.0/255.255.0.0,action=output(3) table=0 in_port=1 priority=0,ip,action=drop table=0 in_port=1 priority=16,tcp6,ipv6_src=aaaa:bbbb:c:d:e:f::/96,ipv6_dst=aaaa:bbbb:c:d:a:f:0000:0000/96,action=output(3) table=0 in_port=1 priority=33,tcp6,ipv6_src=aaaa:bbbb:c:d:e:f:1:2,ipv6_dst=aaaa:bbbb:c:d:a:f:1:2,tp_dst=80,action=drop table=0 in_port=1 priority=33,tcp6,ipv6_src=aaaa:bbbb:c:d:e:f:1:2,ipv6_dst=aaaa:bbbb:c:d:a:f:1:2,tp_dst=8080,action=output(2) table=0 in_port=1 priority=16,tcp6,ipv6_src=cccc:dddd:a:b:c:c::/95,ipv6_dst=cccc:dddd:a:b:c:c::/95,action=output(3) table=0 in_port=1 priority=0,ip6,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl nw_dst and nw_src should be on by default. AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800, nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128, tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_src=192.168.0.0/15,nw_dst=192.168.0.0/16,nw_frag=no Datapath actions: 3 ]) dnl ipv6_dst and ipv6_src should also be on by default. AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x86dd, ipv6_src=cccc:dddd:a:b:c:d:7:8,ipv6_dst=cccc:dddd:a:b:c:d:7:9, nw_proto=6,nw_tos=0,nw_ttl=128, tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tcp6,in_port=1,ipv6_src=cccc:dddd:a:b:c:c::/95,ipv6_dst=cccc:dddd:a:b:c:c::/95,nw_frag=no Datapath actions: 3 ]) dnl Turn optimizations off and check that we fall back to exact match. AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- \ --id=@N1 create Flow_Table name=t0], [0], [ignore]) AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=none]) AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800, nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128, tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_frag=no Datapath actions: 3 ]) AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x86dd, ipv6_src=cccc:dddd:a:b:c:d:7:8,ipv6_dst=cccc:dddd:a:b:c:d:7:9, nw_proto=6,nw_tos=0,nw_ttl=128, tp_src=8,tp_dst=80'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tcp6,in_port=1,ipv6_src=cccc:dddd:a:b:c:d:7:8,ipv6_dst=cccc:dddd:a:b:c:d:7:9,nw_frag=no Datapath actions: 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([flow classifier - ipv6 ND dependency]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0,priority=100,ipv6,ipv6_src=1000::/10 actions=resubmit(,1) table=0,priority=0 actions=NORMAL table=1,priority=110,ipv6,ipv6_dst=1000::3 actions=resubmit(,2) table=1,priority=100,ipv6,ipv6_dst=1000::4 actions=resubmit(,2) table=1,priority=0 actions=NORMAL table=2,priority=120,icmp6,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=1000::1 actions=NORMAL table=2,priority=100,tcp actions=NORMAL table=2,priority=100,icmp6 actions=NORMAL table=2,priority=0 actions=NORMAL ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # test ICMPv6 echo request (which should have no nd_target field) AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,icmpv6_type=128,icmpv6_code=0"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=1,dl_src=f6:d2:b0:19:5e:7b,dl_dst=d2:49:19:91:78:fe,ipv6_src=1000::/10,ipv6_dst=1000::4,nw_ttl=0,nw_frag=no Datapath actions: 100,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_BANNER([conjunctive match]) AT_SETUP([single conjunctive match]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 AT_DATA([flows.txt], [dnl conj_id=1,actions=3 priority=100,ip,ip_src=10.0.0.1,actions=conjunction(1,1/2) priority=100,ip,ip_src=10.0.0.4,actions=conjunction(1,1/2) priority=100,ip,ip_src=10.0.0.6,actions=conjunction(1,1/2) priority=100,ip,ip_src=10.0.0.7,actions=conjunction(1,1/2) priority=100,ip,ip_dst=10.0.0.2,actions=conjunction(1,2/2) priority=100,ip,ip_dst=10.0.0.5,actions=conjunction(1,2/2) priority=100,ip,ip_dst=10.0.0.7,actions=conjunction(1,2/2) priority=100,ip,ip_dst=10.0.0.8,actions=conjunction(1,2/2) priority=100,ip,ip_src=10.0.0.1,ip_dst=10.0.0.4,actions=4 priority=100,ip,ip_src=10.0.0.3,ip_dst=10.0.0.5,actions=5 priority=0 actions=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for src in 0 1 2 3 4 5 6 7; do for dst in 0 1 2 3 4 5 6 7; do if test $src$dst = 14; then out=4 elif test $src$dst = 35; then out=5 else out=2 case $src in [[1467]]) case $dst in [[2578]]) out=3 ;; esac ;; esac fi AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x0800,nw_src=10.0.0.$src,nw_dst=10.0.0.$dst"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out ]) dnl Check detailed output for conjunctive match. if test $out = 3; then AT_CHECK_UNQUOTED([cat stdout | grep conj\\. | sort], [0], [dnl -> conj. priority=100,ip,nw_dst=10.0.0.$dst -> conj. priority=100,ip,nw_src=10.0.0.$src ]) fi done done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([multiple conjunctive match]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 AT_DATA([flows.txt], [dnl conj_id=1,actions=1 conj_id=2,actions=2 conj_id=3,actions=3 priority=5,ip,ip_src=20.0.0.0/8,actions=conjunction(1,1/2),conjunction(2,1/2) priority=5,ip,ip_src=10.1.0.0/16,actions=conjunction(1,1/2),conjunction(3,2/3) priority=5,ip,ip_src=10.2.0.0/16,actions=conjunction(1,1/2),conjunction(2,1/2) priority=5,ip,ip_src=10.1.3.0/24,actions=conjunction(1,1/2),conjunction(3,2/3) priority=5,ip,ip_src=10.1.4.5/32,actions=conjunction(1,1/2),conjunction(2,1/2) priority=5,ip,ip_dst=20.0.0.0/8,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.1.0.0/16,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.2.0.0/16,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.1.3.0/24,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.1.4.5/32,actions=conjunction(1,2/2) priority=5,ip,ip_dst=30.0.0.0/8,actions=conjunction(2,2/2),conjunction(3,1/3) priority=5,ip,ip_dst=40.5.0.0/16,actions=conjunction(2,2/2),conjunction(3,1/3) priority=5,tcp,tcp_dst=80,actions=conjunction(3,3/3) priority=5,tcp,tcp_dst=443,actions=conjunction(3,3/3) priority=5,tcp,tcp_src=80,actions=conjunction(3,3/3) priority=5,tcp,tcp_src=443,actions=conjunction(3,3/3) priority=0,actions=4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for a0 in \ '1 20.0.0.1' \ '2 10.1.0.1' \ '3 10.2.0.1' \ '4 10.1.3.1' \ '5 10.1.4.5' \ '6 1.2.3.4' do for b0 in \ '1 20.0.0.1' \ '2 10.1.0.1' \ '3 10.2.0.1' \ '4 10.1.3.1' \ '5 10.1.4.5' \ '6 30.0.0.1' \ '7 40.5.0.1' \ '8 1.2.3.4' do for c0 in '1 80' '2 443' '3 8080'; do for d0 in '1 80' '2 443' '3 8080'; do set $a0; a=$1 ip_src=$2 set $b0; b=$1 ip_dst=$2 set $c0; c=$1 tcp_src=$2 set $d0; d=$1 tcp_dst=$2 case $a$b$c$d in [[12345]][[12345]]??) out=1 ;; [[135]][[67]]??) out=2 ;; [[24]][[67]][[12]]? | [[24]][[67]]?[[12]]) out=3 ;; *) out=4 esac AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=5,dl_type=0x0800,nw_proto=6,nw_src=$ip_src,nw_dst=$ip_dst,tcp_src=$tcp_src,tcp_dst=$tcp_dst"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out ]) done done done done OVS_VSWITCHD_STOP AT_CLEANUP # In conjunctive match, we can find some soft matches that turn out not to be a # real match. Usually, that's the end of the road--there is no real match. # But if there is a flow identical to one of the flows that was a soft match, # except with a lower priority, then we have to try again with that lower # priority flow. This test checks this special case. AT_SETUP([conjunctive match priority fallback]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 7 AT_DATA([flows.txt], [dnl conj_id=1,actions=1 conj_id=3,actions=3 priority=5,ip,ip_src=10.0.0.1,actions=conjunction(1,1/2) priority=5,ip,ip_src=10.0.0.2,actions=conjunction(1,1/2) priority=5,ip,ip_dst=10.0.0.1,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.0.0.2,actions=conjunction(1,2/2) priority=5,ip,ip_dst=10.0.0.3,actions=conjunction(1,2/2) priority=4,ip,ip_src=10.0.0.3,ip_dst=10.0.0.2,actions=2 priority=3,ip,ip_src=10.0.0.1,actions=conjunction(3,1/2) priority=3,ip,ip_src=10.0.0.3,actions=conjunction(3,1/2) priority=3,ip,ip_dst=10.0.0.2,actions=conjunction(3,2/2) priority=3,ip,ip_dst=10.0.0.3,actions=conjunction(3,2/2) priority=3,ip,ip_dst=10.0.0.4,actions=conjunction(3,2/2) priority=2,ip,ip_dst=10.0.0.1,actions=4 priority=1,ip,ip_src=10.0.0.1,ip_dst=10.0.0.5,actions=5 priority=0,actions=6 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for src in 0 1 2 3; do for dst in 0 1 2 3 4 5; do case $src$dst in [[12]][[123]]) out=1 ;; 32) out=2 ;; [[13]][[234]]) out=3 ;; ?1) out=4 ;; 15) out=5 ;; *) out=6 esac AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=7,dl_type=0x0800,nw_src=10.0.0.$src,nw_dst=10.0.0.$dst"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out ]) done done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conjunctive match and other actions]) OVS_VSWITCHD_START # It's OK to use "conjunction" actions with "note" actions. AT_CHECK([ovs-ofctl add-flow br0 'actions=conjunction(3,1/2),note:41.42.43.44.45.46']) AT_CHECK([ovs-ofctl add-flow br0 'actions=note:41.42.43.44.45.46,conjunction(3,1/2)']) # It's not OK to use "conjunction" actions with other types of actions. AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' add-flow br0 'actions=output:1,conjunction(3,1/2)'], [1], [], [dnl ovs-ofctl: "conjunction" actions may be used along with "note" but not any other kind of action (such as the "output" action used here) ]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' add-flow br0 'actions=conjunction(3,1/2),output:1'], [1], [], [dnl ovs-ofctl: "conjunction" actions may be used along with "note" but not any other kind of action (such as the "output" action used here) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conjunctive match with same priority]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl conj_id=1,actions=2 conj_id=2,actions=drop priority=10,ip,ip_dst=10.0.0.1,actions=conjunction(1,1/2) priority=10,ip,ip_src=10.0.0.2,actions=conjunction(1,2/2) priority=10,ip,ip_dst=10.0.0.3,actions=conjunction(2,1/2) priority=10,ip,in_port=1,actions=conjunction(2,2/2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Check that "priority=10,ip,in_port=1,actions=conjunction(2,2/2)" is # correctly excluded from the output. AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x0800,nw_dst=10.0.0.1,nw_src=10.0.0.2" | grep conj\\. | sort], [0], [dnl -> conj. priority=10,ip,nw_dst=10.0.0.1 -> conj. priority=10,ip,nw_src=10.0.0.2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conjunctive match with metadata]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=1,len=8}->tun_metadata1"]) AT_DATA([flows.txt], [dnl conj_id=7,actions=drop priority=5,tun_metadata0=0x1,actions=conjunction(7,1/2) priority=5,tun_metadata1=0x2,actions=conjunction(7,2/2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Check that tunnel metadata is included in the output. AT_CHECK([ovs-appctl ofproto/trace br0 "tun_metadata0=0x1,tun_metadata1=0x2,in_port=br0" | grep conj\\. | sort], [0], [dnl -> conj. priority=5,tun_metadata0=0x1 -> conj. priority=5,tun_metadata1=0x2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conjunctive match with or without port map]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl conj_id=1,actions=drop conj_id=2,actions=drop priority=10,ip,actions=conjunction(1,1/2),conjunction(2,1/2) priority=10,in_port=p1,actions=conjunction(1,2/2) priority=10,in_port=p2,actions=conjunction(1,2/2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p1" --names | grep conj\\. | sort], [0], [dnl -> conj. priority=10,in_port=p1 -> conj. priority=10,ip ]) AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p2" | grep conj\\. | sort], [0], [dnl -> conj. priority=10,in_port=2 -> conj. priority=10,ip ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conjunctive match with resubmit]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl conj_id=1,actions=resubmit(,2) priority=10,ip,actions=conjunction(1,1/2) priority=10,in_port=p1,actions=conjunction(1,2/2) priority=10,in_port=p2,actions=conjunction(1,2/2) table=2,conj_id=7,actions=resubmit(,3) table=2,priority=20,ip,actions=conjunction(7,1/2) table=2,priority=20,in_port=p1,actions=conjunction(7,2/2) table=2,priority=20,in_port=p2,actions=conjunction(7,2/2) table=3,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Check that conj_flows are reset for each table and that they are output # exactly once. AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p1" --names | grep conj\\. | sort], [0], [dnl -> conj. priority=10,in_port=p1 -> conj. priority=10,ip -> conj. priority=20,in_port=p1 -> conj. priority=20,ip ]) OVS_VSWITCHD_STOP AT_CLEANUP # Flow classifier a packet with excess of padding. AT_SETUP([flow classifier - packet with extra padding]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl priority=5,ip,ip_dst=1.1.1.1,actions=1 priority=5,ip,ip_dst=1.1.1.2,actions=2 priority=0,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) packet=00020202020000010101010008004500001c00010000401176cc01010101010101020d6a00350008ee3a AT_CHECK([ovs-appctl ofproto/trace br0 in_port=1 $packet] , [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_dst=1.1.1.2,nw_frag=no Datapath actions: 2 ]) # normal packet plus 255 bytes of padding (8bit padding). # 255 * 2 = 510 padding=$(printf '%*s' 510 | tr ' ' '0') AT_CHECK([ovs-appctl ofproto/trace br0 in_port=1 ${packet}${padding}] , [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_dst=1.1.1.2,nw_frag=no Datapath actions: 2 ]) # normal packet plus padding up to 65535 bytes of length (16bit limit). # 65535 - 43 = 65492 # 65492 * 2 = 130984 padding=$(printf '%*s' 130984 | tr ' ' '0') AT_CHECK([ovs-appctl ofproto/trace br0 in_port=1 ${packet}${padding}], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_dst=1.1.1.2,nw_frag=no Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/completion.at000066400000000000000000000543021514270232600226250ustar00rootroot00000000000000AT_BANNER([appctl bashcomp unit tests]) m4_define([GET_FORMAT], [ echo "$@" | grep -A 1 -- "Command format" | tail -n+2 ]) m4_define([GET_EXPAN], [ echo "$@" | grep -- "available completions for keyword" \ | sed -e 's/^[ ]*//g;s/[ ]*$//g' ]) m4_define([GET_AVAIL], [ echo "$@" | sed -e '1,/Available/d' | tail -n+2 ]) m4_define([GET_COMP_STR], [ echo "available completions for keyword \"$1\": $2" \ | sed -e 's/[ ]*$//g' ]) AT_SETUP([appctl-bashcomp - basic verification]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START # complete ovs-appctl [TAB] # complete ovs-dpctl [TAB] # complete ovs-ofctl [TAB] # complete ovsdb-tool [TAB] m4_foreach( [test_command], [[ovs-appctl], [ovs-dpctl], [ovs-ofctl], [ovsdb-tool]], [ INPUT="$(bash ovs-appctl-bashcomp.bash debug test_command TAB 2>&1)" MATCH="$(test_command --option | sort | sed -n '/^--.*/p' | cut -d '=' -f1) $(test_command list-commands | tail -n +2 | cut -c3- | cut -d ' ' -f1 | sort)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl ${MATCH} ])]) # complete ovs-appctl --tar[TAB] INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl --tar 2>&1)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl --target ]) # complete ovs-appctl --target [TAB] INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl --target TAB 2>&1)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl ovs-ofctl ovs-vswitchd ovsdb-server ]) # complete ovs-appctl --target ovs-vswitchd [TAB] # complete ovs-appctl --target ovsdb-server [TAB] # complete ovs-appctl --target ovs-ofctl [TAB] AT_CHECK([ovs-ofctl monitor br0 --detach --no-chdir --pidfile]) m4_foreach( [target_daemon], [[ovs-vswitchd], [ovsdb-server], [ovs-ofctl]], [ INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl --target target_daemon TAB 2>&1)" MATCH="$(ovs-appctl --option | sort | sed -n '/^--.*/p' | cut -d '=' -f1) $(ovs-appctl --target target_daemon list-commands | tail -n +2 | cut -c3- | cut -d ' ' -f1 | sort)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl ${MATCH} ])]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP # complex completion check - bfd/set-forwarding # bfd/set-forwarding [interface] normal|false|true # test expansion of 'interface' AT_SETUP([appctl-bashcomp - complex completion check 1]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START(add-port br0 p0 -- set Interface p0 type=dummy) # check the top level completion. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl bfd/set-forwarding TAB 2>&1)" MATCH="$(GET_COMP_STR([normal], []) GET_COMP_STR([false], []) GET_COMP_STR([true], []) GET_COMP_STR([interface], [p0]))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl p0 ]) # set argument to 'true', there should be no more completions. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl bfd/set-forwarding true TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e '/./,$!d'], [0], [dnl ]) # set argument to 'p1', there should still be the completion for booleans. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl bfd/set-forwarding p1 TAB 2>&1)" MATCH="$(GET_COMP_STR([normal], []) GET_COMP_STR([false], []) GET_COMP_STR([true], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) # set argument to 'p1 false', there should still no more completions. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl bfd/set-forwarding p1 false TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e '/./,$!d'], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP # complex completion check - lacp/show # lacp/show [port] # test expansion on 'port' AT_SETUP([appctl-bashcomp - complex completion check 2]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START(add-port br0 p0 -- set Interface p0 type=dummy \ -- add-port br0 p1 -- set Interface p1 type=dummy) # check the top level completion. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl lacp/show TAB 2>&1)" MATCH="$(GET_COMP_STR([port], [br0 p0 p1]))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl br0 p0 p1 ]) # set argument to 'p1', there should be no more completions. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl lacp/show p1 TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e '/./,$!d'], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP # complex completion check - ofproto/trace # ofproto/trace {[dp_name] odp_flow | bridge br_flow} [OPTIONS] [-generate|packet] # test expansion on 'dp|dp_name' and 'bridge' AT_SETUP([appctl-bashcomp - complex completion check 3]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START(add-port br0 p0 -- set Interface p0 type=dummy \ -- add-port br0 p1 -- set Interface p1 type=dummy) # check the top level completion. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace TAB 2>&1)" MATCH="$(GET_COMP_STR([bridge], [br0]) GET_COMP_STR([odp_flow], []) GET_COMP_STR([dp_name], [ovs-dummy]))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl br0 ovs-dummy ]) # set argument to 'ovs-dummy', should go to the dp-name path. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace ovs-dummy TAB 2>&1)" MATCH="$(GET_COMP_STR([odp_flow], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) # set odp_flow to some random string, should go to the next level. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace ovs-dummy "in_port(123),mac(),ip,tcp" TAB 2>&1)" MATCH="$(GET_COMP_STR([-generate], [-generate]) GET_COMP_STR([packet], []) GET_COMP_STR([OPTIONS...], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl -generate ]) # set packet to some random string, there should be no more completions. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace ovs-dummy "in_port(123),mac(),ip,tcp" "ABSJDFLSDJFOIWEQR" TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e '/./,$!d'], [0], [dnl ]) # set argument to 'br0', should go to the bridge path. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace br0 TAB 2>&1)" MATCH="$(GET_COMP_STR([br_flow], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) # set argument to some random string, should go to the odp_flow path. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ofproto/trace "in_port(123),mac(),ip,tcp" TAB 2>&1)" MATCH="$(GET_COMP_STR([-generate], [-generate]) GET_COMP_STR([packet], []) GET_COMP_STR([OPTIONS...], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl -generate ]) OVS_VSWITCHD_STOP AT_CLEANUP # complex completion check - vlog/set # vlog/set {spec | PATTERN:destination:pattern} # test non expandable arguments AT_SETUP([appctl-bashcomp - complex completion check 4]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START # check the top level completion. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl vlog/set TAB 2>&1)" MATCH="$(GET_COMP_STR([PATTERN:destination:pattern], []) GET_COMP_STR([spec], []))" AT_CHECK_UNQUOTED([GET_EXPAN(${INPUT})], [0], [dnl ${MATCH} ]) # check the available completions. AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) # set argument to random 'abcd', there should be no more completions. INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl vlog/set abcd TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e '/./,$!d'], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl-bashcomp - negative test]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) OVS_VSWITCHD_START(add-port br0 p0 -- set Interface p0 type=dummy) # negative test - incorrect subcommand INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ERROR 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e 's/[ \t]*$//' | sed -e '/./,$!d'], [0]) INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl ERROR TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e 's/[ \t]*$//' | sed -e '/./!d'], [0], [dnl ]) # negative test - no ovs-vswitchd # negative test - no ovsdb-server # negative test - no ovs-ofctl # should not see any error. OVS_VSWITCHD_STOP m4_foreach( [target_daemon], [[ovs-vswitchd], [ovsdb-server], [ovs-ofctl]], [ INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl --target target_daemon TAB 2>&1)" MATCH="$(ovs-appctl --option | sort | sed -n '/^--.*/p' | cut -d '=' -f1)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0], [dnl ${MATCH} ]) INPUT="$(bash ovs-appctl-bashcomp.bash debug ovs-appctl --target target_daemon ERROR SUBCMD TAB 2>&1)" AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e 's/[ \t]*$//' | sed -e '/./!d'], [0], [dnl ])]) # negative test - do not match on nested option INPUT="$(bash ovs-appctl-bashcomp.bash debug ovsdb-tool create TAB 2>&1)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vsctl bashcomp unit tests]) m4_define([PREPARE_MATCH_NOSPACE], [ echo "$@" | tr ' ' '\n' | sed -e '/^$/d' | sort -u ]) m4_define([PREPARE_MATCH_SPACE], [ echo "$@" | tr ' ' '\n' | sed -e '/^$/d' | sed -e 's/$/ /g' | sort -u ]) AT_SETUP([vsctl-bashcomp - basic verification]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) AT_SKIP_IF([eval 'test ${BASH_VERSINFO[[0]]} -lt 4']) OVS_VSWITCHD_START # complete ovs-vsctl --db=* [TAB] TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n') $(ovs-vsctl --options | grep -- '--' | sed -e 's/=.*$/=/g')" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--db=unix:$OVS_RUNDIR/db.sock "], [0], [dnl ${MATCH} ]) # complete ovs-vsctl [TAB] AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test ""], [0], [dnl ${MATCH} ]) # complete on global options. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--dry-run "], [0], [dnl ${MATCH} ]) AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--dry-run --pretty "], [0], [dnl ${MATCH} ]) # complete on local options. TMP="$(ovs-vsctl --commands | grep -- '--may-exist' | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n' | grep -v -- '--may-exist')" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--may-exist "], [0], [dnl ${MATCH} ]) # # test !, +, ?, *. # # test !. no following arguments are expanded. TMP="$(ovsdb-client --no-heading list-tables)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set "], [0], [dnl ${MATCH} ]) # test ?. will show completions for both current and following arguments. ovs-vsctl br-set-external-id br0 bridge-id br0 MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], [0], [dnl ${MATCH} ]) # test *. argument with this prefix could be completed for zero or more times. TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "destroy Bridge "], [0], [dnl ${MATCH} ]) AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "destroy Bridge br0 "], [0], [dnl ${MATCH} ]) # test +. the first time, an argument is required, after that, it becomes '*'. TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 "], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_key=123 "], [0], [dnl ${MATCH} ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([vsctl-bashcomp - argument completion]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) AT_SKIP_IF([eval 'test ${BASH_VERSINFO[[0]]} -lt 4']) OVS_VSWITCHD_START( [add-br br1 -- \ set bridge br1 datapath-type=dummy -- \ add-br foo -- \ set bridge foo datapath-type=dummy -- \ add-br --weird-br_name -- \ set bridge --weird-br_name datapath-type=dummy -- \ add-port br0 br0p0 -- set Interface br0p0 type=dummy -- \ add-port br0 br0p1 -- set Interface br0p1 type=dummy -- \ add-port br1 br1p0 -- set Interface br1p0 type=dummy -- \ add-port br1 br1p1 -- set Interface br1p1 type=dummy -- \ add-port foo bar -- set Interface bar type=dummy ]) # # test completion functions defined in '_OVS_VSCTL_ARG_COMPLETION_FUNCS'. # therein, the different argument prefixes are also tested at the same time. # # A space is appended to each output if completion is configured without # 'nospace' option. # # # test: _ovs_vsctl_complete_table # TMP="$(ovsdb-client --no-heading list-tables)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set "], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_SPACE(Open_vSwitch))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Open"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_record # TMP="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d '\"')" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Open_vSwitch "], [0], [dnl ${MATCH} ]) TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_bridge # TMP="$(ovs-vsctl list-br)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-to-vlan "], [0], [dnl ${MATCH} ]) # this also helps check the '_ovs_vsctl_check_startswith_string'. MATCH="$(PREPARE_MATCH_SPACE(--weird-br_name))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-to-vlan --"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_port # TMP="$(ovs-vsctl --no-heading --columns=name list Port | tr -d '\"')" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "port-to-br "], [0], [dnl ${MATCH} ]) # complete on ports in particular bridge. TMP="$(ovs-vsctl list-ports br0)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "del-port br0 "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_iface # for br in `ovs-vsctl list-br`; do TMP="${TMP} $(ovs-vsctl list-ifaces $br)" done MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "iface-to-br "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_bridge_fail_mode # MATCH="$(PREPARE_MATCH_SPACE(standalone secure))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-fail-mode br0 "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_key # AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], [0], [dnl ]) # since there is no key added yet, we will only get our own input. MATCH="$(PREPARE_MATCH_SPACE(test_key))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 test_key"], [0], [dnl ${MATCH} ]) # now add a key, as we should see it. ovs-vsctl br-set-external-id br0 bridge-id br0 MATCH="$(PREPARE_MATCH_SPACE(bridge-id))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_value # # should just return the user input. MATCH="$(PREPARE_MATCH_SPACE(test_value --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "br-set-external-id br0 bridge-id test_value"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_column # TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Open_vSwitch | tr -d ':' | cut -d' ' -f1)" UUID="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d ' ')" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Open_vSwitch $UUID "], [0], [dnl ${MATCH} ]) TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | tr -d ':' | cut -d' ' -f1)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Bridge br0 "], [0], [dnl ${MATCH} ]) # the 'clear' command requires one or more (+) COLUMN. # so, with one specified COLUMN 'other_config', it should still complete on # COLUMNs, plus '--'. MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "clear Bridge br0 other_config "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_key_value # # with no key available, should always get user input. MATCH="$(PREPARE_MATCH_NOSPACE(random_key))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key"], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_NOSPACE(abc))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key=abc"], [0], [dnl ${MATCH} ]) # now add two random keys. ovs-vsctl set Bridge br0 other_config:random_key1=abc other_config:random_val1=xyz MATCH="$(PREPARE_MATCH_NOSPACE(random_key1= random_val1=))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config ran"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_column_optkey_value # # at first, we should complete on column. TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 "], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_NOSPACE(other_config:))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other"], [0], [dnl ${MATCH} ]) # then, with the ':' we should complete on key. TMP="$(ovs-vsctl --no-heading --columns=other_config list Bridge br0 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1)" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:"], [0], [dnl ${MATCH} ]) # finally, if user fill in some value, we should just complete on user input. MATCH="$(PREPARE_MATCH_NOSPACE(random_val1))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_val1=12345"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_filename # touch private_key certificate MATCH="$(PREPARE_MATCH_SPACE(private_key))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-ssl priva"], [0], [dnl ${MATCH} ]) MATCH="$(PREPARE_MATCH_SPACE(certificate))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-ssl private_key cer"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_target # MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: ssl: tcp: unix:))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager "], [0], [dnl ${MATCH} ]) # filename completion on unix, punix. MATCH="$(PREPARE_MATCH_NOSPACE(testsuite.log))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager unix:test"], [0], [dnl ${MATCH} ]) # no completion on other type, just return available types. # in real environment, bash will not complete on anything. MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: tcp: unix:))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set-manager ssl:something"], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_new # # test 'add-br' AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br "], [0], [dnl --- BEGIN MESSAGE Enter a new bridge: > ovs-vsctl add-br --- END MESSAGE ]) # user input does not change the output. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br new-br"], [0], [dnl --- BEGIN MESSAGE Enter a new bridge: > ovs-vsctl add-br new-br--- END MESSAGE ]) # after specifying the new bridge name, we should complete on parent bridge. TMP="$(ovs-vsctl list-br)" MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-br new-br "], [0], [dnl ${MATCH} ]) # test 'add-port' # after specifying the new port name, we should complete on the column part # of '*COLUMN?:KEY=VALUE'. TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Port | awk '/key.*value/ { print $1":"; next } { print $1; next }')" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-port br0 new-port "], [0], [dnl ${MATCH} ]) # # test: _ovs_vsctl_complete_dashdash # # after '--', there should be no global options available for completion. TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n')" MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "init -- "], [0], [dnl ${MATCH} ]) TMP="$(ovs-vsctl --no-heading --columns=name,_uuid list Port | tr -d '\"')" MATCH="$(PREPARE_MATCH_SPACE(${TMP} newp1 newp2))" AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "add-port br0 newp1 -- add-port br1 newp2 -- set Port "], [0], [dnl ${MATCH} ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([vsctl-bashcomp - negative test]) AT_SKIP_IF([test -z ${BASH_VERSION+x}]) AT_SKIP_IF([eval 'test ${BASH_VERSINFO[[0]]} -lt 4']) OVS_VSWITCHD_START # complete non-matching command. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "invalid"], [0], [dnl ]) # complete after invalid command. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "invalid argu"], [0], [dnl ]) # complete non-matching end argument. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set INVALID_"], [0], [dnl ]) # complete after invalid intermediate argument. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "set INVALID_TBL "], [1], [dnl --- BEGIN MESSAGE Cannot complete 'INVALID_TBL' at index 3: > ovs-vsctl set INVALID_TBL --- END MESSAGE]) # complete ovs-vsctl --db=wrongdb [TAB] # should return 1 and show nothing. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test "--db=wrongdb"], [1], []) OVS_VSWITCHD_STOP # delete ovsdb-server and try again. AT_CHECK_UNQUOTED([bash ovs-vsctl-bashcomp.bash test ""], [1], []) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/daemon-py.at000066400000000000000000000165271514270232600223540ustar00rootroot00000000000000AT_BANNER([daemon unit tests - Python3]) AT_SETUP([daemon - Python3]) # Skip this test for Windows, echo $! gives shell pid instead of parent process AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([python daemon]) on_exit 'kill $(cat *.pid)' pidfile=test-daemon.py.pid # Start the daemon and wait for the pidfile to get created # and that its contents are the correct pid. AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile & echo $!], [0], [stdout]) pid=$(cat stdout) OVS_WAIT_UNTIL([test -s $pidfile], [kill $pid]) AT_CHECK([test $pid = $(cat $pidfile)]) AT_CHECK([kill -0 $pid]) # Kill the daemon and make sure that the pidfile gets deleted. kill $pid OVS_WAIT_WHILE([kill -0 $pid]) AT_CHECK([test ! -e $pidfile]) AT_CLEANUP AT_SETUP([daemon --monitor - Python3]) # Skip this test for Windows, echo $! gives shell pid instead of parent process AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill $(cat *.pid)' pidfile=test-daemon.py.pid # Start the daemon and wait for the pidfile to get created. AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --monitor & echo $!], [0], [stdout]) monitor=$(cat stdout) OVS_WAIT_UNTIL([test -s $pidfile]) child=$(cat $pidfile) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. check_ancestors $child $monitor # Kill the daemon process, making it look like a segfault, # and wait for a new child process to get spawned. AT_CHECK([kill -SEGV $child]) OVS_WAIT_WHILE([kill -0 $child]) OVS_WAIT_UNTIL([test -s $pidfile && test $(cat $pidfile) != $child]) child2=$(cat $pidfile) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. check_ancestors $child2 $monitor # Kill the daemon process with SIGTERM, and wait for the daemon # and the monitor processes to go away and the pidfile to get deleted. AT_CHECK([kill $child2]) OVS_WAIT_WHILE([kill -0 $monitor || kill -0 $child2 || test -e $pidfile]) AT_CLEANUP AT_SETUP([daemon --monitor restart exit code - Python3]) # Skip this test for Windows, echo $! gives shell pid instead of parent process AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill $(cat *.pid)' pidfile=test-daemon.py.pid # Start the daemon and wait for the pidfile to get created. AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --monitor & echo $!], [0], [stdout]) monitor=$(cat stdout) OVS_WAIT_UNTIL([test -s $pidfile]) child=$(cat $pidfile) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. check_ancestors $child $monitor # HUP the daemon process causing it to throw an exception, # and wait for a new child process to get spawned. AT_CHECK([kill -HUP $child]) OVS_WAIT_WHILE([kill -0 $child]) OVS_WAIT_UNTIL([test -s $pidfile && test $child != $(cat $pidfile)]) child2=$(cat $pidfile) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. check_ancestors $child2 $monitor # Kill the daemon process with SIGTERM, and wait for the daemon # and the monitor processes to go away and the pidfile to get deleted. AT_CHECK([kill $child2]) OVS_WAIT_WHILE([kill -0 $monitor || kill -0 $child2 || test -e $pidfile]) AT_CLEANUP AT_SETUP([daemon --detach - Python3]) # Skip this test for Windows, the pid file not removed if the daemon is killed AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill $(cat *.pid)' pidfile=test-daemon.py.pid # Start the daemon and make sure that the pidfile exists immediately. # We don't wait for the pidfile to get created because the daemon is # supposed to do so before the parent exits. AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir], [0]) AT_CHECK([test -s $pidfile]) pid=$(cat $pidfile) check_ancestors $pid 1 # Kill the daemon and make sure that the pidfile gets deleted. AT_CHECK([kill $pid]) OVS_WAIT_WHILE([kill -0 $pid]) AT_CHECK([test ! -e $pidfile]) AT_CLEANUP AT_SETUP([daemon --detach --monitor - Python3]) # Skip this test for Windows, uses Linux specific kill signal AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill $(cat *.pid)' pidfile=test-daemon.py.pid # Start the daemon and make sure that the pidfile exists immediately. # We don't wait for the pidfile to get created because the daemon is # supposed to do so before the parent exits. AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir --monitor], [0]) AT_CHECK([test -s $pidfile]) child=$(cat $pidfile) AT_CHECK([parent_pid $child], [0], [stdout]) monitor=$(cat stdout) # Check that the pidfile names a running process, # and that the parent process of that process is a running process, # and that the parent process of that process is init. check_ancestors $child $monitor 1 # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned. AT_CHECK([kill -SEGV $child]) OVS_WAIT_WHILE([kill -0 $child]) OVS_WAIT_UNTIL([test -s $pidfile && test $(cat $pidfile) != $child]) child2=$(cat $pidfile) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. check_ancestors $child2 $monitor 1 # Kill the daemon process with SIGTERM, and wait for the daemon # and the monitor processes to go away and the pidfile to get deleted. AT_CHECK([kill $child2]) OVS_WAIT_WHILE([kill -0 $child2 || kill -0 $monitor || test -e $pidfile]) AT_CLEANUP AT_SETUP([daemon --detach startup errors - Python3]) AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir --bail], [1], [], [stderr]) AT_CHECK([grep 'test-daemon.py: exiting after daemonize_start() as requested' stderr], [0], [ignore]) AT_CHECK([test ! -s test-daemon.py.pid]) AT_CLEANUP AT_SETUP([daemon --detach --monitor startup errors - Python3]) AT_CAPTURE_FILE([pid]) AT_CHECK([$PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir --monitor --bail], [1], [], [stderr]) AT_CHECK([grep 'test-daemon.py: exiting after daemonize_start() as requested' stderr], [0], [ignore]) AT_CHECK([test ! -s test-daemon.py.pid]) AT_CLEANUP AT_SETUP([daemon --detach closes standard fds - Python3]) # Skip this test for Windows, uses Linux specific kill signal AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_CHECK([(yes 2>stderr; echo $? > status) | $PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir]) AT_CHECK([kill $(cat test-daemon.py.pid)]) AT_CHECK([test -s status]) if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then # Something in the environment caused SIGPIPE to be ignored, but # 'yes' at least told us that it got EPIPE. Good enough; we know # that stdout was closed. : else # Otherwise make sure that 'yes' died from SIGPIPE. AT_CHECK([kill -l `cat status`], [0], [PIPE ]) fi AT_CLEANUP AT_SETUP([daemon --detach --monitor closes standard fds - Python3]) # Skip this test for Windows, uses Linux specific kill signal AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_CHECK([(yes 2>stderr; echo $? > status) | $PYTHON3 $srcdir/test-daemon.py --pidfile --detach --no-chdir], [0], [], []) AT_CHECK([kill $(cat test-daemon.py.pid)]) AT_CHECK([test -s status]) if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then # Something in the environment caused SIGPIPE to be ignored, but # 'yes' at least told us that it got EPIPE. Good enough; we know # that stdout was closed. : else # Otherwise make sure that 'yes' died from SIGPIPE. AT_CHECK([kill -l `cat status`], [0], [PIPE ]) fi AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/daemon.at000066400000000000000000000233051514270232600217160ustar00rootroot00000000000000AT_BANNER([daemon unit tests - C]) OVS_START_SHELL_HELPERS # check_process_name PID NAME # # On Linux, make sure that the name of process PID is NAME. # (On other systems, don't bother.) if test -e /proc/$$/comm; then check_process_name() { # In case we're building with shared libraries enabled, strip # off libtool's lt- prefix. AT_CHECK_UNQUOTED([sed 's/lt-//' /proc/$1/comm], [0], [$2 ]) } else check_process_name() { : } fi # check_ancestors PID PARENT [GRANDPARENT...] check_ancestors() { echo "checking ancestry: $*" local child=$1; shift AT_CHECK([kill -0 $child]) while test $# != 0; do local parent=$1; shift AT_CHECK([parent_pid $child], [0], [stdout]) actual_parent=$(cat stdout) if test $parent = 1; then # Traditionally, if a parent's process exits, the process's new # parent is pid 1 (init) but this is not always the case these # days. Instead, if the parent process should be pid 1, be # satisfied if the parent process is different from our own pid. if test $actual_parent = $$; then echo "parent of pid $child is this shell ($$) but should not be" AT_FAIL_IF([:]) fi elif test $parent != $actual_parent; then echo "parent of pid $child is $actual_parent but should be $parent" AT_FAIL_IF([:]) fi child=$parent done } OVS_END_SHELL_HELPERS AT_SETUP([daemon]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) dnl OVS_SKIP_NON_ADMIN_WIN() dnl dnl Checks if we have enough rights to create a service m4_define([OVS_SKIP_NON_ADMIN_WIN], [ AT_SKIP_IF([net session; test $? -ne 0]) ]) # Start the daemon and wait for the pidfile to get created # and that its contents are the correct pid. on_exit 'kill $(cat *.pid)' AT_CHECK([ovsdb-server --pidfile --no-db 2>/dev/null & echo $!], [0], [stdout]) expected_pid=$(cat stdout) OVS_WAIT_UNTIL([test -s ovsdb-server.pid]) pid=$(cat ovsdb-server.pid) AT_CHECK([test $pid = $expected_pid]) AT_CHECK([kill -0 $pid]) # Kill the daemon and make sure that the pidfile gets deleted. AT_CHECK([kill $pid]) OVS_WAIT_WHILE([kill -0 $pid]) AT_CHECK([test ! -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --monitor]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS # Start the daemon and wait for the pidfile to get created. on_exit 'kill $(cat *.pid)' AT_CHECK([ovsdb-server --monitor --pidfile --no-db 2>/dev/null & echo $!], [0], [stdout]) parent=$(cat stdout) OVS_WAIT_UNTIL([test -s ovsdb-server.pid]) # Check that the pidfile names a running process, # and that the parent process of that process is our child process, # and that (with a Linux kernel) the child's process name is correct. child=$(cat ovsdb-server.pid) check_ancestors $child $parent check_process_name $child ovsdb-server # Avoid a race between pidfile creation and notifying the parent, # which can easily trigger if ovsdb-server is slow (e.g. due to valgrind). OVS_WAIT_UNTIL([ovs-appctl -t ovsdb-server version]) # Kill the daemon process, making it look like a segfault, # and wait for a new child process to get spawned. AT_CHECK([kill -SEGV $child], [0], [], [ignore]) OVS_WAIT_WHILE([kill -0 $child]) OVS_WAIT_UNTIL([test -s ovsdb-server.pid && test $(cat ovsdb-server.pid) != $child]) # Check that the pidfile names a running process, # and that the parent process of that process is our child process. child2=$(cat ovsdb-server.pid) check_ancestors $child2 $parent check_process_name $child2 ovsdb-server # Kill the daemon process with SIGTERM, and wait for the daemon # and the monitor processes to go away and the pidfile to get deleted. AT_CHECK([kill $child2]) OVS_WAIT_WHILE([kill -0 $parent || kill -0 $child2 || test -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --detach]) # Start the daemon and make sure that the pidfile exists immediately. # We don't wait for the pidfile to get created because the daemon is # supposed to do so before the parent exits. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --no-db], [0]) AT_CHECK([test -s ovsdb-server.pid]) child=$(cat ovsdb-server.pid) AT_CHECK([kill -0 $child]) # Kill the daemon and make sure that the pidfile gets deleted. if test "$IS_WIN32" = "yes"; then # When a 'kill pid' is done on windows (through 'taskkill //F'), # pidfiles are not deleted (because it is force kill), so use # 'ovs-appctl exit' instead OVS_APP_EXIT_AND_WAIT([ovsdb-server]) else kill $child fi OVS_WAIT_WHILE([kill -0 $child]) AT_CHECK([test ! -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --detach --monitor]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS on_exit 'kill $(cat *.pid)' # Start the daemon and make sure that the pidfile exists immediately. # We don't wait for the pidfile to get created because the daemon is # supposed to do so before the parent exits. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --monitor --no-db]) AT_CHECK([test -s ovsdb-server.pid]) child=$(cat ovsdb-server.pid) # Check process naming and ancestry. monitor=$(parent_pid $child) check_process_name $child ovsdb-server check_ancestors $child $monitor 1 # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned. AT_CHECK([kill -SEGV $child], [0]) OVS_WAIT_WHILE([kill -0 $child]) OVS_WAIT_UNTIL([test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != $child]) child2=$(cat ovsdb-server.pid) # Check process naming and ancestry. check_process_name $child2 ovsdb-server check_ancestors $child2 $monitor 1 # Kill the daemon process with SIGTERM, and wait for the daemon # and the monitor processes to go away and the pidfile to get deleted. AT_CHECK([kill $child2]) OVS_WAIT_WHILE( [kill -0 $monitor || kill -0 $child2 || test -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --detach startup errors]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --unixctl=nonexistent/unixctl --no-db], [1], [], [stderr]) AT_CHECK([grep 'could not initialize control socket' stderr], [0], [ignore]) AT_CHECK([test ! -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --detach --monitor startup errors]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --monitor --unixctl=nonexistent/unixctl --no-db], [1], [], [stderr]) AT_CHECK([grep 'could not initialize control socket' stderr], [0], [ignore]) AT_CHECK([test ! -e ovsdb-server.pid]) AT_CLEANUP AT_SETUP([daemon --service]) AT_KEYWORDS([windows-service]) AT_SKIP_IF([test "$IS_WIN32" != "yes"]) OVS_SKIP_NON_ADMIN_WIN AT_SKIP_IF([sc qc ovsdb-server]) AT_CAPTURE_FILE([pid]) # To create a Windows service, we need the absolute path for the executable. abs_path="$(cd $(dirname `which ovsdb-server`); pwd -W; cd $OLDPWD)" AT_CHECK([sc create ovsdb-server binpath="$abs_path/ovsdb-server --no-db --log-file=`pwd`/ovsdb-server.log --pidfile=`pwd`/ovsdb-server.pid --unixctl=`pwd`/ovsdb-server.ctl --remote=punix:`pwd`/socket --service"], [0], [[[SC]] CreateService SUCCESS ]) AT_CHECK([sc start ovsdb-server], [0], [ignore], [ignore], [sc delete ovsdb-server]) OVS_WAIT_UNTIL([test -s ovsdb-server.pid]) OVS_WAIT_UNTIL([sc query ovsdb-server | grep STATE | grep RUNNING > /dev/null 2>&1]) AT_CHECK([kill -0 `cat ovsdb-server.pid`], [0], [ignore]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs], [0], [_Server ]) AT_CHECK([sc stop ovsdb-server], [0], [ignore]) OVS_WAIT_UNTIL([test ! -s ovsdb-server.pid]) OVS_WAIT_UNTIL([sc query ovsdb-server | grep STATE | grep STOPPED > /dev/null 2>&1]) AT_CHECK([sc delete ovsdb-server], [0], [[[SC]] DeleteService SUCCESS ]) AT_CLEANUP AT_SETUP([backtrace without monitor]) AT_SKIP_IF([test "$HAVE_BACKTRACE" = "no" && test "$HAVE_UNWIND" = "no"]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --no-db \ --log-file --verbose=DBG], [0], [ignore], [ignore]) OVS_WAIT_UNTIL([test -s ovsdb-server.pid]) child=$(cat ovsdb-server.pid) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([kill -SEGV $child]) OVS_WAIT_UNTIL([grep -q "^SIGSEGV detected, backtrace:" ovsdb-server.log]) AT_CLEANUP AT_SETUP([backtrace with monitor]) AT_SKIP_IF([test "$HAVE_BACKTRACE" = "no" && test "$HAVE_UNWIND" = "no"]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS on_exit 'kill $(cat *.pid)' AT_CHECK([ovsdb-server --detach --monitor --no-chdir --pidfile --no-db \ --log-file --verbose=DBG], [0], [ignore], [ignore]) OVS_WAIT_UNTIL([test -s ovsdb-server.pid]) child=$(cat ovsdb-server.pid) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([kill -SEGV $child]) OVS_WAIT_UNTIL([grep -q "backtrace(monitor)|WARN|SIGSEGV detected, backtrace:" ovsdb-server.log]) OVS_WAIT_UNTIL([grep -q "daemon_unix(monitor)|ERR|1 crashes: pid .* died, killed (Segmentation fault)" ovsdb-server.log]) # Wait until a new process is started before exiting, so it will be # stopped correctly. OVS_WAIT_UNTIL([test -s ovsdb-server.pid && test $(cat ovsdb-server.pid) != $child]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/dpctl.at000066400000000000000000000137631514270232600215700ustar00rootroot00000000000000AT_BANNER([dpctl]) AT_SETUP([dpctl - add-dp del-dp]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl dpctl/add-dp dummy@br0]) AT_CHECK([ovs-appctl dpctl/add-dp dummy@br0], [2], [], [ovs-vswitchd: add_dp (File exists) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0]) AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0], [2], [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [dnl ovs-vswitchd: opening datapath (...) ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpctl - add-if set-if del-if]) OVS_VSWITCHD_START([], [], [=override]) AT_CHECK([ovs-appctl dpctl/add-dp dummy@br0]) AT_CHECK([ovs-appctl dpctl/show dummy@br0], [0], [dnl dummy@br0: lookups: hit:0 missed:0 lost:0 flows: 0 port 0: br0 (dummy-internal) ]) AT_CHECK([ovs-appctl dpctl/add-if dummy@br0 vif1.0,type=dummy,port_no=5]) AT_CHECK([ovs-appctl dpctl/show dummy@br0], [0], [dnl dummy@br0: lookups: hit:0 missed:0 lost:0 flows: 0 port 0: br0 (dummy-internal) port 5: vif1.0 (dummy) ]) AT_CHECK([ovs-appctl dpctl/add-if dummy@br0 vif1.0,type=dummy], [2], [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [ovs-vswitchd: adding vif1.0 to dummy@br0 failed (...) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/set-if dummy@br0 vif1.0,port_no=5]) AT_CHECK([ovs-appctl dpctl/set-if dummy@br0 vif1.0,type=system], [2], [], [ovs-vswitchd: vif1.0: can't change type from dummy to system ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/set-if dummy@br0 br0,type=dummy-internal], [0]) AT_CHECK([ovs-appctl dpctl/set-if dummy@br0 br0,type=internal], [2], [], [ovs-vswitchd: br0: can't change type from dummy-internal to internal ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 vif1.0]) AT_CHECK([ovs-appctl dpctl/show dummy@br0], [0], [dnl dummy@br0: lookups: hit:0 missed:0 lost:0 flows: 0 port 0: br0 (dummy-internal) ]) AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 vif1.0], [2], [], [ovs-vswitchd: no port named vif1.0 ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/show dummy@br0], [0], [dnl dummy@br0: lookups: hit:0 missed:0 lost:0 flows: 0 port 0: br0 (dummy-internal) ]) AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 nonexistent], [2], [], [ovs-vswitchd: no port named nonexistent ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 br0], [2], [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [ovs-vswitchd: deleting port br0 from dummy@br0 failed (...) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0]) AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 br0], [2], [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [ovs-vswitchd: opening datapath (...) ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP(["/dummy@br0: port_del failed/d /dummy@br0: failed to add vif1.0 as port/d /Dropped 1 log messages in last/d"]) AT_CLEANUP AT_SETUP([dpctl - add/mod/del-flows]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl dpctl/add-dp dummy@br0]) AT_DATA([flows.txt], [dnl in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234) 2 ]) AT_CHECK([ovs-appctl dpctl/add-flows dummy@br0 flows.txt], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 ]) AT_DATA([flows.txt], [dnl in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234) 3 ]) AT_CHECK([ovs-appctl dpctl/mod-flows dummy@br0 flows.txt], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:3 ]) AT_DATA([flows.txt], [dnl in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234) ]) AT_CHECK([ovs-appctl dpctl/del-flows dummy@br0 flows.txt], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl ]) AT_DATA([flows.txt], [dnl add in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234) 2 add in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:03),eth_type(0x1234) 2 add in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:04),eth_type(0x1234) 2 modify in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234) 1 delete in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:03),eth_type(0x1234) ]) AT_CHECK([ovs-appctl dpctl/add-flows dummy@br0 flows.txt], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:1 recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:04),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 ]) AT_CHECK([ovs-appctl dpctl/del-flows dummy@br0], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@br0 | sort], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/del-dp dummy@br0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpctl - ct-set-limits ct-get-limits ct-del-limits]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [default limit=0 ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=], [0], [default limit=0 ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=,], [0], [default limit=0 ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=x], [2], [], [ovs-vswitchd: invalid zone (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=]) AT_CHECK([ovs-appctl dpctl/ct-set-limits zone=0,limit=0]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=0], [0], [default limit=0 zone=0,limit=0,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=0]) OVS_VSWITCHD_STOP AT_CLEANUPopenvswitch-3.7.0~git20260211.8c6ebf8/tests/dpif-netdev.at000066400000000000000000005125611514270232600226670ustar00rootroot00000000000000AT_BANNER([dpif-netdev]) m4_divert_push([PREPARE_TESTS]) [ # Strips out uninteresting parts of flow output, as well as parts # that vary from one run to another (e.g., timing and bond actions). strip_timers () { sed ' s/duration:[0-9\.][0-9\.]*/duration:0.0/ s/used:[0-9\.][0-9\.]*/used:0.0/ ' } strip_xout () { sed ' s/mega_ufid:[-0-9a-f]* // s/ufid:[-0-9a-f]* // s/used:[0-9\.][0-9\.]*/used:0.0/ s/actions:.*/actions: / s/packets:[0-9]*/packets:0/ s/bytes:[0-9]*/bytes:0/ ' | sort } strip_xout_keep_actions () { sed ' s/mega_ufid:[-0-9a-f]* // s/ufid:[-0-9a-f]* // s/used:[0-9\.][0-9\.]*/used:0.0/ s/packets:[0-9]*/packets:0/ s/bytes:[0-9]*/bytes:0/ ' | sort } filter_flow_install () { grep 'flow_add' | sed 's/.*flow_add: //' | sort | uniq } filter_hw_flow_install () { grep 'dpif_offload_dummy.*flow put\[create\]' | sed 's/.*|DBG|//' | sort \ | uniq } filter_hw_flow_del () { grep 'dpif_offload_dummy.*flow del' | sed 's/.*|DBG|//' | sort | uniq } filter_hw_packet_netdev_dummy () { grep 'dpif_offload_dummy.*: packet:.*with mark' | sed 's/.*|DBG|//' \ | sort | uniq } filter_flow_dump () { grep 'flow_dump ' | sed ' s/.*flow_dump // s/used:[0-9\.][0-9\.]*/used:0.0/ ' | sort | uniq } strip_metadata () { sed 's/metadata=0x[0-9a-f]*/metadata=0x0/' } ] m4_divert_pop([PREPARE_TESTS]) AT_SETUP([dpif-netdev - netdev-dummy/receive]) # Create br0 with interfaces p0 OVS_VSWITCHD_START([add-port br0 p1 -- set interface p1 type=dummy ofport_request=1 -- ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) ovs-appctl time/stop ovs-appctl time/warp 5000 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:02:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(ack)']) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:01,dst=50:54:00:00:02:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(ack) ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:06:00),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.6,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(ack)' --len 1024]) OVS_WAIT_UNTIL([test `grep -c "miss upcall" ovs-vswitchd.log` -ge 2]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:06:00),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.6,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(ack) ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([DPIF_NETDEV_DUMMY_IFACE], [AT_SETUP([dpif-netdev - $1 interface]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [add-port br0 p1 -- set interface p1 type=$1 options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=$1 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p2 -- set interface p2 type=$1 options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=$1 --], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,8,8,8,8"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) ovs-appctl time/stop ovs-appctl time/warp 5000 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 100 sleep 1 # wait for forwarders process packets AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(8),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_DUMMY_IFACE([dummy]) DPIF_NETDEV_DUMMY_IFACE([dummy-pmd]) m4_define([DPIF_NETDEV_MISS_FLOW_INSTALL], [AT_SETUP([dpif-netdev - miss upcall key matches flow_install - $1]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=$1 options:pstream=punix:$OVS_RUNDIR/p0.sock \ -- set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl ofproto/trace 'in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) ]) AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: ]) # Now, the same again without megaflows. AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `grep -c "miss upcall" ovs-vswitchd.log` -ge 2]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) ]) AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_MISS_FLOW_INSTALL([dummy]) DPIF_NETDEV_MISS_FLOW_INSTALL([dummy-pmd]) m4_define([DPIF_NETDEV_FLOW_PUT_MODIFY], [AT_SETUP([dpif-netdev - datapath flow modification - $1]) OVS_VSWITCHD_START( [add-port br0 p1 -- set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock -- \ add-port br0 p2 -- set interface p2 type=$1 ofport_request=2 options:pstream=punix:$OVS_RUNDIR/p2.sock -- \ set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg]) # Add a flow that directs some packets received on p1 to p2 and the # rest back out p1. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 priority=1,ip,in_port=1,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,actions=output:2]) AT_CHECK([ovs-ofctl add-flow br0 priority=0,in_port=1,actions=IN_PORT]) # Inject a packet of the form that should go to p2. packet="in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5),encap(eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=0,tos=0,ttl=64,frag=no))" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5),encap(eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=0,tos=0,ttl=64,frag=no)) ]) ovs-appctl revalidator/wait # Dump the datapath flow to see that it goes to p2 ("actions:2"). AT_CHECK([ovs-appctl dpif/dump-flows br0], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5/0x0),encap(eth_type(0x0800),ipv4(frag=no)), packets:0, bytes:0, used:never, actions:2 ]) # Delete the flows, then add new flows that would not match the same # packet as before. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 priority=1,in_port=1,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,dl_type=0x0801,actions=output:2]) AT_CHECK([ovs-ofctl add-flow br0 priority=0,in_port=1,actions=IN_PORT]) # Wait for flow revalidation ovs-appctl revalidator/wait # Inject the same packet again. AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64]) ovs-appctl revalidator/wait # Dump the datapath flow to see that it goes to p1 ("actions:IN_PORT"). AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5/0x0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:64, used:0.0s, actions:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_FLOW_PUT_MODIFY([dummy]) DPIF_NETDEV_FLOW_PUT_MODIFY([dummy-pmd]) m4_define([DPIF_NETDEV_MISS_FLOW_DUMP], [AT_SETUP([dpif-netdev - miss upcall key matches flow_dump - $1]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=$1 options:pstream=punix:$OVS_RUNDIR/p0.sock \ -- set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl upcall/disable-ufid], [0], [Datapath dumping tersely using UFID disabled ], []) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl vlog/disable-rate-limit dpif]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) ]) ovs-appctl revalidator/wait AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: ]) # Now, the same again without megaflows. AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled ]) AT_CHECK([ovs-appctl upcall/disable-ufid], [0], [Datapath dumping tersely using UFID disabled ], []) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `grep -c "miss upcall" ovs-vswitchd.log` -ge 2]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) ]) ovs-appctl revalidator/wait AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_MISS_FLOW_DUMP([dummy]) DPIF_NETDEV_MISS_FLOW_DUMP([dummy-pmd]) AT_SETUP([dpif-netdev - meters]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [add-port br0 p1 -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p2 -- set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps burst stats bands=type=drop rate=1 burst_size=1']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=2 kbps burst stats bands=type=drop rate=1 burst_size=2']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 'in_port=1 action=meter:1,7']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 'in_port=7 action=meter:2,1']) AT_CHECK([ovs-ofctl add-flow br1 'in_port=2 action=8']) AT_CHECK([ovs-ofctl add-flow br1 'in_port=8 action=2']) ovs-appctl time/stop AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0], [0], [dnl OFPST_METER_CONFIG reply (OF1.3) (xid=0x2): meter=1 pktps burst stats bands= type=drop rate=1 burst_size=1 meter=2 kbps burst stats bands= type=drop rate=1 burst_size=2 ]) ovs-appctl time/warp 5000 for i in `seq 1 7`; do AT_CHECK( [ovs-appctl netdev-dummy/receive p7 \ 'in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60]) done for i in `seq 1 5`; do AT_CHECK( [ovs-appctl netdev-dummy/receive p8 \ 'in_port(8),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60]) done sleep 1 # wait for forwarders process packets # Meter 1 is measuring packets, allowing one packet per second with # bursts of one packet, so 4 out of 5 packets should hit the drop band. # Meter 2 is measuring kbps, with burst size 2 (== 2000 bits). 4 packets # (240 bytes == 1920 bits) pass, but the last three packets should hit the # drop band. There should be 80 bits remaining for the next packets. AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:5 byte_in_count:300 duration:0.0s bands: 0: packet_count:4 byte_count:240 meter:2 flow_count:1 packet_in_count:7 byte_in_count:420 duration:0.0s bands: 0: packet_count:3 byte_count:180 ]) # Advance time by 870 ms ovs-appctl time/warp 870 for i in `seq 1 5`; do AT_CHECK( [ovs-appctl netdev-dummy/receive p7 \ 'in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60]) AT_CHECK( [ovs-appctl netdev-dummy/receive p8 \ 'in_port(8),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60]) done sleep 1 # wait for forwarders process packets # Meter 1 is measuring packets, allowing one packet per second with # bursts of one packet, so all 5 of the new packets should hit the drop # band. # Meter 2 is measuring kbps, with burst size 2 (== 2000 bits). After 870ms # there should be space for 80 + 870 = 950 bits, so one new 60 byte (480 bit) # packet should pass, remaining 4 should hit the drop band. There should be # 470 bits left. AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands: 0: packet_count:9 byte_count:540 meter:2 flow_count:1 packet_in_count:12 byte_in_count:720 duration:0.0s bands: 0: packet_count:7 byte_count:420 ]) # Advance time by 10 ms ovs-appctl time/warp 10 for i in `seq 1 5`; do AT_CHECK( [ovs-appctl netdev-dummy/receive p7 \ 'in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60]) done sleep 1 # wait for forwarders process packets # Meter 1 should remain the same as we didn't send anything that should hit it. # Meter 2 is measuring kbps, with burst size 2 (== 2000 bits). After 10ms # there should be space for 470 + 10 = 480 bits, so one new 60 byte (480 bit) # packet should pass, remaining 4 should hit the drop band. AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands: 0: packet_count:9 byte_count:540 meter:2 flow_count:1 packet_in_count:17 byte_in_count:1020 duration:0.0s bands: 0: packet_count:11 byte_count:660 ]) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter datapath_drop_meter ], [0], [dnl 20 ]) AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7 recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8 recirc_id(0),in_port(7),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(1),1 recirc_id(0),in_port(8),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 del-meters br0]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD], [AT_SETUP([dpif-netdev - partial hw offload - $1]) OVS_VSWITCHD_START( [add-port br0 p1 -- \ set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock options:ifindex=1100 -- \ set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg dpif_offload_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true]) OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=IN_PORT]) packet="packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=0,tos=0,ttl=64,frag=no)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=0,tos=0,ttl=64,frag=no) ]) # Check that flow successfully offloaded. OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow put[[create]]: flow match: recirc_id=0,eth,ip,in_port=1,vlan_tci=0x0000/0x1fff,nw_frag=no, mark: 1 ]) # Check that datapath flow installed successfully. AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions: ]) # Inject the same packet again. AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) # Check for succesfull packet matching with installed offloaded flow. AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0], [dnl p1: packet: ip,vlan_tci=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no matches with flow: recirc_id=0,eth,ip,vlan_tci=0x0000/0x1fff,nw_frag=no with mark: 1 ]) ovs-appctl revalidator/wait # Dump the datapath flow to see that actions was executed for a packet. AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:64, used:0.0s, actions:1 ]) # Wait for datapath flow expiration. ovs-appctl time/stop ovs-appctl time/warp 15000 ovs-appctl revalidator/wait # Check that flow successfully deleted from HW. OVS_WAIT_UNTIL([grep "succeed to delete netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_del < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow del: mark: 1 ]) # Check if partial hw offload was hit in dpif-netdev. m4_if([$1], [dummy-pmd], [AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \ | grep -q "PHWOL hits: 1"])]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy]) DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy-pmd]) m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS], [AT_SETUP([dpif-netdev - partial hw offload with packet modifications - $1]) OVS_VSWITCHD_START( [add-port br0 p1 -- \ set interface p1 type=$1 ofport_request=1 options:pcap=p1.pcap options:ifindex=1101 -- \ set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg dpif_offload_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true]) OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl del-flows br0]) # Setting flow to modify ipv4 src address and udp dst port to be sure that # offloaded packets has correctly initialized l3/l4 offsets. AT_CHECK([ovs-ofctl add-flow br0 in_port=1,udp,actions=mod_nw_src:192.168.0.7,mod_tp_dst:3773,output:IN_PORT]) packet="packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=17,ttl=64,frag=no),udp(src=81,dst=82))" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),dnl packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=127.0.0.1,dst=127.0.0.1,proto=17,tos=0,ttl=64,frag=no),udp(src=81,dst=82)) ]) # Check that flow successfully offloaded. OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow put[[create]]: flow match: recirc_id=0,eth,udp,in_port=1,dl_vlan=99,nw_src=127.0.0.1,nw_frag=no,tp_dst=82, mark: 1 ]) # Check that datapath flow installed successfully. AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), actions: ]) # Inject the same packet again. AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) # Check for succesfull packet matching with installed offloaded flow. AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0], [dnl p1: packet: udp,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=82 dnl matches with flow: recirc_id=0,eth,udp,dl_vlan=99,nw_src=127.0.0.1,nw_frag=no,tp_dst=82 with mark: 1 ]) ovs-appctl revalidator/wait # Dump the datapath flow to see that actions was executed for a packet. AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), dnl packets:1, bytes:64, used:0.0s, actions:set(ipv4(src=192.168.0.7)),set(udp(dst=3773)),1 ]) # Wait for datapath flow expiration. ovs-appctl time/stop ovs-appctl time/warp 15000 ovs-appctl revalidator/wait # Check that flow successfully deleted from HW. OVS_WAIT_UNTIL([grep "succeed to delete netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_del < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow del: mark: 1 ]) # Check that ip address and udp port were correctly modified in output packets. AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=82 udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=192.168.0.7,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=3773 udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=82 udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=192.168.0.7,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=3773 ]) # Check if partial hw offload was hit in dpif-netdev. m4_if([$1], [dummy-pmd], [AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \ | grep -q "PHWOL hits: 1"])]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy]) DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy-pmd]) m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP], [AT_SETUP([dpif-netdev - partial hw offload with arp vlan id packet modifications - $1]) OVS_VSWITCHD_START( [add-port br0 p1 -- \ set interface p1 type=$1 ofport_request=1 options:pcap=p1.pcap options:ifindex=1102 -- \ set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg dpif_offload_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true]) OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl del-flows br0]) # Setting flow to modify vlan id with arp packet to be sure that # offloaded packets has correctly initialized l3 offset. AT_CHECK([ovs-ofctl add-flow br0 in_port=1,arp,dl_vlan=99,actions=mod_vlan_vid=11,output:IN_PORT]) packet="packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806),arp(sip=127.0.0.1,tip=127.0.0.1,op=1,sha=00:0b:0c:0d:0e:0f,tha=00:00:00:00:00:00))" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) OVS_WAIT_UNTIL([grep "miss upcall" ovs-vswitchd.log]) AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),dnl packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806),arp(sip=127.0.0.1,tip=127.0.0.1,op=1,sha=00:0b:0c:0d:0e:0f,tha=00:00:00:00:00:00)) ]) # Check that flow successfully offloaded. OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow put[[create]]: flow match: recirc_id=0,eth,arp,in_port=1,dl_vlan=99,dl_vlan_pcp=7, mark: 1 ]) # Check that datapath flow installed successfully. AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806)), actions: ]) # Inject the same packet again. AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0]) # Check for succesfull packet matching with installed offloaded flow. AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0], [dnl p1: packet: arp,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00 dnl matches with flow: recirc_id=0,eth,arp,dl_vlan=99,dl_vlan_pcp=7 with mark: 1 ]) ovs-appctl revalidator/wait # Dump the datapath flow to see that actions was executed for a packet. AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806)), dnl packets:1, bytes:64, used:0.0s, actions:pop_vlan,push_vlan(vid=11,pcp=7),1 ]) # Wait for datapath flow expiration. ovs-appctl time/stop ovs-appctl time/warp 15000 ovs-appctl revalidator/wait # Check that flow successfully deleted from HW. OVS_WAIT_UNTIL([grep "succeed to delete netdev flow" ovs-vswitchd.log]) AT_CHECK([filter_hw_flow_del < ovs-vswitchd.log | strip_xout], [0], [dnl p1: flow del: mark: 1 ]) # Check that VLAN ID was correctly modified in output packets. AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,dl_vlan=11,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,dl_vlan=11,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00 ]) # Check if partial hw offload was hit in dpif-netdev. m4_if([$1], [dummy-pmd], [AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \ | grep -q "PHWOL hits: 1"])]) OVS_VSWITCHD_STOP AT_CLEANUP]) DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy]) DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy-pmd]) AT_SETUP([dpif-netdev - check dpctl/add-flow in_port exact match]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock \ -- set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure]) AT_CHECK([ovs-appctl dpctl/add-flow "eth(),eth_type(0x0800),ipv4()" "3"], [2], [], [dnl ovs-vswitchd: updating flow table (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_WAIT_UNTIL([grep "flow: in_port is not an exact match" ovs-vswitchd.log]) OVS_VSWITCHD_STOP(["/flow: in_port is not an exact match/d /failed to put/d"]) AT_CLEANUP AT_SETUP([dpif-netdev - check dpctl/add-flow wider ip match]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock \ -- set bridge br0 datapath-type=dummy]) AT_CHECK([ovs-appctl revalidator/pause]) AT_CHECK([ovs-appctl dpctl/add-flow "in_port(1),eth_type(0x0800),ipv4(src=0.0.0.0/192.0.0.0,dst=0.0.0.0/192.0.0.0,frag=no)" "3"]) AT_CHECK([ovs-appctl dpctl/add-flow "in_port(1),eth_type(0x0800),ipv4(src=192.1.1.1/0.0.0.0,dst=49.1.1.1/0.0.0.0,frag=no)" "3"]) AT_CHECK([ovs-appctl revalidator/resume]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - check tx packet checksum offloading]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock \ -- set bridge br0 datapath-type=dummy \ other-config:datapath-id=1234 fail-mode=secure]) AT_CHECK([ovs-vsctl get interface p1 status | sed -n 's/^{\(.*\).*}$/\1/p'], [0], [dnl tx_geneve_tso_offload="false", tx_gre_tso_offload="false", tx_ip_csum_offload="false", tx_out_ip_csum_offload="false", tx_out_udp_csum_offload="false", tx_sctp_csum_offload="false", tx_tcp_csum_offload="false", tx_tcp_seg_offload="false", tx_udp_csum_offload="false", tx_vxlan_tso_offload="false" ], []) AT_CHECK([ovs-vsctl get interface br0 status | sed -n 's/^{\(.*\).*}$/\1/p'], [0], [dnl tx_geneve_tso_offload="false", tx_gre_tso_offload="false", tx_ip_csum_offload="false", tx_out_ip_csum_offload="false", tx_out_udp_csum_offload="false", tx_sctp_csum_offload="false", tx_tcp_csum_offload="false", tx_tcp_seg_offload="false", tx_udp_csum_offload="false", tx_vxlan_tso_offload="false" ], []) OVS_VSWITCHD_STOP AT_CLEANUP # SEND_UDP_PKTS([p_name], [p_ofport]) # # Sends 128 packets to port 'p_name' with different UDP destination ports. m4_define([SEND_UDP_PKTS], [ for i in `seq 1 128`; do pkt="in_port($2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.1.1,dst=10.0.0.1,proto=17),udp(src=1000,dst=$i)" ovs-appctl netdev-dummy/receive $1 $pkt --len 256 done ] ) AT_SETUP([dpif-netdev - tx packet steering]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1], [], [], [--dummy-numa 0]) dnl 'thread' mode, packets are expected to be transmitted on a single dnl queue since there is only one PMD thread. AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 options:n_txq=2 other_config:tx-steering=thread]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=output:2]) AT_CHECK([SEND_UDP_PKTS([p1], [1])]) OVS_WAIT_UNTIL([test `ovs-vsctl get Interface p2 statistics:tx_packets` -eq 128]) AT_CHECK([ovs-vsctl get Interface p2 statistics], [], [stdout]) AT_CHECK([test `ovs-vsctl get Interface p2 statistics:tx_q0_packets` -eq 0 -a dnl `ovs-vsctl get Interface p2 statistics:tx_q1_packets` -eq 128 || dnl test `ovs-vsctl get Interface p2 statistics:tx_q0_packets` -eq 128 -a dnl `ovs-vsctl get Interface p2 statistics:tx_q1_packets` -eq 0]) AT_CHECK([ovs-vsctl del-port p2]) dnl 'hash' mode, packets are expected to be transmitted on both dnl queues, based on their hash value. AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 options:n_txq=2 other_config:tx-steering=hash]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=output:2]) AT_CHECK([SEND_UDP_PKTS([p1], [1])]) OVS_WAIT_UNTIL([test `ovs-vsctl get Interface p2 statistics:tx_packets` -eq 128]) AT_CHECK([ovs-vsctl get Interface p2 statistics], [], [stdout]) AT_CHECK([test `ovs-vsctl get Interface p2 statistics:tx_q0_packets` -gt 0 -a dnl `ovs-vsctl get Interface p2 statistics:tx_q1_packets` -gt 0]) AT_CHECK([ovs-vsctl del-port p2]) dnl 'hash' mode with hw-offload enabled, packets are expected to be transmitted on both dnl queues, based on their hash value. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 options:n_txq=2 other_config:tx-steering=hash]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=output:2]) AT_CHECK([SEND_UDP_PKTS([p1], [1])]) OVS_WAIT_UNTIL([test `ovs-vsctl get Interface p2 statistics:tx_packets` -eq 128]) AT_CHECK([ovs-vsctl get Interface p2 statistics], [], [stdout]) AT_CHECK([test `ovs-vsctl get Interface p2 statistics:tx_q0_packets` -gt 0 -a dnl `ovs-vsctl get Interface p2 statistics:tx_q1_packets` -gt 0]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([CHECK_FWD_PACKET], [m4_if([$3], [], [], [AT_CHECK([ovs-vsctl set interface $1 options:$3=true])]) AT_CHECK([ovs-appctl netdev-dummy/receive $1 $(cat $4)]) m4_if([$5], [none], [], [ AT_CHECK([awk 1 $5 > expout]) dnl Also normalizes the line endings. AT_CHECK([ovs-pcap $2.pcap > $2.pcap.txt 2>&1]) AT_CHECK([tail -n 1 $2.pcap.txt], [0], [expout]) ]) m4_if([$3], [], [], [AT_CHECK([ovs-vsctl remove interface $1 options $3])]) ]) dnl CHECK_IP_CHECKSUMS rx_port tx_port good_pkt bad_pkt good_exp bad_exp dnl dnl Test combinations of Rx IP checksum flags for a good or bad packet dnl received on rx_port, and sent over tx_port. m4_define([CHECK_IP_CHECKSUMS], [dnl Checks for good packet. dnl No Rx flag. CHECK_FWD_PACKET($1, $2, , $3, $5) dnl Rx good. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_good, $3, $5) dnl Rx bad. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_bad, $3, $5) dnl Rx partial. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_partial, $3, $5) dnl Checks for bad packet. dnl No Rx flag. CHECK_FWD_PACKET($1, $2, , $4, $6) dnl Rx good. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_good, $4, $5) dnl Rx bad. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_bad, $4, $6) dnl Rx partial. CHECK_FWD_PACKET($1, $2, ol_ip_rx_csum_set_partial, $4, $5) ]) AT_SETUP([dpif-netdev - ip csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Modify the ip_dst addr to force changing the IP csum. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ tcp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\ tcp_src=54392,tcp_dst=5201,tcp_flags=ack" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) flow_expected=$(echo "${flow_s}" | sed 's/192.168.123.1/192.168.1.1/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) AT_CHECK([ovs-ofctl compose-packet --bare --bad-csum "${flow_s}" > bad_frame]) AT_CHECK([ovs-ofctl compose-packet --bare --bad-csum "${flow_expected}" > bad_expected]) dnl Tx offloads disabled. CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offload did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum_disabled=true]) dnl The bad IP checksum is left untouched but the IP address and the TCP. dnl checksum got updated. first_part_bad=$(cat bad_frame | sed -ne "s/^\(.*\)c0a87b02c0a87b01.*$/\1/p") second_part_good=$(cat good_expected | sed -ne "s/^.*\(c0a87b02c0a80101.*$\)/\1/p") echo ${first_part_bad}${second_part_good} > composed_expected CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [composed_expected]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum_disabled]) dnl Test with IP optional fields in a valid packet. Note that neither this dnl packet nor the following one contain a correct checksum. OVS is dnl expected to replace this dummy checksum with a valid one if possible. m4_define([OPT_PKT], m4_join([], dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800) [aaaaaaaaaaaabbbbbbbbbbbb0800], dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0xeeee) [4f000044abab00004001eeee0a0000010a000002], dnl IPv4 Opt: type 7 (Record Route) len 39 + type 0 (EOL). [07270c010203040a000003000000000000000000], [0000000000000000000000000000000000000000], dnl icmp(type=8,code=0), csum 0x3e2f incorrect, should be 0x412f. [08003e2fb6d00000])) dnl IP header indicates optional fields but doesn't contain any. m4_define([MICROGRAM], m4_join([], dnl eth(dst=aa:aa:aa:aa:aa:aa,src=bb:bb:bb:bb:bb:bb,type=0x0800) [aaaaaaaaaaaabbbbbbbbbbbb0800], dnl ipv4(dst=10.0.0.2,src=10.0.0.1,proto=1,len=60,tot_len=68,csum=0xeeee) [4f000044abab00004001eeee0a0000010a000002])) AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_rx_csum_set_good=true]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 OPT_PKT]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 MICROGRAM]) AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1]) dnl Build the expected modified packets. The first packet has a valid IPv4 dnl checksum and modified destination IP address. The second packet isn't dnl expected to change. AT_CHECK([echo "OPT_PKT" | sed -e "s/0a000002/c0a80101/" -e "s/eeee/dd2e/" > expout]) AT_CHECK([echo "MICROGRAM" >> expout]) AT_CHECK([tail -n 2 p2.pcap.txt], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP dnl CHECK_L4_CHECKSUMS rx_port tx_port good_pkt bad_pkt good_exp bad_exp dnl dnl Test combinations of Rx L4 checksum flags for a good or bad packet dnl received on rx_port, and sent over tx_port. m4_define([CHECK_L4_CHECKSUMS], [dnl Checks for good packet. dnl No Rx flag. CHECK_FWD_PACKET($1, $2, , $3, $5) dnl Rx good. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_good, $3, $5) dnl Rx bad. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_bad, $3, $5) dnl Rx partial. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_partial, $3, $5) dnl Checks for bad packet. dnl No Rx flag. CHECK_FWD_PACKET($1, $2, , $4, $6) dnl Rx good. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_good, $4, $5) dnl Rx bad. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_bad, $4, $6) dnl Rx partial. CHECK_FWD_PACKET($1, $2, ol_l4_rx_csum_set_partial, $4, $5) ]) AT_SETUP([dpif-netdev - tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Modify the tcp_dst port to force changing the TCP csum. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp,actions=mod_tp_dst:2222,output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ tcp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\ tcp_src=54392,tcp_dst=5201,tcp_flags=ack" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) flow_expected=$(echo "${flow_s}" | sed 's/5201/2222/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) AT_CHECK([cat good_frame | sed -e "s/6b72/dead/" > bad_frame]) dnl 0x6b72 + (5201-2222) == 0x7715 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/7715/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offload did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) dnl The bad TCP checksum is left untouched. AT_CHECK([cat good_expected | sed -e "s/7715/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - tcp csum offload (simple match)]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ tcp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\ tcp_src=54392,tcp_dst=5201,tcp_flags=ack" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) AT_CHECK([cat good_frame | sed -e "s/6b72/dead/" > bad_frame]) CHECK_FWD_PACKET(p1, p2, , [bad_frame], [bad_frame]) dnl First packet, no simple matching. AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], [0], [dnl simple match hits: 0 ]) dnl No Rx flag. CHECK_FWD_PACKET(p1, p2, , [bad_frame], [bad_frame]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], [0], [dnl simple match hits: 1 ]) dnl Flag as Rx good. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_frame]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], [0], [dnl simple match hits: 2 ]) dnl Flag as Rx bad. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_bad, [bad_frame], [bad_frame]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], [0], [dnl simple match hits: 3 ]) dnl Flag as Rx partial. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_partial, [bad_frame], [good_frame]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | grep 'simple match hits'], [0], [dnl simple match hits: 4 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Modify the udp_dst port to force changing the UDP csum. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp,actions=mod_tp_dst:2222,output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ udp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\ udp_src=54392,udp_dst=5201" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) flow_expected=$(echo "${flow_s}" | sed 's/5201/2222/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) AT_CHECK([cat good_frame | sed -e "s/bb3b/dead/" > bad_frame]) dnl 0xbb3b + (5201-2222) == 0xc6de dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/c6de/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offload did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) dnl The bad UDP checksum is left untouched. AT_CHECK([cat good_expected | sed -e "s/c6de/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Modify the tcp_dst port to force changing the TCP csum. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp6,actions=mod_tp_dst:2222,output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ tcp6,ipv6_src=fe80::2,ipv6_dst=fe80::1,\ tcp_src=54392,tcp_dst=5201,tcp_flags=ack" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) flow_expected=$(echo "${flow_s}" | sed 's/5201/2222/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) AT_CHECK([cat good_frame | sed -e "s/e5c2/dead/" > bad_frame]) dnl 0xe5c2 + (5201-2222) == 0xf165 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/f165/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offload did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) dnl The bad TCP checksum is left untouched. AT_CHECK([cat good_expected | sed -e "s/f165/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Modify the udp_dst port to force changing the UDP csum. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp6,actions=mod_tp_dst:2222,output:p2]) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ udp6,ipv6_src=fe80::2,ipv6_dst=fe80::1,\ udp_src=54392,udp_dst=5201" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) flow_expected=$(echo "${flow_s}" | sed 's/5201/2222/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) AT_CHECK([cat good_frame | sed -e "s/358c/dead/" > bad_frame]) dnl 0x358c + (5201-2222) == 0x412f dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/412f/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offload did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) dnl The bad UDP checksum is left untouched. AT_CHECK([cat good_expected | sed -e "s/412f/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - no offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Simple ARP. AT_DATA([arp_frame], m4_join([], dnl p = Ether(dst='ff:ff:ff:ff:ff:ff') [ffffffffffff04bf1bd82d2d0806], dnl p /= ARP(pdst='192.168.123.1', hwsrc='8a:bf:7e:2f:05:84') [00010800060400018abf7e2f0584c0a80111000000000000c0a87b01] )) AT_DATA([arp_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500004e00004000401133ea010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5003a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(dst='ff:ff:ff:ff:ff:ff') [ffffffffffff04bf1bd82d2d0806], dnl p /= ARP(pdst='192.168.123.1', hwsrc='8a:bf:7e:2f:05:84') [00010800060400018abf7e2f0584c0a80111000000000000c0a87b01] )) dnl ICMPv6. AT_DATA([ndp_frame], m4_join([], dnl p = Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01') [3333ff00000104bf1bd82d2d86dd], dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1') [6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001], dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1') [87006e2600000000fe800000000000000000000000000001], dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84') [01018abf7e2f0584] )) AT_DATA([ndp_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500007a00004000401133be010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b500660000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01') [3333ff00000104bf1bd82d2d86dd], dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1') [6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001], dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1') [87006e2600000000fe800000000000000000000000000001], dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84') [01018abf7e2f0584] )) CHECK_FWD_PACKET(p1, p2, , [arp_frame], [arp_expected]) CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat arp_expected | sed -e 's/e01312b5003a0000/e01312b5003a1354/' > arp_csum_expected]) AT_CHECK([cat ndp_expected | sed -e 's/e01312b500660000/e01312b50066e106/' > ndp_csum_expected]) CHECK_FWD_PACKET(p1, p2, , [arp_frame], [arp_csum_expected]) CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_csum_expected]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - ip csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d47814510000000000000000501000006b720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500009a000040004011339e010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b500860000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.1.1', id=0, ttl=0) [45000068000000000006bd3cc0a87b02c0a80101], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d4781451000000000000000050100000e5720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/433c/423c/" > bad_frame]) AT_CHECK([cat good_expected | sed -e "s/bd3c/bc3c/" > bad_expected]) dnl Tx offloads disabled. CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/339e/0000/" | sed -e "s/bd3c/423c/" > half_bad_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_expected]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum]) AT_CHECK([cat good_expected | sed -e "s/bd3c/423c/" > half_bad_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_expected]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e "s/e01312b500860000/e01312b50086454d/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b500860000/e01312b50086464d/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/339e/0000/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) dnl Special case 2, to check if Tx offloads did happen in the driver dnl with the outer UDP optimisation. dnl A valid inner L4 checksum is required for the optimisation to trigger. AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=true]) AT_CHECK([cat good_csum_expected | sed -e "s/339e/0000/" -e "s/bd3c/423c/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) AT_CHECK([ovs-vsctl remove Interface p1 options ol_l4_rx_csum_set_good]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum_disabled]) dnl Special case 3, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum]) dnl The inner part will be fixed by datapath. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [good_csum_expected]) dnl Special case 4, to check if Tx offloads did happen in the driver dnl with the outer UDP optimisation. dnl A valid inner L4 checksum is required for the optimisation to trigger. AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=true]) AT_CHECK([cat good_csum_expected | sed -e "s/bd3c/423c/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) AT_CHECK([ovs-vsctl remove Interface p1 options ol_l4_rx_csum_set_good]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d47814510000000000000000501000006b720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500009a000040004011339e010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b500860000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=2222, flags='A', window=0) [d47808ae00000000000000005010000077150000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/6b72/dead/" > bad_frame]) dnl 0x6b72 + (5201-2222) == 0x7715 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/7715/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/7715/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e "s/e01312b500860000/e01312b50086bf4d/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b500860000/e01312b500864c12/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/bf4d/ffff/" -e "s/7715/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/7715/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([no_csum_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=5201, chksum=0) [d478145100480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([no_csum_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500008e00004000401133aa010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5007a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=2222, chksum=0) [d47808ae00480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=5201) [d47814510048bb3b], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [4500008e00004000401133aa010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5007a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=2222) [d47808ae0048c6de], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/bb3b/dead/" > bad_frame]) dnl 0xbb3b + (5201-2222) == 0xc6de dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/c6de/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/c6de/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat no_csum_expected | sed -e 's/e01312b5007a0000/e01312b5007a8643/' > out_no_csum_expected]) AT_CHECK([cat good_expected | sed -e 's/e01312b5007a0000/e01312b5007abf64/' > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e 's/e01312b5007a0000/e01312b5007a9bf2/' > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/bf64/ffff/" -e "s/c6de/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/c6de/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - IPv6 tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp6,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000540640fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d4781451000000000000000050100000e5c20000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [450000ae000040004011338a010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5009a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000540640fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= TCP(sport=54392, dport=2222, flags='A', window=0) [d47808ae000000000000000050100000f1650000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/e5c2/dead/" > bad_frame]) dnl 0xe5c2 + (5201-2222) == 0xf165 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/f165/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/f165/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e 's/e01312b5009a0000/e01312b5009a625e/' > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e 's/e01312b5009a0000/e01312b5009a6973/' > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/625e/ffff/" -e "s/f165/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/f165/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv4 vxlan tunnel - IPv6 udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp6,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([no_csum_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=5201, chksum=0) [d478145100480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([no_csum_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [450000a20000400040113396010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5008e0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=2222, chksum=0) [d47808ae00480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=5201) [d47814510048358c], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa6600000800], dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF') [450000a20000400040113396010102580101025c], dnl p /= UDP(sport=57363, chksum=0) [e01312b5008e0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=2222) [d47808ae0048412f], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/358c/dead/" > bad_frame]) dnl 0x358c + (5201-2222) == 0x412f dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/412f/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/412f/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat no_csum_expected | sed -e "s/e01312b5008e0000/e01312b5008e98b0/" > out_no_csum_expected]) AT_CHECK([cat good_expected | sed -e 's/e01312b5008e0000/e01312b5008e5781/' > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e 's/e01312b5008e0000/e01312b5008eae5f/' > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/5781/ffff/" -e "s/412f/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/412f/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - no offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) dnl Simple ARP. AT_DATA([arp_frame], m4_join([], dnl p = Ether(dst='ff:ff:ff:ff:ff:ff') [ffffffffffff04bf1bd82d2d0806], dnl p /= ARP(pdst='192.168.123.1', hwsrc='8a:bf:7e:2f:05:84') [00010800060400018abf7e2f0584c0a80111000000000000c0a87b01] )) AT_DATA([arp_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000003a11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5003a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(dst='ff:ff:ff:ff:ff:ff') [ffffffffffff04bf1bd82d2d0806], dnl p /= ARP(pdst='192.168.123.1', hwsrc='8a:bf:7e:2f:05:84') [00010800060400018abf7e2f0584c0a80111000000000000c0a87b01] )) dnl ICMPv6. AT_DATA([ndp_frame], m4_join([], dnl p = Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01') [3333ff00000104bf1bd82d2d86dd], dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1') [6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001], dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1') [87006e2600000000fe800000000000000000000000000001], dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84') [01018abf7e2f0584] )) AT_DATA([ndp_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000006611402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b500660000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01') [3333ff00000104bf1bd82d2d86dd], dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1') [6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001], dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1') [87006e2600000000fe800000000000000000000000000001], dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84') [01018abf7e2f0584] )) CHECK_FWD_PACKET(p1, p2, , [arp_frame], [arp_expected]) CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat arp_expected | sed -e 's/e01312b5003a0000/e01312b5003a42f0/' > arp_csum_expected]) AT_CHECK([cat ndp_expected | sed -e 's/e01312b500660000/e01312b5006610a3/' > ndp_csum_expected]) CHECK_FWD_PACKET(p1, p2, , [arp_frame], [arp_csum_expected]) CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_csum_expected]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - ip csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d47814510000000000000000501000006b720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000008611402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b500860000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.1.1', id=0, ttl=0) [45000068000000000006bd3cc0a87b02c0a80101], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d4781451000000000000000050100000e5720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/433c/423c/" > bad_frame]) AT_CHECK([cat good_expected | sed -e "s/bd3c/bc3c/" > bad_expected]) dnl Tx offloads disabled. CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/339e/0000/" | sed -e "s/bd3c/423c/" > half_bad_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_expected]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum]) AT_CHECK([cat good_expected | sed -e "s/bd3c/423c/" > half_bad_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_expected]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e "s/e01312b500860000/e01312b5008674e9/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b500860000/e01312b5008675e9/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum=true]) CHECK_IP_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_ip_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/339e/0000/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) dnl Special case 2, to check if Tx offloads did happen in the driver dnl with the outer UDP optimisation. dnl A valid inner L4 checksum is required for the optimisation to trigger. AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=true]) AT_CHECK([cat good_csum_expected | sed -e "s/339e/0000/" -e "s/bd3c/423c/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) AT_CHECK([ovs-vsctl remove Interface p1 options ol_l4_rx_csum_set_good]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum_disabled]) dnl Special case 3, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_ip_tx_csum]) dnl The inner part will be fixed by datapath. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [good_csum_expected]) dnl Special case 4, to check if Tx offloads did happen in the driver dnl with the outer UDP optimisation. dnl A valid inner L4 checksum is required for the optimisation to trigger. AT_CHECK([ovs-vsctl set Interface p1 options:ol_l4_rx_csum_set_good=true]) AT_CHECK([cat good_csum_expected | sed -e "s/bd3c/423c/" > half_bad_csum_expected]) CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [half_bad_csum_expected]) AT_CHECK([ovs-vsctl remove Interface p1 options ol_l4_rx_csum_set_good]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_ip_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d47814510000000000000000501000006b720000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000008611402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b500860000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [45000068000000000006433cc0a87b02c0a87b01], dnl p /= TCP(sport=54392, dport=2222, flags='A', window=0) [d47808ae00000000000000005010000077150000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/6b72/dead/" > bad_frame]) dnl 0x6b72 + (5201-2222) == 0x7715 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/7715/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/7715/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e "s/e01312b500860000/e01312b50086eee9/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b500860000/e01312b500867bae/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/eee9/ffff/" -e "s/7715/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/7715/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([no_csum_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=5201, chksum=0) [d478145100480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([no_csum_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000007a11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5007a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=2222, chksum=0) [d47808ae00480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=5201) [d47814510048bb3b], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000007a11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5007a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f05840800], dnl p /= IP(src='192.168.123.2', dst='192.168.123.1', id=0, ttl=0) [4500005c000000000011433dc0a87b02c0a87b01], dnl p /= UDP(sport=54392, dport=2222) [d47808ae0048c6de], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/bb3b/dead/" > bad_frame]) dnl 0xbb3b + (5201-2222) == 0xc6de dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/c6de/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/c6de/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat no_csum_expected | sed -e "s/e01312b5007a0000/e01312b5007ab5df/" > out_no_csum_expected]) AT_CHECK([cat good_expected | sed -e "s/e01312b5007a0000/e01312b5007aef00/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b5007a0000/e01312b5007acb8e/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/ef00/ffff/" -e "s/c6de/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/c6de/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - IPv6 tcp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,tcp6,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000540640fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= TCP(sport=54392, dport=5201, flags='A', window=0) [d4781451000000000000000050100000e5c20000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000009a11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5009a0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000540640fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= TCP(sport=54392, dport=2222, flags='A', window=0) [d47808ae000000000000000050100000f1650000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/e5c2/dead/" > bad_frame]) dnl 0xe5c2 + (5201-2222) == 0xf165 dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/f165/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/f165/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat good_expected | sed -e "s/e01312b5009a0000/e01312b5009a91fa/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b5009a0000/e01312b5009a990f/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/91fa/ffff/" -e "s/f165/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/f165/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - IPv6 vxlan tunnel - IPv6 udp csum offload]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-br br2 -- set bridge br2 datapath-type=dummy -- \ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-port br2 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-vsctl add-port br1 t1 \ -- set Interface t1 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:csum=false ofport_request=11], [0]) AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,udp6,tp_dst=5201,actions=mod_tp_dst:2222,output:11]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br2 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br2 2001:cafe::92 aa:66:aa:66:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_DATA([no_csum_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=5201, chksum=0) [d478145100480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([no_csum_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000008e11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5008e0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=2222, chksum=0) [d47808ae00480000], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_frame], m4_join([], dnl p = Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=5201) [d47814510048358c], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_DATA([good_expected], m4_join([], dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01') [aa66aa660001aa66aa66000086dd], dnl p /= IPv6(src='2001:cafe::88', dst='2001:cafe::92') [60000000008e11402001cafe0000000000000000000000882001cafe000000000000000000000092], dnl p /= UDP(sport=57363, chksum=0) [e01312b5008e0000], dnl p /= VXLAN(vni=123, flags='Instance') [0800000000007b00], dnl p /= Ether(src='8a:bf:7e:2f:05:84', dst='0a:8f:39:4f:e0:73') [0a8f394fe0738abf7e2f058486dd], dnl p /= IPv6(src='fe80::2', dst='fe80::1') [6000000000481140fe800000000000000000000000000002fe800000000000000000000000000001], dnl p /= UDP(sport=54392, dport=2222) [d47808ae0048412f], dnl p /= Raw(i for i in range(64)) [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f] )) AT_CHECK([cat good_frame | sed -e "s/358c/dead/" > bad_frame]) dnl 0x358c + (5201-2222) == 0x412f dnl 0xdead + (5201-2222) == 0xea50 AT_CHECK([cat good_expected | sed -e "s/412f/ea50/" > bad_expected]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_expected], [bad_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_expected | sed -e "s/412f/dead/" > bad_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) dnl Now testing with outer checksums. AT_CHECK([ovs-vsctl set Interface t1 options:csum=true], [0]) AT_CHECK([cat no_csum_expected | sed -e 's/e01312b5008e0000/e01312b5008ec84c/' > out_no_csum_expected]) AT_CHECK([cat good_expected | sed -e "s/e01312b5008e0000/e01312b5008e871d/" > good_csum_expected]) AT_CHECK([cat bad_expected | sed -e "s/e01312b5008e0000/e01312b5008eddfb/" > bad_csum_expected]) AT_CHECK([ovs-vsctl get interface p2 options], [0], [{tx_pcap=p2.pcap} ]) dnl Tx offloads disabled. CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Inner Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Outer Tx offloads enabled. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum=true]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [no_csum_frame], [out_no_csum_expected]) CHECK_L4_CHECKSUMS(p1, p2, [good_frame], [bad_frame], [good_csum_expected], [bad_csum_expected]) dnl Special case, to check if Tx offloads did happen in the driver. AT_CHECK([ovs-vsctl set Interface p2 options:ol_out_udp_tx_csum_disabled=true]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_l4_tx_csum_disabled=true]) AT_CHECK([cat good_csum_expected | sed -e "s/871d/ffff/" -e "s/412f/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum_disabled]) dnl Special case 2, to check if inner Tx offload did happen in the driver. AT_CHECK([ovs-vsctl remove Interface p2 options ol_out_udp_tx_csum]) AT_CHECK([cat good_csum_expected | sed -e "s/412f/dead/" > bad_csum_updated]) CHECK_FWD_PACKET(p1, p2, ol_l4_rx_csum_set_good, [bad_frame], [bad_csum_updated]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum_disabled]) AT_CHECK([ovs-vsctl remove Interface p2 options ol_l4_tx_csum]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - conntrack]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy --]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) dnl Modify the ip_dst addr to force changing the IP csum. AT_CHECK([ovs-ofctl add-flow br1 'in_port=p1,ip,ct_state=-trk,actions=ct(commit,nat(dst=192.168.1.1),table=0),p2']) flow_s="\ eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,\ tcp,ip_src=192.168.123.2,ip_dst=192.168.123.1,ip_frag=no,\ tcp_src=54392,tcp_dst=5201,tcp_flags=ack" AT_CHECK([ovs-ofctl compose-packet --bare "${flow_s}" > good_frame]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [0 ]) dnl Checks for good packet (Tx offloads disabled). flow_expected=$(echo "${flow_s}" | sed 's/192.168.123.1/192.168.1.1/g') AT_CHECK([ovs-ofctl compose-packet --bare "${flow_expected}" > good_expected]) dnl No Rx flag. CHECK_FWD_PACKET(p1, p2, , [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [0 ]) dnl Flag as Rx good. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [0 ]) dnl Flag as Rx bad. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_bad, [good_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [1 ]) dnl Flag as Rx partial. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_partial, [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [1 ]) dnl Checks for bad packet (Tx offloads disabled). AT_CHECK([ovs-ofctl compose-packet --bare --bad-csum "${flow_s}" > bad_frame]) dnl No Rx flag. CHECK_FWD_PACKET(p1, p2, , [bad_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [2 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [2 ]) dnl Flag as Rx good. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [2 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [2 ]) dnl Flag as Rx bad. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_bad, [bad_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [2 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [3 ]) dnl Flag as Rx partial. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_partial, [bad_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [2 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [3 ]) AT_CHECK([ovs-vsctl set Interface p2 options:ol_ip_tx_csum=true]) dnl Checks for good packet (Tx offloads enabled). dnl No Rx flag. CHECK_FWD_PACKET(p1, p2, , [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [3 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [3 ]) dnl Flag as Rx good. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [3 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [3 ]) dnl Flag as Rx bad. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_bad, [good_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [3 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [4 ]) dnl Flag as Rx partial. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_partial, [good_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [3 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [4 ]) dnl Checks for bad packet (Tx offloads enabled). dnl No Rx flag. CHECK_FWD_PACKET(p1, p2, , [bad_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [4 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [5 ]) dnl Flag as Rx good. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_good, [bad_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [4 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [5 ]) dnl Flag as Rx bad. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_bad, [bad_frame], [none]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [4 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [6 ]) dnl Flag as Rx partial. CHECK_FWD_PACKET(p1, p2, ol_ip_rx_csum_set_partial, [bad_frame], [good_expected]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_checked], [0], [4 ]) AT_CHECK([ovs-appctl coverage/read-counter conntrack_l3csum_err], [0], [6 ]) dnl Special case, check natted ICMP (for traffic flagged good). icmp_frame="0a8f393fe0738abf7e2f05840800450000440001000040010364c0a87b02c0a87b010303746c0000000045000028000100004006037bc0a87b01c0a87b021451d4780000000000000000500220002fc40000" icmp_expected="0a8f393fe0738abf7e2f05840800450000440001000040017d64c0a87b02c0a801010303fa6b00000000450000280001000040067d7bc0a80101c0a87b021451d478000000000000000050022000a9c40000" AT_CHECK([ovs-vsctl set Interface p1 options:ol_ip_rx_csum_set_good=true]) AT_CHECK([ovs-ofctl add-flow br1 'in_port=p1,icmp,ct_state=-trk,actions=ct(commit,nat(dst=192.168.1.1)),p2']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 ${icmp_frame}]) AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1]) AT_CHECK_UNQUOTED([tail -n 1 p2.pcap.txt], [0], [${icmp_expected} ]) AT_CHECK([ovs-vsctl remove Interface p1 options ol_ip_rx_csum_set_good]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - tso]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [set Open_vSwitch . other_config:userspace-tso-enable=true -- \ add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-port br1 p1 -- \ set Interface p1 type=dummy -- \ add-port br1 p2 -- \ set Interface p2 type=dummy]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) dnl Simple passthrough rule. AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=output:p2]) flow_s="in_port(1),eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x0800), \ ipv4(src=192.168.123.2,dst=192.168.123.1,proto=6,tos=1,ttl=64,frag=no), \ tcp(src=54392,dst=5201),tcp_flags(ack)" flow_s_v6="in_port(1),eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x86dd), \ ipv6(src=2001:cafe::88,dst=2001:cafe::92,proto=6), \ tcp(src=54392,dst=5201),tcp_flags(ack)" dnl Send from tso to no-tso. AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap -- \ set Interface p1 options:ol_tso_segsz=500]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}" --len 2054]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s_v6}" --len 2074]) dnl Send from tso to tso. AT_CHECK([ovs-vsctl set Interface p2 options:ol_tso_segsz=500]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}" --len 2054]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s_v6}" --len 2074]) dnl Check that first we have: dnl - 4x IPv4 500 byte payloads dnl - 4x IPv6 500 byte payloads dnl - one IPv4 2000 byte payload, and dnl - one IPv6 2000 byte payload zero500=$(printf '0%.0s' $(seq 1000)) AT_CHECK_UNQUOTED([ovs-pcap p2.pcap], [0], [dnl [0a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02c0a87b01]dnl [d47814510000000000000000501000004dc20000${zero500}] [0a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02c0a87b01]dnl [d4781451000001f400000000501000004bce0000${zero500}] [0a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02c0a87b01]dnl [d4781451000003e8000000005010000049da0000${zero500}] [0a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02c0a87b01]dnl [d4781451000005dc000000005010000047e60000${zero500}] [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000]dnl [882001cafe000000000000000000000092d4781451000000000000000050100000edfd0000]dnl [${zero500}] [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000]dnl [882001cafe000000000000000000000092d4781451000001f40000000050100000ec090000]dnl [${zero500}] [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000]dnl [882001cafe000000000000000000000092d4781451000003e80000000050100000ea150000]dnl [${zero500}] [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000]dnl [882001cafe000000000000000000000092d4781451000005dc0000000050100000e8210000]dnl [${zero500}] [0a8f394fe0738abf7e2f05840800450107f8000000004006fbaac0a87b02c0a87b01]dnl [d478145100000000000000005010000047e60000${zero500}${zero500}${zero500}${zero500}] [0a8f394fe0738abf7e2f058486dd6000000007e406002001cafe0000000000000000000000]dnl [882001cafe000000000000000000000092d4781451000000000000000050100000e8210000]dnl [${zero500}${zero500}${zero500}${zero500}] ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - tunnel tso fallback]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START([set Open_vSwitch . other_config:userspace-tso-enable=true \ -- add-br br1 -- set bridge br1 datapath-type=dummy \ other-config:hwaddr=aa:55:aa:55:00:03 \ -- add-port br1 p1 -- set Interface p1 type=dummy \ ofport_request=1]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy \ -- add-port int-br t1 -- set Interface t1 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=true ofport_request=2 \ -- add-port int-br t2 -- set Interface t2 type=geneve \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=true ofport_request=3 \ -- add-port int-br t3 -- set Interface t3 type=vxlan \ options:remote_ip=2001:cafe::93 options:key=123 \ options:csum=true ofport_request=4 \ -- add-port int-br t4 -- set Interface t4 type=geneve \ options:remote_ip=2001:cafe::93 options:key=123 \ options:csum=true ofport_request=5 \ -- add-port int-br t5 -- set Interface t5 type=gre \ options:remote_ip=2001:cafe::93 options:key=123 \ options:csum=true ofport_request=6 \ -- add-port int-br t6 -- set Interface t6 type=gre \ options:remote_ip=1.1.2.92 options:key=123 \ options:csum=false ofport_request=7], [0]) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) dnl The final tunnel intentionally has checksum turned off to exercise a dnl different code path, there is no GRE checksum offload anyways. m4_define([IPV4_TSO], [m4_join([,], [eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x0800)], [ipv4(src=192.168.123.2,dst=192.168.123.1,proto=6,tos=1,ttl=64,frag=no)], [tcp(src=54392,dst=5201),tcp_flags(ack)])]) m4_define([IPV6_TSO], [m4_join([,], [eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x86dd)], [ipv6(src=2001:cafe::88,dst=2001:cafe::92,proto=6)], [tcp(src=54392,dst=5201),tcp_flags(ack)])]) dnl Setup dummy interface tunnel connectivity. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br1 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br1 2001:cafe::88/24], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br1 1.1.2.92 aa:bb:cc:00:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br1 2001:cafe::93 aa:bb:cc:00:00:06], [0], [OK ]) AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK ]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) dnl Configure the TX interface to segment. AT_CHECK([ovs-vsctl set Interface p1 options:tx_pcap=p1.pcap -- \ set Interface int-br options:ol_tso_segsz=500]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br "IPV4_TSO" \ --len 2054]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br "IPV6_TSO" \ --len 2074]) dnl Check that first we have the following packets: dnl - IPv4 VXLAN tunnel with IPv4 payload dnl - IPv4 VXLAN tunnel with IPv6 payload dnl - IPv6 VXLAN tunnel with IPv4 payload dnl - IPv6 VXLAN tunnel with IPv6 payload dnl - IPv4 Geneve tunnel with IPv4 payload dnl - IPv4 Geneve tunnel with IPv6 payload dnl - IPv6 Geneve tunnel with IPv4 payload dnl - IPv6 Geneve tunnel with IPv6 payload dnl - IPv6 Geneve tunnel with IPv4 payload dnl - IPv6 Geneve tunnel with IPv6 payload dnl - IPv4 GRE tunnel with IPv4 payload dnl - IPv4 GRE tunnel with IPv6 payload dnl - IPv6 GRE tunnel with IPv4 payload dnl - IPv6 GRE tunnel with IPv6 payload dnl These are sorted since OVS may send payloads to the tunnels in any order. zero400=$(printf '0%.0s' $(seq 800)) zero100=$(printf '0%.0s' $(seq 200)) AT_CHECK_UNQUOTED([ovs-pcap p1.pcap | sort], [0], [dnl [aabbcc000001aa55aa55000308004500025a00004000402f31c0010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe000000000000000000000092d4781451]dnl [000000000000000050100000edfd0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500025a00014000402f31bf010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe000000000000000000000092d4781451]dnl [000001f40000000050100000ec090000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500025a00024000402f31be010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe000000000000000000000092d4781451]dnl [000003e80000000050100000ea150000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500025a00034000402f31bd010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe000000000000000000000092d4781451]dnl [000005dc0000000050100000e8210000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200004000401131d6010102580101025ce01312b5024e5f360800000000007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000000000000000050100000edfd0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200004000401131d6010102580101025ce01317c1024efcd10000655800007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000000000000000050100000edfd0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200014000401131d5010102580101025ce01312b5024e5f360800000000007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000001f40000000050100000ec090000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200014000401131d5010102580101025ce01317c1024efcd10000655800007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000001f40000000050100000ec090000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200024000401131d4010102580101025ce01312b5024e5f360800000000007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000003e80000000050100000ea150000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200024000401131d4010102580101025ce01317c1024efcd10000655800007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000003e80000000050100000ea150000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200034000401131d3010102580101025ce01312b5024e5f360800000000007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000005dc0000000050100000e8210000${zero100}${zero400}] [aabbcc000001aa55aa55000308004500026200034000401131d3010102580101025ce01317c1024efcd10000655800007b00]dnl [0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000092d4781451000005dc0000000050100000e8210000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024600004000402f31d3010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058408004501021c0000000040060187c0a87b02c0a87b01d47814510000000000000000501000004dc20000]dnl [${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024600014000402f31d2010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058408004501021c0001000040060186c0a87b02c0a87b01d4781451000001f400000000501000004bce0000]dnl [${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024600024000402f31d1010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058408004501021c0002000040060185c0a87b02c0a87b01d4781451000003e8000000005010000049da0000]dnl [${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024600034000402f31d0010102580101025c200065580000007b0a8f394fe0738abf]dnl [7e2f058408004501021c0003000040060184c0a87b02c0a87b01d4781451000005dc000000005010000047e60000]dnl [${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00004000401131e9010102580101025ce01312b5023abd990800000000007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02c0a87b01d4781451000000000000000050100000]dnl [4dc20000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00004000401131e9010102580101025ce01317c1023a5b350000655800007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02c0a87b01d4781451000000000000000050100000]dnl [4dc20000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00014000401131e8010102580101025ce01312b5023abd990800000000007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02c0a87b01d4781451000001f40000000050100000]dnl [4bce0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00014000401131e8010102580101025ce01317c1023a5b350000655800007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02c0a87b01d4781451000001f40000000050100000]dnl [4bce0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00024000401131e7010102580101025ce01312b5023abd990800000000007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02c0a87b01d4781451000003e80000000050100000]dnl [49da0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00024000401131e7010102580101025ce01317c1023a5b350000655800007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02c0a87b01d4781451000003e80000000050100000]dnl [49da0000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00034000401131e6010102580101025ce01312b5023abd990800000000007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02c0a87b01d4781451000005dc0000000050100000]dnl [47e60000${zero100}${zero400}] [aabbcc000001aa55aa55000308004501024e00034000401131e6010102580101025ce01317c1023a5b350000655800007b00]dnl [0a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02c0a87b01d4781451000005dc0000000050100000]dnl [47e60000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60000000024a2f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a0006558da8e00000000007b0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000]dnl [000000882001cafe000000000000000000000092d4781451000005dc0000000050100000e8210000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60000000024a2f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a0006558dc8200000000007b0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000]dnl [000000882001cafe000000000000000000000092d4781451000003e80000000050100000ea150000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60000000024a2f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a0006558de7600000000007b0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000]dnl [000000882001cafe000000000000000000000092d4781451000001f40000000050100000ec090000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60000000024a2f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a0006558e06a00000000007b0a8f394fe0738abf7e2f058486dd60000000020806002001cafe0000000000000000]dnl [000000882001cafe000000000000000000000092d4781451000000000000000050100000edfd0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5024e8ed10800000000007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000000000000000050100000edfd0000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5024e8ed10800000000007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000001f40000000050100000ec090000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5024e8ed10800000000007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000003e80000000050100000ea150000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5024e8ed10800000000007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000005dc0000000050100000e8210000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1024e2c6d0000655800007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000000000000000050100000edfd0000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1024e2c6d0000655800007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000001f40000000050100000ec090000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1024e2c6d0000655800007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000003e80000000050100000ea150000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd60000000024e11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1024e2c6d0000655800007b000a8f394fe0738abf7e2f058486dd60000000020806002001cafe00000000]dnl [00000000000000882001cafe000000000000000000000092d4781451000005dc0000000050100000e8210000${zero100}]dnl [${zero400}] [aabbcc000006aa55aa55000386dd6010000002362f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a00065583a4e00000000007b0a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02c0a87b01]dnl [d4781451000005dc000000005010000047e60000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd6010000002362f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a00065583c4300000000007b0a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02c0a87b01]dnl [d4781451000003e8000000005010000049da0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd6010000002362f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a00065583e3800000000007b0a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02c0a87b01]dnl [d4781451000001f400000000501000004bce0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd6010000002362f402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093a0006558402d00000000007b0a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02c0a87b01]dnl [d47814510000000000000000501000004dc20000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5023aed340800000000007b000a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02]dnl [c0a87b01d47814510000000000000000501000004dc20000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5023aed340800000000007b000a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02]dnl [c0a87b01d4781451000001f400000000501000004bce0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5023aed340800000000007b000a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02]dnl [c0a87b01d4781451000003e8000000005010000049da0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01312b5023aed340800000000007b000a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02]dnl [c0a87b01d4781451000005dc000000005010000047e60000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1023a8ad00000655800007b000a8f394fe0738abf7e2f058408004501021c0000000040060187c0a87b02]dnl [c0a87b01d47814510000000000000000501000004dc20000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1023a8ad00000655800007b000a8f394fe0738abf7e2f058408004501021c0001000040060186c0a87b02]dnl [c0a87b01d4781451000001f400000000501000004bce0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1023a8ad00000655800007b000a8f394fe0738abf7e2f058408004501021c0002000040060185c0a87b02]dnl [c0a87b01d4781451000003e8000000005010000049da0000${zero100}${zero400}] [aabbcc000006aa55aa55000386dd60100000023a11402001cafe0000000000000000000000882001cafe0000000000000000]dnl [00000093e01317c1023a8ad00000655800007b000a8f394fe0738abf7e2f058408004501021c0003000040060184c0a87b02]dnl [c0a87b01d4781451000005dc000000005010000047e60000${zero100}${zero400}] ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - tso + check_pkt_len]) AT_KEYWORDS([userspace offload]) OVS_VSWITCHD_START( [set Open_vSwitch . other_config:userspace-tso-enable=true]) add_of_ports --pcap br0 $(seq 1 3) AT_CHECK([ovs-appctl vlog/set netdev_dummy:file:dbg]) AT_DATA([flows.txt], [dnl table=0,in_port=1,ip actions=check_pkt_larger(514)->NXM_NX_REG0[[0]],resubmit(,1) table=1,ip,reg0=0x0 actions=output:2 table=1,ip,reg0=0x1 actions=output:3 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow_s="in_port(1),eth(src=8a:bf:7e:2f:05:84,dst=0a:8f:39:4f:e0:73),eth_type(0x0800), \ ipv4(src=192.168.123.2,dst=192.168.123.1,proto=6,tos=1,ttl=64,frag=no), \ tcp(src=54392,dst=5201),tcp_flags(ack)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}"]) AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | strip_stats], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:0, bytes:0, used:never, actions:check_pkt_len(size=514,gt(3),le(2)) ]) AT_CHECK([ovs-pcap p2-tx.pcap | wc -l], [0], [1 ]) AT_CHECK([ovs-pcap p3-tx.pcap | wc -l], [0], [0 ]) AT_CHECK([ovs-vsctl set Interface p1 options:ol_tso_segsz=460]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}"]) AT_CHECK([ovs-pcap p2-tx.pcap | wc -l], [0], [2 ]) AT_CHECK([ovs-pcap p3-tx.pcap | wc -l], [0], [0 ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}" --len 974]) AT_CHECK([ovs-pcap p2-tx.pcap | wc -l], [0], [4 ]) AT_CHECK([ovs-pcap p3-tx.pcap | wc -l], [0], [0 ]) AT_CHECK([ovs-vsctl set Interface p1 options:ol_tso_segsz=462]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "${flow_s}" --len 974]) AT_CHECK([ovs-pcap p2-tx.pcap | wc -l], [0], [4 ]) AT_CHECK([ovs-pcap p3-tx.pcap | wc -l], [0], [2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - revalidators handle dp modification fail correctly]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set interface p1 type=dummy \ -- set bridge br0 datapath-type=dummy \ -- add-port br0 p2 \ -- set interface p2 type=dummy -- ]) AT_CHECK([ovs-ofctl add-flow br0 'table=0,in_port=p1,actions=p2']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2),tcp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2),tcp(src=1,dst=2)']) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*thread://' | strip_xout_keep_actions ], [0], [ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:2 ]) dnl Wait for the dp flow to enter OPERATIONAL state. AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl revalidator/pause]) dnl Delete all dp flows, so flow modification will fail. AT_CHECK([ovs-appctl dpctl/del-flows]) AT_CHECK([ovs-appctl revalidator/resume]) dnl Replace OpenFlow rules, trigger revalidation and wait for it to complete. AT_CHECK([echo 'table=0,in_port=p1,ip actions=ct(commit)' | ovs-ofctl --bundle replace-flows br0 -]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Inconsistent ukey should be deleted. AT_CHECK([ovs-appctl upcall/show | grep keys | grep -q -v 0], [1]) dnl Check the log for the flow modification error. AT_CHECK([grep -q -E ".*failed to put.*$" ovs-vswitchd.log]) dnl Remove warning logs to let test suite pass. OVS_VSWITCHD_STOP(["dnl /.*failed to put.*$/d /.*failed to flow_del.*$/d"]) AT_CLEANUP AT_SETUP([dpif-netdev - MFEX Autovalidator]) AT_SKIP_IF([! $PYTHON3 -c "import scapy"], [], []) AT_SKIP_IF([! $PYTHON3 $srcdir/genpkts.py 2000 > packets]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set Interface p1 type=dummy-pmd], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"]) AT_SKIP_IF([! ovs-appctl dpif-netdev/miniflow-parser-get | sed 1,4d | grep "True"], [], [dnl ]) AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_avx512], [0], [dnl DPIF implementation set to dpif_avx512. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set autovalidator], [0], [dnl Miniflow extract implementation set to autovalidator. ]) cat packets | while read line; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 $line], [0], [ignore]) done OVS_WAIT_UNTIL([test `ovs-vsctl get interface p1 statistics | grep -oP 'rx_packets=\s*\K\d+'` -ge 16000]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpif-netdev - MFEX Autovalidator Fuzzy]) AT_SKIP_IF([! $PYTHON3 -c "import scapy"], [], []) AT_SKIP_IF([! $PYTHON3 $srcdir/genpkts.py 2000 fuzzy > packets]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set Interface p1 type=dummy-pmd], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"]) AT_SKIP_IF([! ovs-appctl dpif-netdev/miniflow-parser-get | sed 1,4d | grep "True"], [], [dnl ]) AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_avx512], [0], [dnl DPIF implementation set to dpif_avx512. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set autovalidator], [0], [dnl Miniflow extract implementation set to autovalidator. ]) cat packets | while read line; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 $line], [0], [ignore]) done OVS_WAIT_UNTIL([test `ovs-vsctl get interface p1 statistics | grep -oP 'rx_packets=\s*\K\d+'` -ge 16000]) OVS_VSWITCHD_STOP(["dnl /upcall: datapath reached the dynamic limit of .* flows./d"]) AT_CLEANUP AT_SETUP([dpif-netdev - MFEX Configuration]) OVS_VSWITCHD_START( [set Open_vSwitch . other_config:pmd-cpu-mask=0x1 \ -- add-port br0 p1 \ -- set Interface p1 type=dummy-pmd], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set scalar 1], [2], [], [dnl Error: unknown argument 1. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 6 study 300 xyz], [2], [], [dnl Error: invalid study_pkt_cnt value: xyz. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set scalar abcd], [2], [], [dnl Error: unknown argument abcd. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 0 scalar abcd], [2], [], [dnl Error: unknown argument abcd. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd], [2], [], [dnl Error: -pmd option requires a thread id argument. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set tudy abcd], [2], [], [dnl Error: unknown argument abcd. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 7 study abcd], [2], [], [dnl Error: invalid study_pkt_cnt value: abcd. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 0 study], [0], [dnl Miniflow extract implementation set to study, on pmd thread 0, studying 128 packets. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 0 study 512], [0], [dnl Miniflow extract implementation set to study, on pmd thread 0, studying 512 packets. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set study 512], [0], [dnl Miniflow extract implementation set to study, studying 512 packets. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set study], [0], [dnl Miniflow extract implementation set to study, studying 128 packets. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 0 autovalidator], [0], [dnl Miniflow extract implementation set to autovalidator, on pmd thread 0. ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd zero study], [2], [], [dnl Error: miniflow extract parser not changed, PMD thread passed is not valid: 'zero'. Pass a valid pmd thread ID. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 1], [2], [], [dnl Error: no miniflow extract name provided. Output of miniflow-parser-get shows implementation list. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 1 superstudy], [2], [], [dnl Error: unknown miniflow extract implementation superstudy. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set superstudy], [2], [], [dnl Error: unknown miniflow extract implementation superstudy. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set -pmd 1 study -pmd], [2], [], [dnl Error: invalid study_pkt_cnt value: -pmd. ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP(["dnl /Error: unknown argument 1./d /Error: invalid study_pkt_cnt value: xyz./d /Error: unknown argument abcd./d /Error: -pmd option requires a thread id argument./d /Error: invalid study_pkt_cnt value: abcd./d /Error: miniflow extract parser not changed, PMD thread passed is not valid: 'zero'. Pass a valid pmd thread ID./d /Error: no miniflow extract name provided. Output of miniflow-parser-get shows implementation list./d /Error: unknown miniflow extract implementation superstudy./d /Error: invalid study_pkt_cnt value: -pmd./d"]) AT_CLEANUP AT_SETUP([datapath - Actions Autovalidator Checksum]) OVS_VSWITCHD_START(add-port br0 p0 -- set Interface p0 type=dummy \ -- add-port br0 p1 -- set Interface p1 type=dummy) AT_CHECK([ovs-appctl odp-execute/action-impl-set autovalidator], [0], [dnl Action implementation set to autovalidator. ]) dnl Add flows to trigger checksum calculation. AT_DATA([flows.txt], [dnl in_port=p0,ip,actions=mod_nw_src=10.1.1.1,p1 in_port=p0,ipv6,actions=set_field:fc00::100->ipv6_src,p1 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-vsctl set Interface p1 options:pcap=p1.pcap]) dnl IPv4 packet with values that will trigger carry-over addition for checksum. flow_s_v4=" eth_src=47:42:86:08:17:50,eth_dst=3e:55:b5:9e:3a:fb,dl_type=0x0800, nw_src=229.167.36.90,nw_dst=130.161.64.186,nw_proto=6,nw_ttl=64,nw_frag=no, tp_src=54392,tp_dst=5201,tcp_flags=ack" good_frame=$(ovs-ofctl compose-packet --bare "${flow_s_v4}") AT_CHECK([ovs-appctl netdev-dummy/receive p0 ${good_frame}]) dnl Checksum should change to 0xAC33 with ip_src changed to 10.1.1.1 dnl by the datapath while processing the packet. flow_expected=$(echo "${flow_s_v4}" | sed 's/229.167.36.90/10.1.1.1/g') good_expected=$(ovs-ofctl compose-packet --bare "${flow_expected}") AT_CHECK([ovs-pcap p1.pcap > p1.pcap.txt 2>&1]) AT_CHECK_UNQUOTED([tail -n 1 p1.pcap.txt], [0], [${good_expected} ]) dnl Repeat similar test for IPv6. flow_s_v6=" eth_src=8a:bf:7e:2f:05:84,eth_dst=0a:8f:39:4f:e0:73,dl_type=0x86dd, ipv6_src=2f8a:2076:3926:9e7:2d47:4bc9:9c7:17f3, ipv6_dst=7287:10dd:2fb9:41d5:3eb2:2c7a:11b0:6258, ipv6_label=0x51ac,nw_proto=6,nw_ttl=142,nw_frag=no, tp_src=20405,tp_dst=20662,tcp_flags=ack" good_frame_v6=$(ovs-ofctl compose-packet --bare "${flow_s_v6}") AT_CHECK([ovs-appctl netdev-dummy/receive p0 ${good_frame_v6}]) dnl Checksum should change to 0x59FD with ipv6_src changed to fc00::100 dnl by the datapath while processing the packet. flow_expected_v6=$(echo "${flow_s_v6}" | \ sed 's/2f8a:2076:3926:9e7:2d47:4bc9:9c7:17f3/fc00::100/g') good_expected_v6=$(ovs-ofctl compose-packet --bare "${flow_expected_v6}") AT_CHECK([ovs-pcap p1.pcap > p1.pcap.txt 2>&1]) AT_CHECK_UNQUOTED([tail -n 1 p1.pcap.txt], [0], [${good_expected_v6} ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/drop-stats.at000066400000000000000000000257501514270232600225610ustar00rootroot00000000000000AT_BANNER([drop-stats]) AT_SETUP([drop-stats - cli tests]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_DATA([flows.txt], [dnl table=0,in_port=1,actions=drop ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=1 actions=drop ]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ], [0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.0/' | sort], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:212, used:0.0, actions:drop ]) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_of_pipeline ], [0], [dnl 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([drop-stats - pipeline and recursion drops]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_DATA([flows.txt], [dnl table=0,in_port=1,actions=drop ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=1 actions=drop ]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ], [0], [ignore]) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_of_pipeline ], [0], [dnl 1 ]) AT_DATA([flows.txt], [dnl table=0, in_port=1, actions=goto_table:1 table=1, in_port=1, actions=goto_table:2 table=2, in_port=1, actions=resubmit(,1) ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [ignore]) ovs-appctl time/warp 15000 AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ], [0], [ignore]) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_recursion_too_deep ], [0], [dnl 1 ]) OVS_VSWITCHD_STOP(["/|WARN|/d"]) AT_CLEANUP AT_SETUP([drop-stats - too many resubmit]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 1 64`; do j=`expr $i + 1` echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done echo "in_port=65, actions=local") > flows.txt AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ], [0], [ignore]) ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_too_many_resubmit ], [0], [dnl 1 ]) OVS_VSWITCHD_STOP(["/|WARN|/d"]) AT_CLEANUP AT_SETUP([drop-stats - stack too deep]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 1 12`; do j=`expr $i + 1` echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done push="push:NXM_NX_REG0[[]]" echo "in_port=13, actions=$push,$push,$push,$push,$push,$push,$push,$push") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_stack_too_deep ], [0], [dnl 1 ]) OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of stack/d"]) AT_CLEANUP AT_SETUP([drop-stats - too many mpls labels]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_DATA([flows.txt], [dnl table=0, in_port=1, actions=push_mpls:0x8847, resubmit:3 table=0, in_port=3, actions=push_mpls:0x8847, set_field:10->mpls_label, set_field:15->mpls_label, resubmit:4 table=0, in_port=4, actions=push_mpls:0x8847, set_field:11->mpls_label, resubmit:5 table=0, in_port=5, actions=push_mpls:0x8847, set_field:12->mpls_label, resubmit:6 table=0, in_port=6, actions=push_mpls:0x8847, set_field:13->mpls_label, output:2 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 'in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' ], [0], [ignore]) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_too_many_mpls_labels ], [0], [dnl 1 ]) OVS_VSWITCHD_STOP(["/|WARN|/d"]) AT_CLEANUP m4_define([ICMP_PKT], [m4_join([,], [in_port(1),packet_type(ns=0,id=0)], [eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e)], [ipv4(src=192.168.10.10,dst=192.168.10.30,proto=1,tos=0,ttl=64,frag=no)], [icmp(type=8,code=0)])]) AT_SETUP([drop-stats - bridge sampling]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_DATA([flows.txt], [dnl table=0,in_port=1,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \ --id=@fix create ipfix targets=\"127.0.0.1:4739\" \ sampling=1], [0], [ignore]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:2, bytes:212, used:0.0s, dnl actions:userspace(pid=0,ipfix(output_port=4294967295)) ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 0 ]) dnl Now activate explicit sampled drops. AT_CHECK([ovs-vsctl set Open_vSwitch . other-config:explicit-sampled-drops=true]) AT_CHECK([ovs-appctl revalidator/wait]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:5, bytes:530, used:0.0s, dnl actions:userspace(pid=0,ipfix(output_port=4294967295)),drop ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 3 ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP AT_SETUP([drop-stats - sampling action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0,in_port=1,actions=sample(probability=65535,collector_set_id=1) table=0,in_port=2,actions=sample(probability=32767,collector_set_id=1),load:0->reg0 table=0,in_port=3,actions=clone(sample(probability=65535,collector_set_id=1)) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-vsctl --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ ipfix=@ipfix], [0], [ignore]) m4_define([USERSPACE_SAMPLE_ACTION], [m4_join([,], [userspace(pid=0], [flow_sample(probability=$1,collector_set_id=1,obs_domain_id=0], [obs_point_id=0,output_port=4294967295))])]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:2, bytes:212, used:0.0s, dnl actions:USERSPACE_SAMPLE_ACTION(65535) ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 0 ]) dnl Now activate explicit sampled drops. AT_CHECK([ovs-vsctl set Open_vSwitch . other-config:explicit-sampled-drops=true]) AT_CHECK([ovs-appctl revalidator/wait]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:5, bytes:530, used:0.0s, dnl actions:USERSPACE_SAMPLE_ACTION(65535),drop ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 3 ]) AT_CHECK([ovs-appctl dpctl/del-flows]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p2 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:2, bytes:212, used:0.0s, dnl actions:sample(sample=50.0%,actions(USERSPACE_SAMPLE_ACTION(32767))),drop ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 6 ]) AT_CHECK([ovs-appctl dpctl/del-flows]) for i in $(seq 1 3); do AT_CHECK([ovs-appctl netdev-dummy/receive p3 'ICMP_PKT'], [0], [ignore]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), dnl packets:2, bytes:212, used:0.0s, dnl actions:USERSPACE_SAMPLE_ACTION(65535),drop ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter drop_action_of_pipeline], [0], [dnl 9 ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/file_name.at000066400000000000000000000062601514270232600223730ustar00rootroot00000000000000AT_BANNER([test dir_name and base_name functions]) m4_define([CHECK_FILE_NAME], [AT_SETUP([components of "$1" are "$2", "$3"]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([dir_name base_name]) AT_CHECK([ovstest test-util file_name "AS_ESCAPE($1)"], [0], [$2 $3 ]) AT_CLEANUP]) # These are the test cases given in POSIX for dirname() and basename(). CHECK_FILE_NAME([/usr/lib], [/usr], [lib]) CHECK_FILE_NAME([/usr/], [/], [usr]) CHECK_FILE_NAME([usr], [.], [usr]) CHECK_FILE_NAME([/], [/], [/]) CHECK_FILE_NAME([.], [.], [.]) CHECK_FILE_NAME([..], [.], [..]) CHECK_FILE_NAME([//], [//], [//]) # / is also allowed CHECK_FILE_NAME([//foo], [//], [foo]) # / is also allowed for dirname CHECK_FILE_NAME([], [.], [.]) # Additional test cases. CHECK_FILE_NAME([dir/file], [dir], [file]) CHECK_FILE_NAME([dir/file/], [dir], [file]) CHECK_FILE_NAME([dir/file//], [dir], [file]) CHECK_FILE_NAME([///foo], [/], [foo]) AT_BANNER([test follow_symlinks function]) m4_define([CHECK_FOLLOW], [echo "check $1 -> $2" AT_CHECK_UNQUOTED([ovstest test-util follow-symlinks "$1"], [0], [$2 ]) echo]) AT_SETUP([follow_symlinks - relative symlinks]) : > target ln -s target source AT_SKIP_IF([test ! -h source]) CHECK_FOLLOW([source], [target]) mkdir dir ln -s target2 dir/source2 CHECK_FOLLOW([dir/source2], [dir/target2]) mkdir dir/dir2 ln -s dir/b a ln -s c dir/b ln -s dir2/d dir/c CHECK_FOLLOW([a], [dir/dir2/d]) AT_CLEANUP AT_SETUP([follow_symlinks - absolute symlinks]) : > target ln -s "`pwd`/target" source AT_SKIP_IF([test ! -h source]) CHECK_FOLLOW([source], [`pwd`/target]) mkdir dir ln -s "`pwd`/dir/target2" dir/source2 CHECK_FOLLOW([dir/source2], [`pwd`/dir/target2]) mkdir dir/dir2 ln -s "`pwd`/dir/b" a ln -s "`pwd`/dir/c" dir/b ln -s "`pwd`/dir/dir2/d" dir/c CHECK_FOLLOW([a], [`pwd`/dir/dir2/d]) AT_CLEANUP AT_SETUP([follow_symlinks - symlinks to directories]) mkdir target ln -s target source AT_SKIP_IF([test ! -h source]) ln -s target/ source2 CHECK_FOLLOW([source], [target]) CHECK_FOLLOW([source2], [target/]) # follow_symlinks() doesn't expand symlinks in the middle of a name. : > source/x CHECK_FOLLOW([source/x], [source/x]) AT_CLEANUP AT_SETUP([follow_symlinks - nonexistent targets]) ln -s target source AT_SKIP_IF([test ! -h source]) CHECK_FOLLOW([source], [target]) CHECK_FOLLOW([target], [target]) CHECK_FOLLOW([target], [target]) AT_CLEANUP AT_SETUP([follow_symlinks - regular files]) touch x CHECK_FOLLOW([x], [x]) AT_CLEANUP AT_SETUP([follow_symlinks - device targets]) AT_SKIP_IF([test ! -e /dev/null]) AT_SKIP_IF([test ! -e /dev/full]) ln -s /dev/null x ln -s /dev/full y CHECK_FOLLOW([x], [/dev/null]) CHECK_FOLLOW([y], [/dev/full]) AT_CLEANUP AT_SETUP([follow_symlinks - nonexistent files]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) CHECK_FOLLOW([nonexistent], [nonexistent]) CHECK_FOLLOW([a/b/c], [a/b/c]) CHECK_FOLLOW([/a/b/c], [/a/b/c]) CHECK_FOLLOW([//a/b/c], [//a/b/c]) AT_CLEANUP AT_SETUP([follow_symlinks - symlink loop]) ln -s a b AT_SKIP_IF([test ! -h b]) ln -s b a AT_SKIP_IF([test ! -h a]) AT_CHECK([ovstest test-util follow-symlinks a], [0], [a ], [stderr]) AT_CHECK([sed 's/^[[^|]]*|//' stderr], [0], [00001|util|WARN|a: too many levels of symlinks ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/flowgen.py000077500000000000000000000232671514270232600221520ustar00rootroot00000000000000#! /usr/bin/env python3 # Copyright (c) 2009, 2010, 2011, 2012, 2015, 2017 Nicira, Inc. # # 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. import socket import struct def pack_ethaddr(ea): octets = ea.split(':') assert len(octets) == 6 return b''.join([struct.pack('B', int(octet, 16)) for octet in octets]) def output(attrs): # Compose flow. flow = {} flow['DL_SRC'] = "00:02:e3:0f:80:a4" flow['DL_DST'] = "00:1a:92:40:ac:05" flow['NW_PROTO'] = 0 flow['NW_TOS'] = 0 flow['NW_SRC'] = '0.0.0.0' flow['NW_DST'] = '0.0.0.0' flow['TP_SRC'] = 0 flow['TP_DST'] = 0 if 'DL_VLAN' in attrs: flow['DL_VLAN'] = {'none': 0xffff, 'zero': 0, 'nonzero': 0x0123}[attrs['DL_VLAN']] else: flow['DL_VLAN'] = 0xffff # OFP_VLAN_NONE if attrs['DL_HEADER'] == '802.2': flow['DL_TYPE'] = 0x5ff # OFP_DL_TYPE_NOT_ETH_TYPE elif attrs['DL_TYPE'] == 'ip': flow['DL_TYPE'] = 0x0800 # ETH_TYPE_IP flow['NW_SRC'] = '10.0.2.15' flow['NW_DST'] = '192.168.1.20' flow['NW_TOS'] = 44 if attrs['TP_PROTO'] == 'other': flow['NW_PROTO'] = 42 elif attrs['TP_PROTO'] in ('TCP', 'TCP+options'): flow['NW_PROTO'] = 6 # IPPROTO_TCP flow['TP_SRC'] = 6667 flow['TP_DST'] = 9998 elif attrs['TP_PROTO'] == 'UDP': flow['NW_PROTO'] = 17 # IPPROTO_UDP flow['TP_SRC'] = 1112 flow['TP_DST'] = 2223 elif attrs['TP_PROTO'] == 'ICMP': flow['NW_PROTO'] = 1 # IPPROTO_ICMP flow['TP_SRC'] = 8 # echo request flow['TP_DST'] = 0 # code else: assert False if attrs['IP_FRAGMENT'] not in ('no', 'first'): flow['TP_SRC'] = flow['TP_DST'] = 0 elif attrs['DL_TYPE'] == 'non-ip': flow['DL_TYPE'] = 0x5678 else: assert False # Compose packet packet = b'' wildcards = 1 << 5 | 1 << 6 | 1 << 7 | 32 << 8 | 32 << 14 | 1 << 21 packet += pack_ethaddr(flow['DL_DST']) packet += pack_ethaddr(flow['DL_SRC']) if flow['DL_VLAN'] != 0xffff: packet += struct.pack('>HH', 0x8100, flow['DL_VLAN']) len_ofs = len(packet) if attrs['DL_HEADER'].startswith('802.2'): packet += struct.pack('>H', 0) if attrs['DL_HEADER'] == '802.2': packet += struct.pack('BBB', 0x42, 0x42, 0x03) # LLC for 802.1D STP else: if attrs['DL_HEADER'] == '802.2+SNAP': packet += struct.pack('BBB', 0xaa, 0xaa, 0x03) # LLC for SNAP packet += struct.pack('BBB', 0, 0, 0) # SNAP OUI packet += struct.pack('>H', flow['DL_TYPE']) if attrs['DL_TYPE'] == 'ip': ip = struct.pack('>BBHHHBBHLL', (4 << 4) | 5, # version, hdrlen flow['NW_TOS'], # type of service 0, # total length, filled in later 65432, # id 0, # frag offset 64, # ttl flow['NW_PROTO'], # protocol 0, # checksum 0x0a00020f, # source 0xc0a80114) # dest wildcards &= ~(1 << 5 | 63 << 8 | 63 << 14 | 1 << 21) if attrs['IP_OPTIONS'] == 'yes': ip = struct.pack('B', (4 << 4) | 8) + ip[1:] ip += struct.pack('>BBHHHBBBx', 130, # type 11, # length 0x6bc5, # top secret 0xabcd, 0x1234, 1, 2, 3) if attrs['IP_FRAGMENT'] != 'no': frag_map = {'first': 0x2000, # more frags, ofs 0 'middle': 0x2111, # more frags, ofs 0x888 'last': 0x0222} # last frag, ofs 0x1110 ip = (ip[:6] + struct.pack('>H', frag_map[attrs['IP_FRAGMENT']]) + ip[8:]) if attrs['IP_FRAGMENT'] in ('no', 'first'): if attrs['TP_PROTO'].startswith('TCP'): tcp = struct.pack('>HHLLHHHH', flow['TP_SRC'], # source port flow['TP_DST'], # dest port 87123455, # seqno 712378912, # ackno (5 << 12) | 0x02 | 0x10, # hdrlen, SYN, ACK 5823, # window size 18923, # checksum 12893) # urgent pointer if attrs['TP_PROTO'] == 'TCP+options': tcp = (tcp[:12] + struct.pack('>H', (6 << 12) | 0x02 | 0x10) + tcp[14:]) tcp += struct.pack('>BBH', 2, 4, 1975) # MSS option tcp += b'payload' ip += tcp wildcards &= ~(1 << 6 | 1 << 7) elif attrs['TP_PROTO'] == 'UDP': udp_len = 15 udp = struct.pack('>HHHH', flow['TP_SRC'], flow['TP_DST'], udp_len, 0) while len(udp) < udp_len: udp += struct.pack('B', udp_len) ip += udp wildcards &= ~(1 << 6 | 1 << 7) elif attrs['TP_PROTO'] == 'ICMP': ip += struct.pack('>BBHHH', 8, # echo request 0, # code 0, # checksum 736, # identifier 931) # sequence number wildcards &= ~(1 << 6 | 1 << 7) elif attrs['TP_PROTO'] == 'other': ip += b'other header' else: assert False ip = ip[:2] + struct.pack('>H', len(ip)) + ip[4:] packet += ip if attrs['DL_HEADER'].startswith('802.2'): packet_len = len(packet) - 14 if flow['DL_VLAN'] != 0xffff: packet_len -= 4 packet = (packet[:len_ofs] + struct.pack('>H', packet_len) + packet[len_ofs + 2:]) print(' '.join(['%s=%s' % (k, v) for k, v in attrs.items()])) print(' '.join(['%s=%s' % (k, v) for k, v in flow.items()])) print() flows.write(struct.pack('>LH', wildcards, # wildcards 1)) # in_port flows.write(pack_ethaddr(flow['DL_SRC'])) flows.write(pack_ethaddr(flow['DL_DST'])) flows.write(struct.pack('>HBxHBBxx', flow['DL_VLAN'], 0, # DL_VLAN_PCP flow['DL_TYPE'], flow['NW_TOS'], flow['NW_PROTO'])) flows.write(socket.inet_aton(flow['NW_SRC'])) flows.write(socket.inet_aton(flow['NW_DST'])) flows.write(struct.pack('>HH', flow['TP_SRC'], flow['TP_DST'])) packets.write(struct.pack('>LLLL', 0, # timestamp seconds 0, # timestamp microseconds len(packet), # bytes saved len(packet))) # total length packets.write(packet) flows = open('flows', 'wb') packets = open('pcap', 'wb') # Print pcap file header. packets.write(struct.pack('>LHHLLLL', 0xa1b2c3d4, # magic number 2, # major version 4, # minor version 0, # time zone offset 0, # time stamp accuracy 1518, # snaplen 1)) # Ethernet output({'DL_HEADER': '802.2'}) for dl_header in ('802.2+SNAP', 'Ethernet'): a = {'DL_HEADER': dl_header} for dl_vlan in ('none', 'zero', 'nonzero'): b = a.copy() b['DL_VLAN'] = dl_vlan # Non-IP case. c = b.copy() c['DL_TYPE'] = 'non-ip' output(c) for ip_options in ('no', 'yes'): c = b.copy() c['DL_TYPE'] = 'ip' c['IP_OPTIONS'] = ip_options for ip_fragment in ('no', 'first', 'middle', 'last'): d = c.copy() d['IP_FRAGMENT'] = ip_fragment for tp_proto in ('TCP', 'TCP+options', 'UDP', 'ICMP', 'other'): e = d.copy() e['TP_PROTO'] = tp_proto output(e) openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression-list.at000066400000000000000000000026621514270232600246030ustar00rootroot00000000000000TEST_FUZZ_REGRESSION([flow_extract_fuzzer-5112775280951296]) TEST_FUZZ_REGRESSION([flow_extract_fuzzer-5457710546944000]) TEST_FUZZ_REGRESSION([json_parser_fuzzer-4790908707930112]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-4584019764183040]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-4671928750702592]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-4730143510626304]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-4854119633256448]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5070973479944192]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5072291707748352]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5147430386401280]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5168455220199424]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5190507327127552]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5204186701496320]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5394482341085184]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5395207246839808]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5647458888581120]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5674119268925440]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5674419757252608]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5677588436484096]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5706562554298368]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5722747668791296]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6285128790704128]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6470117922701312]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6502620041576448]) TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6540965472632832]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression.at000066400000000000000000000011431514270232600236230ustar00rootroot00000000000000AT_BANNER([fuzz regression]) OVS_START_SHELL_HELPERS test_fuzz_regression() { filename=$top_srcdir/tests/fuzz-regression/$1 AS_CASE([$1], [flow_extract_fuzzer*], [ovs-ofctl parse-packet < $filename], [ofp_print_fuzzer*], [ovs-ofctl ofp-parse - < $filename], [json_parser_fuzzer*], [ovstest test-json $filename], [*], [AT_FAIL_IF([:])]) status=$? AT_CHECK([test $status = 0 || test $status = 1]) } OVS_END_SHELL_HELPERS m4_define([TEST_FUZZ_REGRESSION], [AT_SETUP([fuzz regression - $1]) test_fuzz_regression $1 AT_CLEANUP]) m4_include([tests/fuzz-regression-list.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/000077500000000000000000000000001514270232600232765ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/flow_extract_fuzzer-5112775280951296000066400000000000000000000000661514270232600311140ustar00rootroot00000000000000 openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/flow_extract_fuzzer-5457710546944000000066400000000000000000000003431514270232600311010ustar00rootroot00000000000000 :                   openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/json_parser_fuzzer-4790908707930112000066400000000000000000000000531514270232600307310ustar00rootroot00000000000000340282366920938463461761716499e2147483637 openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-4584019764183040000066400000000000000000000002001514270232600303730ustar00rootroot00000000000000 8#  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-4671928750702592000066400000000000000000000000401514270232600304070ustar00rootroot00000000000000 #  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-4730143510626304000066400000000000000000000000141514270232600303570ustar00rootroot00000000000000 openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-4854119633256448000066400000000000000000000000601514270232600304100ustar00rootroot000000000000000 @ openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5070973479944192000066400000000000000000000001601514270232600304200ustar00rootroot00000000000000p  `  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5072291707748352000066400000000000000000000001601514270232600304050ustar00rootroot00000000000000p  `  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5147430386401280000066400000000000000000000002001514270232600303630ustar00rootroot00000000000000 # openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5168455220199424000066400000000000000000000001201514270232600303770ustar00rootroot00000000000000P  7@ hash  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5190507327127552000066400000000000000000000001611514270232600303760ustar00rootroot00000000000000p  `  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5204186701496320000066400000000000000000000001201514270232600303660ustar00rootroot00000000000000P openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5394482341085184000066400000000000000000000002001514270232600304000ustar00rootroot00000000000000 0#  ~ openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5395207246839808000066400000000000000000000002341514270232600304210ustar00rootroot00000000000000  8#   ~ openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5647458888581120000066400000000000000000000000601514270232600304170ustar00rootroot00000000000000 0  @=0# .] openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5674119268925440000066400000000000000000000001401514270232600304070ustar00rootroot00000000000000`  @@ hash  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5674419757252608000066400000000000000000000001601514270232600304160ustar00rootroot00000000000000p  `  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5677588436484096000066400000000000000000000002001514270232600304250ustar00rootroot00000000000000  S@ hash  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5706562554298368000066400000000000000000000001101514270232600304140ustar00rootroot00000000000000H  -@ hash  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-5722747668791296000066400000000000000000000001701514270232600304310ustar00rootroot00000000000000x # $ # *  openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-6285128790704128000066400000000000000000000002001514270232600304010ustar00rootroot00000000000000 8# * # # openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-6470117922701312000066400000000000000000000004001514270232600303620ustar00rootroot00000000000000   openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-6502620041576448000066400000000000000000000002001514270232600303670ustar00rootroot00000000000000'' <F @8# # H (**openvswitch-3.7.0~git20260211.8c6ebf8/tests/fuzz-regression/ofp_print_fuzzer-6540965472632832000066400000000000000000000000001514270232600304010ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tests/genpkts.py000077500000000000000000000061471514270232600221620ustar00rootroot00000000000000#!/usr/bin/python3 import sys import warnings try: from cryptography.utils import CryptographyDeprecationWarning warnings.filterwarnings( "ignore", category=CryptographyDeprecationWarning, message=r"(blowfish|cast5)", ) except ModuleNotFoundError: pass # flake8: noqa: E402 from scapy.all import RandMAC, RandIP, PcapWriter, RandIP6, RandShort, fuzz from scapy.all import IPv6, Dot1Q, IP, Ether, UDP, TCP, random # The number of packets generated will be size * 8. size = int(sys.argv[1]) # Traffic option is used to choose between fuzzy or simple packet type. if len(sys.argv) > 2: traffic_opt = str(sys.argv[2]) else: traffic_opt = "" for i in range(0, size): pkt = [] if traffic_opt == "fuzzy": eth = Ether(src=RandMAC(), dst=RandMAC()) vlan = Dot1Q() ipv4 = IP(src=RandIP(), dst=RandIP(), len=random.randint(0, 100)) ipv6 = IPv6(src=RandIP6(), dst=RandIP6(), plen=random.randint(0, 100)) udp = UDP(dport=RandShort(), sport=RandShort()) tcp = TCP(dport=RandShort(), sport=RandShort(), flags='S', dataofs=random.randint(0, 15)) # IPv4 packets with fuzzing pkt.append(fuzz(eth / ipv4 / udp).build().hex()) pkt.append(fuzz(eth / ipv4 / tcp).build().hex()) pkt.append(fuzz(eth / vlan / ipv4 / udp).build().hex()) pkt.append(fuzz(eth / vlan / ipv4 / tcp).build().hex()) # IPv6 packets with fuzzing pkt.append(fuzz(eth / ipv6 / udp).build().hex()) pkt.append(fuzz(eth / ipv6 / tcp).build().hex()) pkt.append(fuzz(eth / vlan / ipv6 / udp).build().hex()) pkt.append(fuzz(eth / vlan / ipv6 / tcp).build().hex()) else: mac_addr_src = "52:54:00:FF:FF:{:02X}".format(i % 0xff) mac_addr_dst = "80:FF:FF:FF:FF:{:02X}".format(i % 0xff) eth = Ether(src=mac_addr_src, dst=mac_addr_dst) vlan = Dot1Q(vlan=(i % 10)) # IPv4 address range limits to 255 and IPv6 limit to 65535 ipv4_addr_src = "192.168.150." + str((i % 255)) ipv4_addr_dst = "200.100.198." + str((i % 255)) ipv6_addr_src = "2001:0db8:85a3:0000:0000:8a2e:0370:{:04x}" \ .format(i % 0xffff) ipv6_addr_dst = "3021:ffff:85a3:ffff:0000:8a2e:0480:{:04x}" \ .format(i % 0xffff) ipv4 = IP(src=ipv4_addr_src, dst=ipv4_addr_dst) ipv6 = IPv6(src=ipv6_addr_src, dst=ipv6_addr_dst) src_port = 200 + (i % 20) dst_port = 1000 + (i % 20) udp = UDP(dport=src_port, sport=dst_port) tcp = TCP(dport=src_port, sport=dst_port, flags='S') # IPv4 packets pkt.append((eth / ipv4 / udp).build().hex()) pkt.append((eth / ipv4 / tcp).build().hex()) pkt.append((eth / vlan / ipv4 / udp).build().hex()) pkt.append((eth / vlan / ipv4 / tcp).build().hex()) # IPv6 packets pkt.append((eth / ipv6 / udp).build().hex()) pkt.append((eth / ipv6 / tcp).build().hex()) pkt.append((eth / vlan / ipv6 / udp).build().hex()) pkt.append((eth / vlan / ipv6 / tcp).build().hex()) print(' '.join(pkt)) openvswitch-3.7.0~git20260211.8c6ebf8/tests/glibc.supp000066400000000000000000000005171514270232600221160ustar00rootroot00000000000000# suppress what appear to unavoidable error reports from glibc { timer_create Memcheck:Leak fun:malloc fun:timer_create } { timer_create Memcheck:Param timer_create(evp) ... fun:set_up_timer } { aio Memcheck:Leak fun:calloc ... fun:allocate_stack ... fun:__aio_create_helper_thread } openvswitch-3.7.0~git20260211.8c6ebf8/tests/heap.at000066400000000000000000000005711514270232600213700ustar00rootroot00000000000000AT_BANNER([heap library]) m4_define([TEST_HEAP], [AT_SETUP([heap library -- m4_bpatsubst([$1], [-], [ ])]) AT_CHECK([ovstest test-heap $1]) AT_CLEANUP]) TEST_HEAP([insert-delete-same-order]) TEST_HEAP([insert-delete-reverse-order]) TEST_HEAP([insert-delete-every-order]) TEST_HEAP([insert-delete-same-order-with-dups]) TEST_HEAP([raw-insert]) TEST_HEAP([raw-delete]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/idltest.ann000066400000000000000000000005371514270232600222750ustar00rootroot00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates idltest.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "idltest_" s["idlHeader"] = "\"tests/idltest.h\"" openvswitch-3.7.0~git20260211.8c6ebf8/tests/idltest.ovsschema000066400000000000000000000106601514270232600235070ustar00rootroot00000000000000{ "name": "idltest", "version": "1.2.3", "tables": { "link1": { "columns": { "i": { "type": "integer" }, "k": { "type": { "key": { "type": "uuid", "refTable": "link1" } } }, "ka": { "type": { "key": { "type": "uuid", "refTable": "link1" }, "max": "unlimited", "min": 0 } }, "l2": { "type": { "key": { "type": "uuid", "refTable": "link2" }, "min": 0 } } }, "isRoot" : true }, "link2": { "columns": { "i": { "type": "integer" }, "l1": { "type": { "key": { "type": "uuid", "refTable": "link1" }, "min": 0 } } }, "isRoot" : true }, "indexed": { "columns": { "i": { "type": "integer" } }, "indexes": [["i"]], "isRoot" : true }, "simple": { "columns": { "b": { "type": "boolean" }, "ba": { "type": { "key": "boolean", "max": 1, "min": 0 } }, "i": { "type": "integer" }, "ia": { "type": { "key": "integer", "max": "unlimited", "min": 0 } }, "r": { "type": "real" }, "ra": { "type": { "key": "real", "max": "unlimited", "min": 0 } }, "s": { "type": "string" }, "sa": { "type": { "key": "string", "max": "unlimited", "min": 0 } }, "u": { "type": "uuid" }, "ua": { "type": { "key": "uuid", "max": "unlimited", "min": 0 } } }, "isRoot" : true }, "simple2" : { "columns" : { "name" : { "type": "string" }, "smap" : { "type": { "key" : "string", "value": "string", "min": 0, "max": "unlimited" } }, "imap": { "type" : { "key": { "type" : "integer", "minInteger" : 0, "maxInteger" : 4095 }, "value": { "type" : "string" }, "min": 0, "max": "unlimited" } } }, "isRoot" : true }, "simple3" : { "columns" : { "name" : { "type": "string" }, "uset": { "type": { "key": {"type": "uuid"}, "min": 0, "max": "unlimited" } }, "uref": { "type": { "key": {"type": "uuid", "refTable": "simple4", "refType": "strong"}, "min": 0, "max": "unlimited" } } }, "isRoot" : true }, "simple4" : { "columns" : { "name" : { "type": "string" } }, "isRoot" : false }, "simple5": { "columns" : { "name": {"type": "string"}, "irefmap": { "type": { "key": {"type": "integer"}, "value": {"type": "uuid", "refTable": "simple3"}, "min": 0, "max": "unlimited" } } }, "isRoot": true }, "simple6": { "columns" : { "name": {"type": "string"}, "weak_ref": { "type": { "key": {"type": "uuid", "refTable": "simple", "refType": "weak"}, "min": 0, "max": "unlimited" } } }, "isRoot": true }, "simple7" : { "columns" : { "name" : { "type": "string" }, "id": {"type": "string"} }, "isRoot" : true }, "singleton" : { "columns" : { "name" : { "type": "string" } }, "isRoot" : true, "maxRows" : 1 } } } openvswitch-3.7.0~git20260211.8c6ebf8/tests/idltest2.ovsschema000066400000000000000000000054631514270232600235760ustar00rootroot00000000000000{ "name": "idltest", "version": "1.2.3", "tables": { "link1": { "columns": { "i": { "type": "integer" }, "k": { "type": { "key": { "type": "uuid", "refTable": "link1" } } }, "ka": { "type": { "key": { "type": "uuid", "refTable": "link1" }, "max": "unlimited", "min": 0 } } } }, "simple": { "columns": { "b": { "type": "boolean" }, "ba": { "type": { "key": "boolean", "max": 1, "min": 0 } }, "i": { "type": "integer" }, "ia": { "type": { "key": "integer", "max": "unlimited", "min": 0 } }, "r": { "type": "real" }, "ra": { "type": { "key": "real", "max": "unlimited", "min": 0 } }, "s": { "type": "string" }, "sa": { "type": { "key": "string", "max": "unlimited", "min": 0 } }, "u": { "type": "uuid" }, "ua": { "type": { "key": "uuid", "max": "unlimited", "min": 0 } } } }, "simple2" : { "columns" : { "name" : { "type": "string" }, "smap" : { "type": { "key" : "string", "value": "string", "min": 0, "max": "unlimited" } }, "imap": { "type" : { "key": { "type" : "integer", "minInteger" : 0, "maxInteger" : 4095 }, "value": { "type" : "string" }, "min": 0, "max": "unlimited" } } } }, "simple3" : { "columns" : { "name" : { "type": "string" }, "uset": { "type": { "key": {"type": "uuid"}, "min": 0, "max": "unlimited" } }, "uref": { "type": { "key": {"type": "uuid", "refTable": "simple4", "refType": "strong"}, "min": 0, "max": "unlimited" } } } }, "simple4" : { "columns" : { "name" : { "type": "string" } } }, "simple7" : { "columns" : { "name" : { "type": "string" } } } } } openvswitch-3.7.0~git20260211.8c6ebf8/tests/json.at000066400000000000000000000325341514270232600214300ustar00rootroot00000000000000m4_define([JSON_CHECK_POSITIVE_C], [AT_SETUP([$1]) AT_KEYWORDS([json positive]) AT_CHECK([printf %s "AS_ESCAPE([$2])" > input]) AT_CAPTURE_FILE([input]) AT_CHECK([ovstest test-json $4 input], [0], [stdout], []) AT_CHECK([cat stdout], [0], [$3 ]) AT_CLEANUP]) # JSON_CHECK_POSITIVE_PY(TITLE, INPUT, OUTPUT, TEST-JSON-ARGS) # m4_define([JSON_CHECK_POSITIVE_PY], [AT_SETUP([$1]) AT_KEYWORDS([json positive Python]) AT_CHECK([printf %s "AS_ESCAPE([$2])" > input]) AT_CAPTURE_FILE([input]) AT_CHECK([$PYTHON3 $srcdir/test-json.py $4 input], [0], [stdout], []) AT_CHECK([cat stdout], [0], [$3 ]) AT_CLEANUP]) m4_define([JSON_CHECK_POSITIVE], [JSON_CHECK_POSITIVE_C([$1 - C], [$2], [$3], [$4]) JSON_CHECK_POSITIVE_PY([$1 - Python3], [$2], [$3], [$4])]) m4_define([JSON_CHECK_NEGATIVE_C], [AT_SETUP([$1]) AT_KEYWORDS([json negative]) AT_CHECK([printf %s "AS_ESCAPE([$2])" > input]) AT_CAPTURE_FILE([input]) AT_CHECK([ovstest test-json $4 input], [1], [stdout], []) AT_CHECK([[sed 's/^error: [^:]*:/error:/' < stdout]], [0], [$3 ]) AT_CLEANUP]) # JSON_CHECK_NEGATIVE_PY(TITLE, INPUT, OUTPUT, TEST-JSON-ARGS) # m4_define([JSON_CHECK_NEGATIVE_PY], [AT_SETUP([$1]) AT_KEYWORDS([json negative Python]) AT_CHECK([printf %s "AS_ESCAPE([$2])" > input]) AT_CAPTURE_FILE([input]) AT_CHECK([$PYTHON3 $srcdir/test-json.py $4 input], [1], [stdout], []) AT_CHECK([[sed 's/^error: [^:]*:/error:/' < stdout]], [0], [$3 ]) AT_CLEANUP]) m4_define([JSON_CHECK_NEGATIVE], [JSON_CHECK_NEGATIVE_C([$1 - C], [$2], [$3], [$4]) JSON_CHECK_NEGATIVE_PY([$1 - Python3], [$2], [$3], [$4])]) AT_BANNER([JSON -- arrays]) JSON_CHECK_POSITIVE([empty array], [[ [ ] ]], [[[]]]) JSON_CHECK_POSITIVE([single-element array], [[ [ 1 ] ]], [[[1]]]) JSON_CHECK_POSITIVE([2-element array], [[ [ 1, 2 ] ]], [[[1,2]]]) JSON_CHECK_POSITIVE([many-element array], [[ [ 1, 2, 3, 4, 5 ] ]], [[[1,2,3,4,5]]]) JSON_CHECK_NEGATIVE([missing comma], [[ [ 1, 2, 3 4, 5 ] ]], [error: syntax error expecting '@:>@' or ',']) JSON_CHECK_NEGATIVE([trailing comma not allowed], [[[1,2,]]], [error: syntax error expecting value]) JSON_CHECK_NEGATIVE([doubled comma not allowed], [[[1,,2]]], [error: syntax error expecting value]) AT_BANNER([JSON -- strings]) JSON_CHECK_POSITIVE([empty string], [[[ "" ]]], [[[""]]]) JSON_CHECK_POSITIVE([1-character strings], [[[ "a", "b", "c" ]]], [[["a","b","c"]]]) JSON_CHECK_POSITIVE([escape sequences], [[[ " \" \\ \/ \b \f \n \r \t" ]]], [[[" \" \\ / \b \f \n \r \t"]]]) JSON_CHECK_POSITIVE([Unicode escape sequences], [[[ " \u0022 \u005c \u002F \u0008 \u000c \u000A \u000d \u0009" ]]], [[[" \" \\ / \b \f \n \r \t"]]]) JSON_CHECK_POSITIVE_C([surrogate pairs - C], [[["\ud834\udd1e"]]], [[["𝄞"]]]) JSON_CHECK_NEGATIVE([a string by itself is not valid JSON], ["xxx"], [error: syntax error at beginning of input]) JSON_CHECK_NEGATIVE([end of line in quoted string], [[["xxx "]]], [error: U+000A must be escaped in quoted string]) JSON_CHECK_NEGATIVE([formfeed in quoted string], [[["xxx "]]], [error: U+000C must be escaped in quoted string]) JSON_CHECK_NEGATIVE([bad escape in quoted string], [[["\x12"]]], [error: bad escape \x]) JSON_CHECK_NEGATIVE([\u must be followed by 4 hex digits (1)], [[["\u1x"]]], [error: quoted string ends within \u escape]) JSON_CHECK_NEGATIVE([\u must be followed by 4 hex digits (2)], [[["\u1xyz"]]], [error: malformed \u escape]) JSON_CHECK_NEGATIVE([isolated leading surrogate not allowed], [[["\ud834xxx"]]], [error: malformed escaped surrogate pair]) JSON_CHECK_NEGATIVE([surrogatess must paired properly], [[["\ud834\u1234"]]], [error: second half of escaped surrogate pair is not trailing surrogate]) JSON_CHECK_NEGATIVE([null bytes not allowed], [[["\u0000"]]], [error: null bytes not supported in quoted strings]) dnl Check for regression against a prior bug. JSON_CHECK_POSITIVE([properly quoted backslash at end of string], [[["\\"]]], [[["\\"]]]) JSON_CHECK_NEGATIVE([stray backslash at end of string], [[["abcd\"]]], [error: unexpected end of input in quoted string]) AT_SETUP([end of input in quoted string - C]) AT_KEYWORDS([json negative]) AT_CHECK([printf '"xxx' | ovstest test-json -], [1], [error: line 0, column 4, byte 4: unexpected end of input in quoted string ]) AT_CLEANUP AT_BANNER([JSON -- objects]) JSON_CHECK_POSITIVE([empty object], [[{ }]], [[{}]]) JSON_CHECK_POSITIVE([simple object], [[{"b": 2, "a": 1, "c": 3}]], [[{"a":1,"b":2,"c":3}]]) JSON_CHECK_NEGATIVE([bad value], [[{"a": }, "b": 2]], [error: syntax error expecting value]) JSON_CHECK_NEGATIVE([missing colon], [[{"b": 2, "a" 1, "c": 3}]], [error: syntax error parsing object expecting ':']) JSON_CHECK_NEGATIVE([missing comma], [[{"b": 2 "a" 1, "c": 3}]], [error: syntax error expecting '}' or ',']) JSON_CHECK_NEGATIVE([trailing comma not allowed], [[{"b": 2, "a": 1, "c": 3, }]], [[error: syntax error parsing object expecting string]]) JSON_CHECK_NEGATIVE([doubled comma not allowed], [[{"b": 2, "a": 1,, "c": 3}]], [[error: syntax error parsing object expecting string]]) JSON_CHECK_NEGATIVE([names must be strings], [[{1: 2}]], [[error: syntax error parsing object expecting string]]) AT_BANNER([JSON -- literal names]) JSON_CHECK_POSITIVE([null], [[[ null ]]], [[[null]]]) JSON_CHECK_POSITIVE([false], [[[ false ]]], [[[false]]]) JSON_CHECK_POSITIVE([true], [[[ true ]]], [[[true]]]) JSON_CHECK_NEGATIVE([a literal by itself is not valid JSON], [null], [error: syntax error at beginning of input]) JSON_CHECK_NEGATIVE([nullify is invalid], [[[ nullify ]]], [error: invalid keyword 'nullify']) JSON_CHECK_NEGATIVE([nubs is invalid], [[[ nubs ]]], [error: invalid keyword 'nubs']) JSON_CHECK_NEGATIVE([xxx is invalid], [[[ xxx ]]], [error: invalid keyword 'xxx']) AT_BANNER([JSON -- numbers]) JSON_CHECK_POSITIVE( [integers expressed as reals], [[[1.0000000000, 2.00000000000000000000000000000000000, 2e5, 2.1234e4, 2.1230e3, 0e-10000, 0e10000]]], [[[1,2,200000,21234,2123,0,0]]]) JSON_CHECK_POSITIVE( [large integers], [[[9223372036854775807, -9223372036854775808]]], [[[9223372036854775807,-9223372036854775808]]]) JSON_CHECK_POSITIVE( [large integers expressed as reals], [[[9223372036854775807.0, -9223372036854775808.0, 92233720.36854775807e11, -9.223372036854775808e18]]], [[[9223372036854775807,-9223372036854775808,9223372036854775807,-9223372036854775808]]]) # It seems likely that the following test will fail on some system that # rounds slightly differently in arithmetic or in printf, but I'd like # to keep it this way until we run into such a system. JSON_CHECK_POSITIVE_C( [C - large integers that overflow to reals], [[[9223372036854775807000, -92233720368547758080000]]], [[[9.22337203685478e+21,-9.22337203685478e+22]]]) JSON_CHECK_POSITIVE_PY( [large integers that overflow to reals], [[[9223372036854775807000, -92233720368547758080000]]], [[[9.223372036854776e+21,-9.223372036854776e+22]]]) JSON_CHECK_POSITIVE( [negative zero], [[[-0, -0.0, 1e-9999, -1e-9999]]], [[[0,0,0,0]]]) JSON_CHECK_POSITIVE( [reals], [[[0.0, 1.0, 2.0, 3.0, 3.5, 81.250]]], [[[0,1,2,3,3.5,81.25]]]) JSON_CHECK_POSITIVE( [scientific notation], [[[1e3, 1E3, 2.5E2, 1e+3, 125e-3, 3.125e-2, 3125e-05, 1.525878906e-5]]], [[[1000,1000,250,1000,0.125,0.03125,0.03125,1.525878906e-05]]]) # It seems likely that the following test will fail on some system that # rounds slightly differently in arithmetic or in printf, but I'd like # to keep it this way until we run into such a system. JSON_CHECK_POSITIVE_C( [C - +/- DBL_MAX], [[[1.7976931348623157e+308, -1.7976931348623157e+308]]], [[[1.79769313486232e+308,-1.79769313486232e+308]]]) JSON_CHECK_POSITIVE_PY( [+/- DBL_MAX], [[[1.7976931348623157e+308, -1.7976931348623157e+308]]], [[[1.7976931348623157e+308,-1.7976931348623157e+308]]]) JSON_CHECK_POSITIVE( [negative reals], [[[-0, -1.0, -2.0, -3.0, -3.5, -8.1250]]], [[[0,-1,-2,-3,-3.5,-8.125]]]) JSON_CHECK_POSITIVE( [negative scientific notation], [[[-1e3, -1E3, -2.5E2, -1e+3, -125e-3, -3.125e-2, -3125e-05, -1.525878906e-5]]], [[[-1000,-1000,-250,-1000,-0.125,-0.03125,-0.03125,-1.525878906e-05]]]) JSON_CHECK_POSITIVE( [1e-9999 underflows to 0], [[[1e-9999]]], [[[0]]]) JSON_CHECK_NEGATIVE([a number by itself is not valid JSON], [1], [error: syntax error at beginning of input]) JSON_CHECK_NEGATIVE( [leading zeros not allowed], [[[0123]]], [error: leading zeros not allowed]) JSON_CHECK_NEGATIVE( [1e9999 is too big], [[[1e9999]]], [error: number outside valid range]) JSON_CHECK_NEGATIVE_C( [exponent bigger than INT_MAX], [[[1e9999999999999999999]]], [error: exponent outside valid range]) JSON_CHECK_NEGATIVE_C( [exponent smaller than INT_MIN], [[[1e-9999999999999999999]]], [error: exponent outside valid range]) JSON_CHECK_NEGATIVE_C( [accumulated exponent bigger than INT_MAX], [[[340282366920938463461761716499e2147483647]]], [error: exponent outside valid range]) JSON_CHECK_NEGATIVE_C( [accumulated exponent smaller than INT_MIN], [[[0.340282366920938463461761716499e-2147483648]]], [error: exponent outside valid range]) JSON_CHECK_NEGATIVE( [decimal point must be followed by digit], [[[1.]]], [error: decimal point must be followed by digit]) JSON_CHECK_NEGATIVE( [exponent must contain at least one digit (1)], [[[1e]]], [error: exponent must contain at least one digit]) JSON_CHECK_NEGATIVE( [exponent must contain at least one digit (2)], [[[1e+]]], [error: exponent must contain at least one digit]) JSON_CHECK_NEGATIVE( [exponent must contain at least one digit (3)], [[[1e-]]], [error: exponent must contain at least one digit]) AT_BANNER([JSON -- RFC 4627 examples]) JSON_CHECK_POSITIVE([RFC 4267 object example], [[{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] } }]], [[{"Image":{"Height":600,"IDs":[116,943,234,38793],"Thumbnail":{"Height":125,"Url":"http://www.example.com/image/481989943","Width":"100"},"Title":"View from 15th Floor","Width":800}}]]) JSON_CHECK_POSITIVE([RFC 4267 array example], [[[ { "precision": "zip", "Latitude": 37.7668, "Longitude": -122.3959, "Address": "", "City": "SAN FRANCISCO", "State": "CA", "Zip": "94107", "Country": "US" }, { "precision": "zip", "Latitude": 37.371991, "Longitude": -122.026020, "Address": "", "City": "SUNNYVALE", "State": "CA", "Zip": "94085", "Country": "US" } ]]], [[[{"Address":"","City":"SAN FRANCISCO","Country":"US","Latitude":37.7668,"Longitude":-122.3959,"State":"CA","Zip":"94107","precision":"zip"},{"Address":"","City":"SUNNYVALE","Country":"US","Latitude":37.371991,"Longitude":-122.02602,"State":"CA","Zip":"94085","precision":"zip"}]]]) AT_BANNER([JSON -- pathological cases]) JSON_CHECK_NEGATIVE([trailing garbage], [[[1]null]], [error: trailing garbage at end of input]) JSON_CHECK_NEGATIVE([formfeeds are not valid white space], [[[ ]]], [error: invalid character U+000c]) JSON_CHECK_NEGATIVE([';' is not a valid token], [;], [error: invalid character ';']) JSON_CHECK_NEGATIVE([arrays nesting too deep], [m4_for([i], [0], [1002], [1], [@<:@])dnl m4_for([i], [0], [1002], [1], [@:>@])], [error: input exceeds maximum nesting depth 1000]) JSON_CHECK_NEGATIVE([objects nesting too deep], [m4_for([i], [0], [1002], [1], [{"x":])dnl m4_for([i], [0], [1002], [1], [}])], [error: input exceeds maximum nesting depth 1000]) AT_SETUP([input may not be empty]) AT_KEYWORDS([json negative]) AT_CHECK([ovstest test-json /dev/null], [1], [error: line 0, column 0, byte 0: empty input stream ]) AT_CLEANUP AT_BANNER([JSON -- multiple inputs]) JSON_CHECK_POSITIVE([multiple adjacent objects], [[{}{}{}]], [[{} {} {}]], [--multiple]) JSON_CHECK_POSITIVE([multiple space-separated objects], [[{} {} {}]], [[{} {} {}]], [--multiple]) JSON_CHECK_POSITIVE([multiple objects on separate lines], [[{} {} {}]], [[{} {} {}]], [--multiple]) JSON_CHECK_POSITIVE([multiple objects and arrays], [[{}[]{}[]]], [[{} [] {} []]], [--multiple]) JSON_CHECK_NEGATIVE([garbage between multiple objects], [[{}x{}]], [[{} error: invalid keyword 'x' {}]], [--multiple]) JSON_CHECK_NEGATIVE([garbage after multiple objects], [[{}{}x]], [[{} {} error: invalid keyword 'x']], [--multiple]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/jsonrpc-py.at000066400000000000000000000024011514270232600225510ustar00rootroot00000000000000AT_BANNER([JSON-RPC - Python3]) AT_SETUP([JSON-RPC request and successful reply - Python3]) AT_KEYWORDS([python jsonrpc]) AT_CHECK([$PYTHON3 $srcdir/test-jsonrpc.py --pidfile --detach --no-chdir listen punix:socket]) on_exit 'kill `cat test-jsonrpc.py.pid`' AT_CHECK( [[$PYTHON3 $srcdir/test-jsonrpc.py request unix:socket echo '[{"a": "b", "x": null}]']], [0], [[{"error":null,"id":0,"result":[{"a":"b","x":null}]} ]]) AT_CLEANUP AT_SETUP([JSON-RPC request and error reply - Python3]) AT_KEYWORDS([python jsonrpc]) AT_CHECK([$PYTHON3 $srcdir/test-jsonrpc.py --pidfile --detach --no-chdir listen punix:socket]) on_exit 'kill `cat test-jsonrpc.py.pid`' AT_CHECK( [[$PYTHON3 $srcdir/test-jsonrpc.py request unix:socket bad-request '[]']], [0], [[{"error":{"error":"unknown method"},"id":0,"result":null} ]]) AT_CLEANUP AT_SETUP([JSON-RPC notification - Python3]) AT_KEYWORDS([python jsonrpc]) AT_CHECK([$PYTHON3 $srcdir/test-jsonrpc.py --pidfile --detach --no-chdir listen punix:socket]) on_exit 'kill `cat test-jsonrpc.py.pid`' AT_CHECK([test -e test-jsonrpc.py.pid]) AT_CHECK([[$PYTHON3 $srcdir/test-jsonrpc.py notify unix:socket shutdown '[]']]) # Wait for test-jsonrpc to die, based on its pidfile disappearing OVS_WAIT_WHILE([test -e test-jsonrpc.py.pid]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/jsonrpc.at000066400000000000000000000021221514270232600221230ustar00rootroot00000000000000AT_BANNER([JSON-RPC - C]) AT_SETUP([JSON-RPC request and successful reply]) AT_CHECK([ovstest test-jsonrpc --detach --no-chdir --pidfile listen punix:socket]) on_exit 'kill `cat test-jsonrpc.pid`' AT_CHECK( [[ovstest test-jsonrpc request unix:socket echo '[{"a": "b", "x": null}]']], [0], [[{"error":null,"id":0,"result":[{"a":"b","x":null}]} ]]) AT_CLEANUP AT_SETUP([JSON-RPC request and error reply]) AT_CHECK([ovstest test-jsonrpc --detach --no-chdir --pidfile listen punix:socket]) on_exit 'kill `cat test-jsonrpc.pid`' AT_CHECK( [[ovstest test-jsonrpc request unix:socket bad-request '[]']], [0], [[{"error":{"error":"unknown method"},"id":0,"result":null} ]]) AT_CLEANUP AT_SETUP([JSON-RPC notification]) AT_CHECK([ovstest test-jsonrpc --detach --no-chdir --pidfile listen punix:socket]) on_exit 'kill `cat test-jsonrpc.pid`' # Check that the pidfile got created. AT_CHECK([test -e test-jsonrpc.pid]) AT_CHECK([[ovstest test-jsonrpc notify unix:socket shutdown '[]']]) # Wait for test-jsonrpc to die, based on its pidfile disappearing OVS_WAIT_WHILE([test -e test-jsonrpc.pid]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/lacp.at000066400000000000000000000722271514270232600214010ustar00rootroot00000000000000AT_BANNER([lacp]) # Strips out Reciulation ID information since it may change over time. m4_define([STRIP_RECIRC_ID], [[sed ' s/Recirc-ID.*$// ' ]]) # Strips out active member mac address since it may change over time. m4_define([STRIP_ACTIVE_MEMBER_MAC], [[sed ' s/active member mac.*$// ' ]]) AT_SETUP([lacp - config]) OVS_VSWITCHD_START([\ add-port br0 p1 --\ set Port p1 lacp=active --\ set Interface p1 type=dummy ]) ovs-appctl time/stop ovs-appctl time/warp 300 100 AT_CHECK([ovs-appctl lacp/show], [0], [dnl ---- p1 ---- status: active negotiated sys_id: aa:55:aa:55:00:00 sys_priority: 65535 aggregation key: 1 lacp_time: slow member: p1: expired attached port_id: 1 port_priority: 65535 may_enable: false actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65535 actor port_id: 1 actor port_priority: 65535 actor key: 1 actor state: activity synchronized collecting distributing expired partner sys_id: 00:00:00:00:00:00 partner sys_priority: 0 partner port_id: 0 partner port_priority: 0 partner key: 0 partner state: timeout ]) AT_CHECK([ovs-appctl bond/show]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([lacp - multi port config]) OVS_VSWITCHD_START([dnl add-bond br0 bond p1 p2 --\ set Port bond lacp=active bond-mode=active-backup \ other_config:lacp-time="fast" \ other_config:lacp-system-id=11:22:33:44:55:66 \ other_config:lacp-system-priority=54321 --\ set Interface p1 type=dummy \ other_config:lacp-port-id=11 \ other_config:lacp-port-priority=111 \ other_config:lacp-aggregation-key=3333 --\ set Interface p2 type=dummy \ other_config:lacp-port-id=22 \ other_config:lacp-port-priority=222 \ other_config:lacp-aggregation-key=3333 ]) ovs-appctl time/stop ovs-appctl time/warp 300 100 AT_CHECK([ovs-appctl lacp/show], [0], [stdout]) AT_CHECK([sed -e 's/aggregation key:.*/aggregation key: /' < stdout], [0], [dnl ---- bond ---- status: active negotiated sys_id: 11:22:33:44:55:66 sys_priority: 54321 aggregation key: lacp_time: fast member: p1: expired attached port_id: 11 port_priority: 111 may_enable: false actor sys_id: 11:22:33:44:55:66 actor sys_priority: 54321 actor port_id: 11 actor port_priority: 111 actor key: 3333 actor state: activity timeout aggregation synchronized collecting distributing expired partner sys_id: 00:00:00:00:00:00 partner sys_priority: 0 partner port_id: 0 partner port_priority: 0 partner key: 0 partner state: timeout member: p2: expired attached port_id: 22 port_priority: 222 may_enable: false actor sys_id: 11:22:33:44:55:66 actor sys_priority: 54321 actor port_id: 22 actor port_priority: 222 actor key: 3333 actor state: activity timeout aggregation synchronized collecting distributing expired partner sys_id: 00:00:00:00:00:00 partner sys_priority: 0 partner port_id: 0 partner port_priority: 0 partner key: 0 partner state: timeout ]) AT_CHECK([ovs-appctl bond/show], [0], [dnl ---- bond ---- bond_mode: active-backup bond may use recirculation: no, Recirc-ID : -1 bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: active member mac: 00:00:00:00:00:00(none) member p1: disabled may_enable: false member p2: disabled may_enable: false ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([lacp - negotiation]) # Create bond0 on br0 with members p0 and p1 # and bond1 on br1 with members p2 and p3 # with p0 patched to p2 and p1 patched to p3. OVS_VSWITCHD_START( [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p0 type=patch options:peer=p2 ofport_request=1 \ other-config:lacp-aggregation-key=2 -- \ set interface p1 type=patch options:peer=p3 ofport_request=2 \ other-config:lacp-aggregation-key=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p2 type=patch options:peer=p0 ofport_request=3 \ other-config:lacp-aggregation-key=4 -- \ set interface p3 type=patch options:peer=p1 ofport_request=4 \ other-config:lacp-aggregation-key=4 --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) ovs-appctl time/stop # Wait for up to 5 (simulated) seconds, until LACP negotiation finishes. i=0 while :; do ovs-appctl lacp/show bond0 > bond0 AT_CAPTURE_FILE([bond0]) ovs-appctl lacp/show bond1 > bond1 AT_CAPTURE_FILE([bond1]) if grep negotiated bond0 && grep negotiated bond1; then if grep expired bond0 || grep expired bond1; then : else break fi fi i=`expr $i + 1` if test $i = 50; then AT_FAIL_IF([:]) fi ovs-appctl time/warp 100 done # Now check the correctly negotiated configuration. AT_CHECK( [ovs-appctl lacp/show bond0 ovs-appctl lacp/show bond1 ovs-appctl bond/show bond0 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ovs-appctl bond/show bond1 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ], [0], [stdout]) AT_CHECK([sed '/active member/d' stdout], [0], [dnl ---- bond0 ---- status: active negotiated sys_id: aa:55:aa:55:00:00 sys_priority: 65534 aggregation key: 2 lacp_time: fast member: p0: current attached port_id: 1 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 1 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 3 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing member: p1: current attached port_id: 2 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 2 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 4 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing ---- bond1 ---- status: active negotiated sys_id: aa:66:aa:66:00:00 sys_priority: 65534 aggregation key: 4 lacp_time: fast member: p2: current attached port_id: 3 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 3 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 1 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing member: p3: current attached port_id: 4 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 4 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 2 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing ---- bond0 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p0: enabled may_enable: true member p1: enabled may_enable: true ---- bond1 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p2: enabled may_enable: true member p3: enabled may_enable: true ]) AT_CHECK([grep 'active member$' stdout], [0], [dnl active member active member ]) # Redirect the patch link between p0 and p2 so that no packets get # back and forth across them anymore. Then wait 4 simulated # seconds. The LACP state should become "expired" for p0 and p2. AT_CHECK([ovs-vsctl \ -- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \ -- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1]) ovs-appctl time/warp 4100 100 AT_CHECK( [ovs-appctl lacp/show bond0 ovs-appctl lacp/show bond1 ovs-appctl bond/show bond0 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ovs-appctl bond/show bond1 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ], [0], [dnl ---- bond0 ---- status: active negotiated sys_id: aa:55:aa:55:00:00 sys_priority: 65534 aggregation key: 2 lacp_time: fast member: p0: expired attached port_id: 1 port_priority: 65535 may_enable: false actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 1 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing expired partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 3 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation collecting distributing member: p1: current attached port_id: 2 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 2 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 4 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing ---- bond1 ---- status: active negotiated sys_id: aa:66:aa:66:00:00 sys_priority: 65534 aggregation key: 4 lacp_time: fast member: p2: expired attached port_id: 3 port_priority: 65535 may_enable: false actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 3 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing expired partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 1 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation collecting distributing member: p3: current attached port_id: 4 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 4 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 2 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing ---- bond0 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p0: disabled may_enable: false member p1: enabled active member may_enable: true ---- bond1 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p2: disabled may_enable: false member p3: enabled active member may_enable: true ]) # Wait 4 more simulated seconds. The LACP state should become # "defaulted" for p0 and p2. ovs-appctl time/warp 4100 100 AT_CHECK( [ovs-appctl lacp/show bond0 ovs-appctl lacp/show bond1 ovs-appctl bond/show bond0 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ovs-appctl bond/show bond1 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ], [0], [dnl ---- bond0 ---- status: active negotiated sys_id: aa:55:aa:55:00:00 sys_priority: 65534 aggregation key: 2 lacp_time: fast member: p0: defaulted detached port_id: 1 port_priority: 65535 may_enable: false actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 1 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation defaulted partner sys_id: 00:00:00:00:00:00 partner sys_priority: 0 partner port_id: 0 partner port_priority: 0 partner key: 0 partner state: member: p1: current attached port_id: 2 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 2 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 4 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing ---- bond1 ---- status: active negotiated sys_id: aa:66:aa:66:00:00 sys_priority: 65534 aggregation key: 4 lacp_time: fast member: p2: defaulted detached port_id: 3 port_priority: 65535 may_enable: false actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 3 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation defaulted partner sys_id: 00:00:00:00:00:00 partner sys_priority: 0 partner port_id: 0 partner port_priority: 0 partner key: 0 partner state: member: p3: current attached port_id: 4 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 4 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 2 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing ---- bond0 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p0: disabled may_enable: false member p1: enabled active member may_enable: true ---- bond1 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p2: disabled may_enable: false member p3: enabled active member may_enable: true ]) # Reconnect the patch link between p0 and p2 to allow traffic between the ports. AT_CHECK([ovs-vsctl \ -- del-port null0 -- set int p2 options:peer=p0 \ -- del-port null1 -- set int p0 options:peer=p2]) # Wait for 30 more seconds (LACP_SLOW_TIME_TX) for the lacp to renegotiate ovs-appctl time/warp 30100 100 AT_CHECK( [ovs-appctl lacp/show bond0 ovs-appctl lacp/show bond1 ovs-appctl bond/show bond0 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ovs-appctl bond/show bond1 | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC ], [0], [dnl ---- bond0 ---- status: active negotiated sys_id: aa:55:aa:55:00:00 sys_priority: 65534 aggregation key: 2 lacp_time: fast member: p0: current attached port_id: 1 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 1 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 3 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing member: p1: current attached port_id: 2 port_priority: 65535 may_enable: true actor sys_id: aa:55:aa:55:00:00 actor sys_priority: 65534 actor port_id: 2 actor port_priority: 65535 actor key: 2 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:66:aa:66:00:00 partner sys_priority: 65534 partner port_id: 4 partner port_priority: 65535 partner key: 4 partner state: activity timeout aggregation synchronized collecting distributing ---- bond1 ---- status: active negotiated sys_id: aa:66:aa:66:00:00 sys_priority: 65534 aggregation key: 4 lacp_time: fast member: p2: current attached port_id: 3 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 3 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 1 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing member: p3: current attached port_id: 4 port_priority: 65535 may_enable: true actor sys_id: aa:66:aa:66:00:00 actor sys_priority: 65534 actor port_id: 4 actor port_priority: 65535 actor key: 4 actor state: activity timeout aggregation synchronized collecting distributing partner sys_id: aa:55:aa:55:00:00 partner sys_priority: 65534 partner port_id: 2 partner port_priority: 65535 partner key: 2 partner state: activity timeout aggregation synchronized collecting distributing ---- bond0 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p0: enabled may_enable: true member p1: enabled active member may_enable: true ---- bond1 ---- bond_mode: balance-tcp bond may use recirculation: yes, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: negotiated lacp_fallback_ab: false active-backup primary: member p2: enabled may_enable: true member p3: enabled active member may_enable: true ]) OVS_VSWITCHD_STOP AT_CLEANUP # test lacp liveness propagation - OF1.3. AT_SETUP([lacp - liveness propagation - OF1.3]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow13 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.3): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 # Create bond0 on br0 with members p0 and p1 # and bond1 on br1 with members p2 and p3 # with p0 patched to p2 and p1 patched to p3. AT_CHECK([ovs-vsctl add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p0 type=patch options:peer=p2 ofport_request=1 \ other-config:lacp-aggregation-key=2 -- \ set interface p1 type=patch options:peer=p3 ofport_request=2 \ other-config:lacp-aggregation-key=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p2 type=patch options:peer=p0 ofport_request=3 \ other-config:lacp-aggregation-key=4 -- \ set interface p3 type=patch options:peer=p1 ofport_request=4 \ other-config:lacp-aggregation-key=4 --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) ovs-appctl time/stop # Wait for up to 5 (simulated) seconds, until LACP negotiation finishes. i=0 while :; do ovs-appctl lacp/show bond0 > bond0 AT_CAPTURE_FILE([bond0]) ovs-appctl lacp/show bond1 > bond1 AT_CAPTURE_FILE([bond1]) if grep negotiated bond0 && grep negotiated bond1; then if grep expired bond0 || grep expired bond1; then : else break fi fi i=`expr $i + 1` if test $i = 50; then AT_FAIL_IF([:]) fi ovs-appctl time/warp 100 done check_liveness 1 LIVE # Makes LACP state "expired" for p0 and p2. AT_CHECK([ovs-vsctl \ -- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \ -- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1]) # Wait 4 more simulated seconds. The LACP state should become "defaulted" for p0 and p2. ovs-appctl time/warp 4100 100 check_liveness 3 0 # Reconnect the patch link between p0 and p2 to allow traffic between the ports. AT_CHECK([ovs-vsctl \ -- del-port null0 -- set int p2 options:peer=p0 \ -- del-port null1 -- set int p0 options:peer=p2]) # Wait for 30 more seconds (LACP_SLOW_TIME_TX) for the lacp to renegotiate ovs-appctl time/warp 30100 100 check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP # test lacp liveness propagation - OF1.4. AT_SETUP([lacp - liveness propagation - OF1.4]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow14 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.4): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0509000c0123456700000080 # Create bond0 on br0 with members p0 and p1 # and bond1 on br1 with members p2 and p3 # with p0 patched to p2 and p1 patched to p3. AT_CHECK([ovs-vsctl add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p0 type=patch options:peer=p2 ofport_request=1 \ other-config:lacp-aggregation-key=2 -- \ set interface p1 type=patch options:peer=p3 ofport_request=2 \ other-config:lacp-aggregation-key=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p2 type=patch options:peer=p0 ofport_request=3 \ other-config:lacp-aggregation-key=4 -- \ set interface p3 type=patch options:peer=p1 ofport_request=4 \ other-config:lacp-aggregation-key=4 --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) ovs-appctl time/stop # Wait for up to 5 (simulated) seconds, until LACP negotiation finishes. i=0 while :; do ovs-appctl lacp/show bond0 > bond0 AT_CAPTURE_FILE([bond0]) ovs-appctl lacp/show bond1 > bond1 AT_CAPTURE_FILE([bond1]) if grep negotiated bond0 && grep negotiated bond1; then if grep expired bond0 || grep expired bond1; then : else break fi fi i=`expr $i + 1` if test $i = 50; then AT_FAIL_IF([:]) fi ovs-appctl time/warp 100 done check_liveness 1 LIVE # Makes LACP state "expired" for p0 and p2. AT_CHECK([ovs-vsctl \ -- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \ -- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1]) # Wait 4 more simulated seconds. The LACP state should become "defaulted" for p0 and p2. ovs-appctl time/warp 4100 100 check_liveness 3 0 # Reconnect the patch link between p0 and p2 to allow traffic between the ports. AT_CHECK([ovs-vsctl \ -- del-port null0 -- set int p2 options:peer=p0 \ -- del-port null1 -- set int p0 options:peer=p2]) # Wait for 30 more seconds (LACP_SLOW_TIME_TX) for the lacp to renegotiate ovs-appctl time/warp 30100 100 check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP # test lacp liveness propagation - OF1.5. AT_SETUP([lacp - liveness propagation - OF1.5]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) check_liveness () { printf '\n\n--- check_liveness %d ---\n\n\n' $1 shift echo >>expout "OFPT_PORT_STATUS (OF1.5): MOD: 1(p0): addr: config: 0 state: $1 speed: 0 Mbps now, 0 Mbps max" AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/addr:[0-9a-fA-F:]*/addr:/' < monitor.log|grep -A3 "MOD: 1(p0)"|grep -ve --]], [0], [expout]) } : > expout ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Set miss_send_len to 128, enabling port_status messages to our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 # Create bond0 on br0 with members p0 and p1 # and bond1 on br1 with members p2 and p3 # with p0 patched to p2 and p1 patched to p3. AT_CHECK([ovs-vsctl add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p0 type=patch options:peer=p2 ofport_request=1 \ other-config:lacp-aggregation-key=2 -- \ set interface p1 type=patch options:peer=p3 ofport_request=2 \ other-config:lacp-aggregation-key=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p2 type=patch options:peer=p0 ofport_request=3 \ other-config:lacp-aggregation-key=4 -- \ set interface p3 type=patch options:peer=p1 ofport_request=4 \ other-config:lacp-aggregation-key=4 --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) ovs-appctl time/stop # Wait for up to 5 (simulated) seconds, until LACP negotiation finishes. i=0 while :; do ovs-appctl lacp/show bond0 > bond0 AT_CAPTURE_FILE([bond0]) ovs-appctl lacp/show bond1 > bond1 AT_CAPTURE_FILE([bond1]) if grep negotiated bond0 && grep negotiated bond1; then if grep expired bond0 || grep expired bond1; then : else break fi fi i=`expr $i + 1` if test $i = 50; then AT_FAIL_IF([:]) fi ovs-appctl time/warp 100 done check_liveness 1 LIVE # Makes LACP state "expired" for p0 and p2. AT_CHECK([ovs-vsctl \ -- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \ -- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1]) # Wait 4 more simulated seconds. The LACP state should become "defaulted" for p0 and p2. ovs-appctl time/warp 4100 100 check_liveness 3 0 # Reconnect the patch link between p0 and p2 to allow traffic between the ports. AT_CHECK([ovs-vsctl \ -- del-port null0 -- set int p2 options:peer=p0 \ -- del-port null1 -- set int p0 options:peer=p2]) # Wait for 30 more seconds (LACP_SLOW_TIME_TX) for the lacp to renegotiate ovs-appctl time/warp 30100 100 check_liveness 3 LIVE OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/learn.at000066400000000000000000001236421514270232600215610ustar00rootroot00000000000000AT_BANNER([learning action]) AT_SETUP([learning action - parsing and formatting]) AT_DATA([flows.txt], [[ actions=learn() actions=learn(send_flow_rem) actions=learn(delete_learned) actions=learn(send_flow_rem,delete_learned) actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], NXM_NX_REG3[3..19]=0x10011, output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10]) actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,eth_dst=eth_src,load:in_port->reg1[16..31]) actions=learn(limit=4096) actions=learn(limit=4096,result_dst=reg0[0]) ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: any chosen protocol: OpenFlow10-table_id OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1) OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,send_flow_rem) OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,delete_learned) OFPT_FLOW_MOD (xid=0x4): ADD actions=learn(table=1,send_flow_rem,delete_learned) OFPT_FLOW_MOD (xid=0x5): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],NXM_NX_REG3[3..19]=0x10011,output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10]) OFPT_FLOW_MOD (xid=0x6): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) OFPT_FLOW_MOD (xid=0x7): ADD actions=learn(table=1,limit=4096) OFPT_FLOW_MOD (xid=0x8): ADD actions=learn(table=1,limit=4096,result_dst=NXM_NX_REG0[0]) ]]) AT_CLEANUP AT_SETUP([learning action - parsing and formatting - illegal in_port_oxm]) AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(table=1, in_port_oxm=123456)']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|ofp_port|WARN|//' < stderr], [0], [[port 123456 is outside the supported range 0 through ffff or 0xffffff00 through 0xffffffff ovs-ofctl: table=1, in_port_oxm=123456: in_port_oxm value 123456 cannot be parsed as a subfield (123456: unknown field `123456') or an immediate value (123456: port value out of range for in_port_oxm) ]], [[]]) AT_CLEANUP AT_SETUP([learning action - parsing and formatting - OXM]) AT_DATA([flows.txt], [[ actions=learn(output:OXM_OF_IN_PORT[]) actions=learn(table=1, in_port=1, load:OXM_OF_IN_PORT[]->NXM_NX_REG1[], load:0xfffffffe->OXM_OF_IN_PORT[]) ]]) AT_CHECK([ovs-ofctl -O OpenFlow12 parse-flows flows.txt], [0], [[usable protocols: any chosen protocol: OXM-OpenFlow12 OFPT_FLOW_MOD (OF1.2) (xid=0x1): ADD actions=learn(table=1,output:OXM_OF_IN_PORT[]) OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[]) ]]) AT_CLEANUP AT_SETUP([learning action - examples]) AT_DATA([flows.txt], [[ # These are the examples from ofp-actions.c. actions=learn(in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]), resubmit(,1) table=1 priority=0 actions=flood ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: OXM,OpenFlow10+table_id,NXM+table_id,OpenFlow11 chosen protocol: OpenFlow10+table_id OFPT_FLOW_MOD (xid=0x1): ADD table:255 actions=learn(table=1,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) OFPT_FLOW_MOD (xid=0x2): ADD table:255 actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,hard_timeout=10,NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]),resubmit(,1) OFPT_FLOW_MOD (xid=0x4): ADD table:1 priority=0 actions=FLOOD ]]) AT_CLEANUP AT_SETUP([learning action - satisfied prerequisites]) AT_DATA([flows.txt], [[actions=learn(eth_type=0x800,load:5->NXM_OF_IP_DST[]) ip,actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[]) ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[]) ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: any chosen protocol: OpenFlow10-table_id OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[]) OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[]) OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[]) ]]) AT_CLEANUP AT_SETUP([learning action - invalid prerequisites]) AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:5->NXM_OF_IP_DST[])']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0], [[destination field ip_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]], [[]]) AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0], [[source field ip_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]]) AT_CLEANUP AT_SETUP([learning action - too-long immediate value]) dnl 129 bits is too long. AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:0x1fedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[])']], [1], [], [[ovs-ofctl: 0x1fedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[]: value does not fit into 128 bits ]]) dnl 128 bits is merely a bad prerequisite. AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:0xfedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[])']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0], [[destination field ipv6_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]], [[]]) AT_CLEANUP AT_SETUP([learning action - standard VLAN+MAC learning]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3]) # Set up flow table for VLAN+MAC learning. AT_DATA([flows.txt], [[ table=0 actions=learn(table=1, hard_timeout=60, NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1) table=1 priority=0 actions=flood ]]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Trace an ARP packet arriving on port 3, to create a MAC learning entry. flow="in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="1,2,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) # Check for the MAC learning entry. AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats --sort], [0], [dnl table=1, priority=0 actions=FLOOD table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3 ]) # Trace a packet arrival destined for the learned MAC. # (This will also learn a MAC.) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=2,sha=50:54:00:00:00:06,tha=50:54:00:00:00:05)' -generate], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3 ]) # Check for both MAC learning entries. AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats | sort], [0], [dnl table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3 table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1 table=1, priority=0 actions=FLOOD ]) # Trace a packet arrival that updates the first learned MAC entry. flow="in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="1,3,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) # Check that the MAC learning entry was updated. AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats | sort], [0], [dnl table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2 table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1 table=1, priority=0 actions=FLOOD ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that repeated uses of a "learn" action cause the dnl modified time of the learned flow to advance. Otherwise, the dnl learned flow will expire after its hard timeout even though it's dnl supposed to be refreshed. (The expiration can be hard to see since dnl it gets re-learned again the next time a packet appears, but dnl sometimes the expiration can cause temporary flooding etc.) AT_SETUP([learning action - learn refreshes hard_age]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3]) ovs-appctl time/stop # Set up flow table for MAC learning. AT_DATA([flows.txt], [[ table=0 actions=learn(table=1, hard_timeout=10, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1) table=1 priority=0 actions=flood ]]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Trace an ICMP packet arriving on port 3, to create a MAC learning entry. flow="in_port(3),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="1,2,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) # Check that the MAC learning entry appeared. AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats | sort], [0], [dnl table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3 table=1, priority=0 actions=FLOOD ]) # For 25 seconds, make sure that the MAC learning entry doesn't # disappear as long as we refresh it every second. for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) # Check that the entry is there. AT_CHECK([ovs-ofctl dump-flows br0 table=1], [0], [stdout]) AT_CHECK([ofctl_strip < stdout | sort], [0], [dnl table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3 table=1, priority=0 actions=FLOOD NXST_FLOW reply: ]) if test $i != 1; then # Check that hard_age has appeared. We need to do this separately # from the above check because ofctl_strip removes it. dump-flows # only prints hard_age when it is different from the flow's duration # (that is, the number of seconds from the time it was created), # so we only check for it after we've refreshed the flow once. AT_CHECK([grep dl_dst=50:54:00:00:00:07 stdout | grep -c hard_age], [0], [1 ]) fi done # Make sure that 15 seconds without refreshing makes the flow time out. ovs-appctl time/warp 15000 5000 sleep 1 AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats | sort], [0], [dnl table=1, priority=0 actions=FLOOD ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - TCPv4 port learning]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy -- \ add-port br0 p2 -- set Interface p2 type=dummy -- \ add-port br0 p3 -- set Interface p3 type=dummy]) # Set up flow table for TCPv4 port learning. AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp actions=learn(table=1, hard_timeout=60, eth_type=0x800, nw_proto=6, NXM_OF_IP_SRC[]=NXM_OF_IP_DST[], NXM_OF_IP_DST[]=NXM_OF_IP_SRC[], NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[]), flood']]) # Trace a TCPv4 packet arriving on port 3. flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=40000,dst=80)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="1,2,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) # Check for the learning entry. AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats | sort], [0], [dnl table=1, hard_timeout=60, tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - TCPv6 port learning]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy -- \ add-port br0 p2 -- set Interface p2 type=dummy -- \ add-port br0 p3 -- set Interface p3 type=dummy]) # Set up flow table for TCPv6 port learning. # Also add a 128-bit-wide "load" action and a 128-bit literal match to check # that they work. AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp6 actions=learn(table=1, hard_timeout=60, eth_type=0x86dd, nw_proto=6, NXM_NX_IPV6_SRC[]=NXM_NX_IPV6_DST[], ipv6_dst=2001:0db8:85a3:0000:0000:8a2e:0370:7334, NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[], load(0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[])), flood']]) # Trace a TCPv6 packet arriving on port 3. flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x86dd),ipv6(src=fec0::2,dst=fec0::1,label=0,proto=6,tclass=0,hlimit=255,frag=no),tcp(src=40000,dst=80)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="1,2,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) # Check for the learning entry. AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl table=1, hard_timeout=60, tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]] tcp6 actions=learn(table=1,hard_timeout=60,eth_type=0x86dd,nw_proto=6,NXM_NX_IPV6_SRC[[]]=NXM_NX_IPV6_DST[[]],ipv6_dst=2001:db8:85a3::8a2e:370:7334,NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],NXM_OF_TCP_DST[[]]=NXM_OF_TCP_SRC[[]],load:0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[[]]),FLOOD ]) OVS_VSWITCHD_STOP AT_CLEANUP # In this use of a learn action, the first packet in the flow creates # a new flow that changes the behavior of subsequent packets in the # flow. AT_SETUP([learning action - self-modifying flow]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-appctl time/stop # Set up flow table for TCPv4 port learning. AT_CHECK([[ovs-ofctl add-flow br0 'actions=load:3->NXM_NX_REG0[0..15],learn(table=0,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2']]) # Trace some packets arriving. The particular packets don't matter. for i in 1 2 3 4 5 6 7 8 9 10; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)' ovs-appctl time/warp 10 if [[ $i -eq 1 ]]; then sleep 1 fi done # Check for the learning entry. ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [[ n_packets=1, n_bytes=118, actions=load:0x3->NXM_NX_REG0[0..15],learn(table=0,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2 n_packets=9, n_bytes=1062, priority=65535,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:05 actions=output:3 NXST_FLOW reply: ]]) # Check that the first packet went out port 2 and the rest out port 3. AT_CHECK( [(ovs-ofctl dump-ports br0 2; ovs-ofctl dump-ports br0 3) | strip_xids], [0], [OFPST_PORT reply: 1 ports port 2: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=1, bytes=118, drop=?, errs=?, coll=? OFPST_PORT reply: 1 ports port 3: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=9, bytes=1062, drop=?, errs=?, coll=? ]) OVS_VSWITCHD_STOP AT_CLEANUP # This test is much like the previous, but adds idle timeouts and sends # two different flows to the bridge. This tests that the statistics are # attributed correctly. AT_SETUP([learning action - self-modifying flow with idle_timeout]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-appctl time/stop # Set up flow table for TCPv4 port learning. AT_CHECK([[ovs-ofctl add-flow br0 'actions=load:3->NXM_NX_REG0[0..15],learn(table=0,idle_timeout=5,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2']]) # Trace some packets arriving. The particular packets don't matter. for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)' ovs-appctl time/warp 10 if [[ $i -eq 1 ]]; then sleep 1 fi done # Trace some packets arriving. This is is a different flow from the previous. # Note that we advance time by 1 second between each packet here. for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)' ovs-appctl time/warp 1000 # Note: netdev-dummy/receive merely queues the packet. # We need to wait for other thread to process the packet # and update the flow's 'used' for the packet. # (i % 3 == 0) below is somehow arbitrary but chosen to ensure # that we update the flow's 'used' frequently enough to prevent # idle_timeout. if [[ $i -eq 1 -o $((i % 3)) -eq 0 ]]; then sleep 1 fi done # Check that the first packet of each flow went out port 2 and the rest out # port 3. AT_CHECK( [(ovs-ofctl dump-ports br0 2; ovs-ofctl dump-ports br0 3) | strip_xids], [0], [OFPST_PORT reply: 1 ports port 2: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=2, bytes=236, drop=?, errs=?, coll=? OFPST_PORT reply: 1 ports port 3: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=18, bytes=2124, drop=?, errs=?, coll=? ]) # Check for the learning entry. ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [[ n_packets=2, n_bytes=236, actions=load:0x3->NXM_NX_REG0[0..15],learn(table=0,idle_timeout=5,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2 n_packets=9, n_bytes=1062, idle_timeout=5, priority=65535,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:06 actions=output:3 NXST_FLOW reply: ]]) OVS_VSWITCHD_STOP AT_CLEANUP # This test is much like the previous, but adds hard timeouts and sends # two different flows to the bridge. This tests that the statistics are # attributed correctly. AT_SETUP([learning action - self-modifying flow with hard_timeout]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-appctl time/stop # Set up flow table for TCPv4 port learning. AT_CHECK([[ovs-ofctl add-flow br0 'actions=load:3->NXM_NX_REG0[0..15],learn(table=0,hard_timeout=10,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2']]) # Trace some packets arriving. The particular packets don't matter. for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)' if [[ $i -eq 1 ]]; then sleep 1 fi ovs-appctl time/warp 10 done # Check that the first packet of each flow went out port 2 and the rest out # port 3. AT_CHECK( [(ovs-ofctl dump-ports br0 2; ovs-ofctl dump-ports br0 3) | strip_xids], [0], [OFPST_PORT reply: 1 ports port 2: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=1, bytes=118, drop=?, errs=?, coll=? OFPST_PORT reply: 1 ports port 3: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=9, bytes=1062, drop=?, errs=?, coll=? ]) # Trace some packets arriving. This is is a different flow from the previous. # Note that we advance time by 2 second between each packet here. for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)' # Note: hard_timeout should fire immediately after #6 packet. # #7 packet re-install the flow and the following 3 packets # (#8, #9, #10) use the flow. # it's difficult to predict the exact timing of rule expiry # because it's affected by flow dumper thread via udpif_dump_seq. # hard_timeout value for this test was chosen to overcome the uncertainty. # # receive #1 learn, install flow with hard_timeout=10 # sleep to ensure the flow installation # (warp, timeout left 8s) # receive #2 the learned flow # (warp, timeout left 6s) # receive #3 # (warp, timeout left 4s) # receive #4 # (warp, timeout left 2s) # receive #5 # (warp, timeout left 0s) # NOTE: OVS does not consider this expired yet. cf. rule_expire() # receive #6 # (warp, timeout left -2s) # sleep to ensure flow expiration # receive #7 learn, install flow with hard_timeout=10 # sleep to ensure the flow installation # (warp, timeout left 8s) # receive #8 # (warp, timeout left 6s) # receive #9 # (warp, timeout left 4s) # receive #10 # (warp, timeout left 2s) if [[ $i -eq 1 -o $i -eq 7 ]]; then sleep 1 fi ovs-appctl time/warp 2000 if [[ $i -eq 6 ]]; then sleep 1 fi done # Check that the first packet of each flow went out port 2 and the rest out # port 3. AT_CHECK( [(ovs-ofctl dump-ports br0 2; ovs-ofctl dump-ports br0 3) | strip_xids], [0], [OFPST_PORT reply: 1 ports port 2: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=3, bytes=354, drop=?, errs=?, coll=? OFPST_PORT reply: 1 ports port 3: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=17, bytes=2006, drop=?, errs=?, coll=? ]) # Check for the learning entry. ovs-appctl time/warp 1000 sleep 1 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [[ n_packets=3, n_bytes=354, actions=load:0x3->NXM_NX_REG0[0..15],learn(table=0,hard_timeout=10,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2 n_packets=3, n_bytes=354, hard_timeout=10, priority=65535,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:06 actions=output:3 NXST_FLOW reply: ]]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - fin_timeout feature]) # This is a totally artificial use of the "learn" action. The only purpose # is to check that specifying fin_idle_timeout or fin_hard_timeout causes # a corresponding fin_timeout action to end up in the learned flows. OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_CHECK([[ovs-ofctl add-flow br0 'actions=learn(fin_hard_timeout=10, fin_idle_timeout=5, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[])']]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [ignore]) AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats], [0], [ table=1, dl_dst=50:54:00:00:00:05 actions=fin_timeout(idle_timeout=5,hard_timeout=10),output:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - delete_learned feature]) OVS_VSWITCHD_START # Add some initial flows and check that it was successful. AT_DATA([flows.txt], [dnl reg0=0x1 actions=learn(delete_learned,cookie=0x123) reg0=0x2 actions=learn(delete_learned,cookie=0x123) cookie=0x123, table=1, reg0=0x3 actions=drop cookie=0x123, table=1, reg0=0x4 actions=drop cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=1, reg0=0x3 actions=drop cookie=0x123, table=1, reg0=0x4 actions=drop cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123) reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x123) ]) # Delete one of the learn actions. The learned flows should stay, since there # is another learn action with the identical target. AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=1']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=1, reg0=0x3 actions=drop cookie=0x123, table=1, reg0=0x4 actions=drop cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x123) ]) # Change the flow with the learn action by adding a second action. The learned # flows should stay because the learn action is still there. AT_CHECK([ovs-ofctl mod-flows br0 'table=0 reg0=2 actions=output:1,learn(delete_learned,cookie=0x123)']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=1, reg0=0x3 actions=drop cookie=0x123, table=1, reg0=0x4 actions=drop cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop reg0=0x2 actions=output:1,learn(table=1,delete_learned,cookie=0x123) ]) # Change the flow with the learn action by replacing its learn action by one # with a different target. The (previous) learned flows disappear. AT_CHECK([ovs-ofctl mod-flows br0 'table=0 reg0=2 actions=learn(delete_learned,cookie=0x234)']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x234) ]) # Use add-flow to replace the flow with the learn action by one with the # same learn action and an extra action. The (new) learned flow remains. AT_CHECK([ovs-ofctl add-flow br0 'table=0 reg0=2 actions=learn(delete_learned,cookie=0x234),output:2']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=2, reg0=0x5 actions=drop cookie=0x234, table=1, reg0=0x6 actions=drop reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x234),output:2 ]) # Delete the flow with the learn action. The learned flow disappears too. AT_CHECK([ovs-ofctl del-flows br0 table=0]) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=2, reg0=0x5 actions=drop ]) # Add a new set of flows to check on a corner case: the learned flows # contain their own learn actions which cascade to further deletions. # This can't happen if the learned flows were actually created by a # learn action, since the learn action has very restricted action # support, but there's no restriction that the deleted flows were # created by a learn action. AT_DATA([flows.txt], [dnl reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123) reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234) cookie=0x123, table=1, reg0=0x3 actions=learn(table=3,delete_learned,cookie=0x345) cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456) cookie=0x345, table=3, reg0=0x4 actions=learn(table=5,delete_learned,cookie=0x567) cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567) cookie=0x567, table=5, reg0=0x6 actions=drop cookie=0x567, table=5, reg0=0x7 actions=drop cookie=0x567, table=5, reg0=0x8 actions=drop ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x123, table=1, reg0=0x3 actions=learn(table=3,delete_learned,cookie=0x345) cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456) cookie=0x345, table=3, reg0=0x4 actions=learn(table=5,delete_learned,cookie=0x567) cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567) cookie=0x567, table=5, reg0=0x6 actions=drop cookie=0x567, table=5, reg0=0x7 actions=drop cookie=0x567, table=5, reg0=0x8 actions=drop reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123) reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234) ]) # Deleting the flow with reg0=1 should cascade to delete a few levels # of learned flows, but the ones with cookie=0x567 stick around # because of the flow with cookie=0x456. AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=1']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456) cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567) cookie=0x567, table=5, reg0=0x6 actions=drop cookie=0x567, table=5, reg0=0x7 actions=drop cookie=0x567, table=5, reg0=0x8 actions=drop reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234) ]) # Deleting the flow with reg0=2 should cascade to delete all the rest: AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=2']) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - delete_learned/limit with packet]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 --\ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) # Add some initial flows and check that it was successful. AT_DATA([flows.txt], [dnl table=0 actions=set_field:0x2->reg7,set_field:0xabcdef01->metadata, resubmit(,1) table=1 actions=learn(table=10,delete_learned,cookie=0x123,limit=3,result_dst=NXM_NX_REG6[[0]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],OXM_OF_METADATA[[]],output:NXM_NX_REG7) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 --no-stats | sort], [0], [dnl actions=load:0x2->NXM_NX_REG7[[]],load:0xabcdef01->OXM_OF_METADATA[[]],resubmit(,1) table=1, actions=learn(table=10,delete_learned,cookie=0x123,limit=3,result_dst=NXM_NX_REG6[[0]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],OXM_OF_METADATA[[]],output:NXM_NX_REG7[[]]) ]) dnl Each packet will generate its own flow at table=10, except last one AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:02,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:03,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:04,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-ofctl dump-flows br0 table=10 --no-stats | sort], [0], [dnl cookie=0x123, table=10, metadata=0xabcdef01,dl_dst=50:54:00:00:00:01 actions=output:2 cookie=0x123, table=10, metadata=0xabcdef01,dl_dst=50:54:00:00:00:02 actions=output:2 cookie=0x123, table=10, metadata=0xabcdef01,dl_dst=50:54:00:00:00:03 actions=output:2 ]) ovs-appctl revalidator/wait AT_CHECK([ovs-ofctl del-flows br0 'table=1']) AT_CHECK([ovs-ofctl dump-flows br0 table=10 --no-stats | sort], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - limit]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=learn(table=1,dl_dst=dl_src,cookie=0x1,limit=1),2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:02,dst=50:54:00:00:00:ff),eth_type(0x1234)']) OVS_WAIT_UNTIL([ovs-ofctl dump-ports br0 2 | grep -o 'tx pkts=2' >/dev/null]) AT_CHECK([ovs-ofctl dump-flows br0 table=1 --no-stats], [0], [dnl cookie=0x1, table=1, dl_dst=50:54:00:00:00:01 actions=drop ]) dnl Delete the learned flow AT_CHECK([ovs-ofctl del-flows br0 table=1]) AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) ovs-appctl revalidator/wait AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:02,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x1234)']) OVS_WAIT_UNTIL([ovs-ofctl dump-ports br0 2 | grep -o 'tx pkts=4' >/dev/null]) AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl cookie=0x1, table=1, dl_dst=50:54:00:00:00:02 actions=drop NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - limit result_dst]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=learn(table=1,dl_dst=dl_src,cookie=0x1,limit=1,result_dst=reg0[[0]]),controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x1234)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:02,dst=50:54:00:00:00:ff),eth_type(0x1234)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 reg0=0x1,in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:ff,dl_type=0x1234 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:ff,dl_type=0x1234 ]) AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl cookie=0x1, table=1, dl_dst=50:54:00:00:00:01 actions=drop NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - different limits]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 udp,actions=learn(table=11,dl_type=0x0800,nw_proto=17,udp_src=udp_dst,limit=1,result_dst=reg0[[0]]),resubmit(,1) table=0 in_port=2 udp,actions=learn(table=12,dl_type=0x0800,nw_proto=17,udp_src=udp_dst,limit=10,result_dst=reg0[[0]]),resubmit(,1) table=0 in_port=3 udp,actions=learn(table=13,dl_type=0x0800,nw_proto=17,udp_src=udp_dst,limit=20,result_dst=reg0[[0]]),resubmit(,1) dnl dnl These flows simply counts the packets that executed a successful learn action: dnl table=1 cookie=1,reg0=1,in_port=1 actions=drop table=1 cookie=2,reg0=1,in_port=2 actions=drop table=1 cookie=3,reg0=1,in_port=3 actions=drop dnl dnl These flows simply counts the packets that didn't execute a successful learn action: dnl table=1 cookie=1,reg0=0,in_port=1 actions=drop table=1 cookie=2,reg0=0,in_port=2 actions=drop table=1 cookie=3,reg0=0,in_port=3 actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for i in `seq 1001 1030`; do ovs-appctl netdev-dummy/receive p1 "in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=$i)" ovs-appctl netdev-dummy/receive p2 "in_port(2),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=$i)" ovs-appctl netdev-dummy/receive p3 "in_port(3),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:ff),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=$i)" done dnl Check successful counters: AT_CHECK([ovs-ofctl dump-flows br0 table=1,reg0=1 | ofctl_strip | sort], [0], [dnl cookie=0x1, table=1, n_packets=1, n_bytes=106, reg0=0x1,in_port=1 actions=drop cookie=0x2, table=1, n_packets=10, n_bytes=1060, reg0=0x1,in_port=2 actions=drop cookie=0x3, table=1, n_packets=20, n_bytes=2120, reg0=0x1,in_port=3 actions=drop NXST_FLOW reply: ]) dnl Check failed counters: AT_CHECK([ovs-ofctl dump-flows br0 table=1,reg0=0 | ofctl_strip | sort], [0], [dnl cookie=0x1, table=1, n_packets=29, n_bytes=3074, reg0=0,in_port=1 actions=drop cookie=0x2, table=1, n_packets=20, n_bytes=2120, reg0=0,in_port=2 actions=drop cookie=0x3, table=1, n_packets=10, n_bytes=1060, reg0=0,in_port=3 actions=drop NXST_FLOW reply: ]) dnl Check learned flows: AT_CHECK([ovs-ofctl dump-flows br0 table=13 | ofctl_strip | sort], [0], [dnl table=13, udp,tp_src=1001 actions=drop table=13, udp,tp_src=1002 actions=drop table=13, udp,tp_src=1003 actions=drop table=13, udp,tp_src=1004 actions=drop table=13, udp,tp_src=1005 actions=drop table=13, udp,tp_src=1006 actions=drop table=13, udp,tp_src=1007 actions=drop table=13, udp,tp_src=1008 actions=drop table=13, udp,tp_src=1009 actions=drop table=13, udp,tp_src=1010 actions=drop table=13, udp,tp_src=1011 actions=drop table=13, udp,tp_src=1012 actions=drop table=13, udp,tp_src=1013 actions=drop table=13, udp,tp_src=1014 actions=drop table=13, udp,tp_src=1015 actions=drop table=13, udp,tp_src=1016 actions=drop table=13, udp,tp_src=1017 actions=drop table=13, udp,tp_src=1018 actions=drop table=13, udp,tp_src=1019 actions=drop table=13, udp,tp_src=1020 actions=drop NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl dump-flows br0 table=12 | ofctl_strip | sort], [0], [dnl table=12, udp,tp_src=1001 actions=drop table=12, udp,tp_src=1002 actions=drop table=12, udp,tp_src=1003 actions=drop table=12, udp,tp_src=1004 actions=drop table=12, udp,tp_src=1005 actions=drop table=12, udp,tp_src=1006 actions=drop table=12, udp,tp_src=1007 actions=drop table=12, udp,tp_src=1008 actions=drop table=12, udp,tp_src=1009 actions=drop table=12, udp,tp_src=1010 actions=drop NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl dump-flows br0 table=11 | ofctl_strip | sort], [0], [dnl table=11, udp,tp_src=1001 actions=drop NXST_FLOW reply: ]) AT_CHECK([ovs-vsctl del-br br0]) ovs-appctl time/warp 500 ovs-appctl time/warp 500 ovs-appctl time/warp 500 ovs-appctl time/warp 500 AT_CHECK([ovs-vsctl add-br br1 -- set b br1 datapath_type=dummy]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([learning action - flapping learn rule]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-appctl time/stop], [0], [ignore]) AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=1,actions=resubmit(,2)']]) AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=2,actions=resubmit(,2)']]) AT_CHECK([[ovs-ofctl add-flow br0 'table=2,actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:OXM_OF_IN_PORT[]),output:3']]) packet="eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)" dnl Run this test a few times in a loop to reduce the likelyhood that it passes by chance. for i in 1 2 3; do AT_CHECK([ovs-appctl revalidator/pause], [0]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/resume], [0]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:1 table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ]) AT_CHECK([ovs-appctl revalidator/pause], [0]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/resume], [0]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:2 table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ]) done dnl Wait and check for learned rule eviction due to hard timeout. AT_CHECK([ovs-appctl time/warp 3200], [0], [ignore]) AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | grep 0x123], [0], [dnl table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/learning-switch.at000066400000000000000000000020151514270232600235440ustar00rootroot00000000000000AT_BANNER([learning switch]) ### ----------------------------------------------------------------- ### learning switch OpenFlow15 test case ### ----------------------------------------------------------------- AT_SETUP([learning switch - OpenFlow15]) dnl Start ovs-testcontroller AT_CHECK([ovs-testcontroller --no-chdir --detach punix:controller --pidfile -v ptcp:], [0], [ignore]) dnl Start ovs OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ set-controller br0 tcp:127.0.0.1:6653]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) AT_CHECK([kill `cat ovs-testcontroller.pid`]) OVS_WAIT_UNTIL([! test -e controller]) OVS_VSWITCHD_STOP(["/cannot find route for controller/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/lib.at000066400000000000000000000002531514270232600212160ustar00rootroot00000000000000AT_BANNER([Library -- basic]) AT_SETUP([successful linking]) AT_KEYWORDS([libopenvswitch]) AT_CAPTURE_FILE([log]) AT_CHECK( [test-lib], [0], [], [ignore]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/library.at000066400000000000000000000177351514270232600221310ustar00rootroot00000000000000AT_BANNER([library unit tests]) AT_SETUP([flow extractor]) AT_CHECK([$PYTHON3 $srcdir/flowgen.py >/dev/null]) AT_CHECK([ovstest test-flows flows pcap], [0], [checked 247 packets, 0 errors ]) AT_CLEANUP AT_SETUP([TCP/IP checksumming]) AT_CHECK([ovstest test-csum], [0], [....#....#....####................................#................................# ]) AT_CLEANUP AT_SETUP([hash functions]) AT_CHECK([ovstest test-hash]) AT_CLEANUP AT_SETUP([hash map]) AT_KEYWORDS([hmap]) AT_CHECK([ovstest test-hmap], [0], [............ ]) AT_CLEANUP AT_SETUP([hash index]) AT_KEYWORDS([hindex]) AT_CHECK([ovstest test-hindex], [0], [..................... ]) AT_CLEANUP AT_SETUP([test rcu linked lists]) AT_CHECK([ovstest test-rculist], [0], [..... ]) AT_CLEANUP AT_SETUP([cuckoo hash]) AT_KEYWORDS([cmap]) AT_CHECK([ovstest test-cmap check 1], [0], [... ]) AT_CLEANUP AT_SETUP([counting cuckoo hash]) AT_KEYWORDS([cmap]) AT_CHECK([ovstest test-ccmap check 1], [0], [... ]) AT_CLEANUP AT_SETUP([atomic operations]) AT_CHECK([ovstest test-atomic]) AT_CLEANUP AT_SETUP([test linked lists]) AT_CHECK([ovstest test-list], [0], [.... ]) AT_CLEANUP AT_SETUP([packet library]) AT_CHECK([ovstest test-packets]) AT_CLEANUP AT_SETUP([SHA-1]) AT_KEYWORDS([sha1]) AT_CHECK([ovstest test-sha1], [0], [.................... ]) AT_CLEANUP AT_SETUP([test skiplist]) AT_KEYWORDS([skiplist]) AT_CHECK([ovstest test-skiplist], [0], [skiplist insert skiplist delete skiplist find skiplist forward_to skiplist random ]) AT_CLEANUP AT_SETUP([type properties]) AT_CHECK([test-type-props]) AT_CLEANUP AT_SETUP([strtok_r bug fix]) AT_CHECK([test-strtok_r], [0], [NULL NULL ]) AT_CLEANUP AT_SETUP([byte order conversion]) AT_KEYWORDS([byte order]) AT_CHECK([ovstest test-byte-order]) AT_CLEANUP AT_SETUP([byteq - basic]) AT_KEYWORDS([byteq]) AT_CHECK([ovstest test-byteq basic], [0], [.... ]) AT_CLEANUP AT_SETUP([byteq - write_read]) AT_KEYWORDS([byteq]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_CHECK([ovstest test-byteq write_read], [0], [. ]) AT_CLEANUP AT_SETUP([random number generator]) AT_CHECK([ovstest test-random], [0], [dnl average=7fa2014f bit 0 1 0 4946 5054 1 4939 5061 2 4947 5053 3 4935 5065 4 5004 4996 5 4998 5002 6 5062 4938 7 5009 4991 8 5001 4999 9 5022 4978 10 5006 4994 11 5039 4961 12 4940 5060 13 5048 4952 14 4930 5070 15 4973 5027 16 4954 5046 17 5043 4957 18 5020 4980 19 5104 4896 20 5051 4949 21 5003 4997 22 5110 4890 23 4950 5050 24 5016 4984 25 5019 4981 26 4948 5052 27 4995 5005 28 4995 5005 29 4969 5031 30 5109 4891 31 4984 5016 (expected values are 5000) nibble 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 640 589 610 613 588 632 650 613 582 646 627 640 612 650 637 671 1 626 642 663 620 630 609 617 602 615 638 614 644 641 597 598 644 2 667 611 617 613 609 629 642 651 604 641 594 659 651 610 617 585 3 621 662 594 605 618 644 616 613 613 616 611 608 614 660 653 652 4 641 668 621 664 619 624 625 642 624 629 607 566 599 639 618 614 5 666 629 620 621 581 615 598 620 630 651 671 622 628 603 657 588 6 620 640 621 606 603 644 628 633 620 597 653 591 637 658 634 615 7 636 645 679 593 598 609 612 612 623 626 638 669 603 629 606 622 (expected values are 625) ]) AT_CLEANUP m4_foreach( [testname], [[ctz], [clz], [round_up_pow2], [round_down_pow2], [count_1bits], [log_2_floor], [bitwise_copy], [bitwise_zero], [bitwise_one], [bitwise_is_all_zeros], [bitwise_rscan], [ovs_scan]], [AT_SETUP([testname[()] function]) AT_KEYWORDS([testname]) AT_CHECK([ovstest test-util testname], [0], [], []) AT_CLEANUP]) AT_SETUP([unix socket, short pathname - C]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_CHECK([ovstest test-unix-socket x]) AT_CLEANUP dnl Unix sockets with long names are problematic because the name has to dnl go in a fixed-length field in struct sockaddr_un. Generally the limit dnl is about 100 bytes. On Linux, we work around this by indirecting through dnl a directory fd using /proc/self/fd/. We do not have a workaround dnl for other platforms, so we skip the test there. AT_SETUP([unix socket, long pathname - C]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) dnl Linux sockaddr_un has a 108-byte limit, so this needs to be longer. dnl Linux "ecryptfs" has a 143-byte limit, so we use that many bytes. longname=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 dnl Skip the test if the directory can't be created (presumably the file dnl system doesn't support such long names). AT_CHECK([mkdir $longname || exit 77]) AT_CHECK([cd $longname && ovstest test-unix-socket ../$longname/socket socket]) AT_CLEANUP AT_SETUP([unix socket, short pathname - Python3]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([python unixsocket]) AT_CHECK([$PYTHON3 $srcdir/test-unix-socket.py x]) AT_CLEANUP dnl Unix sockets with long names are problematic because the name has to dnl go in a fixed-length field in struct sockaddr_un. Generally the limit dnl is about 100 bytes. On Linux, we work around this by indirecting through dnl a directory fd using /proc/self/fd/. We do not have a workaround dnl for other platforms, so we skip the test there. AT_SETUP([unix socket, long pathname - Python3]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([python unixsocket]) dnl Linux sockaddr_un has a 108-byte limit, so this needs to be longer. dnl Linux "ecryptfs" has a 143-byte limit, so we use that many bytes. longname=01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 dnl Skip the test if the directory can't be created (presumably the file dnl system doesn't support such long names). AT_CHECK([mkdir $longname || exit 77]) AT_CHECK([cd $longname && $PYTHON3 $abs_srcdir/test-unix-socket.py ../$longname/socket socket]) AT_CLEANUP AT_SETUP([ovs_assert]) if test "$IS_WIN32" = "yes"; then exit_status=9 else # SIGABRT + 128 exit_status=134 fi AT_CHECK([ovstest test-util -voff -vfile:info '-vPATTERN:file:%c|%p|%m' --log-file assert], [$exit_status], [], [stderr]) AT_CHECK([sed 's/\(opened log file\) .*/\1/ s/|[[^|]]*: /|/ /backtrace/d /|.*|/!d' test-util.log], [0], [dnl vlog|INFO|opened log file util|EMER|assertion false failed in test_assert() ]) AT_CHECK([sed 's/.*: // 1q' stderr], [0], [assertion false failed in test_assert() ]) AT_CLEANUP AT_SETUP([saturating arithmetic]) AT_KEYWORDS([sat math sat_math]) AT_CHECK([ovstest test-util sat_math]) AT_CLEANUP AT_SETUP([snprintf]) AT_CHECK([ovstest test-util snprintf]) AT_CLEANUP AT_SETUP([bitmap functions]) AT_CHECK([ovstest test-bitmap check], [0], [.. ]) AT_CLEANUP AT_SETUP([use of public headers]) AT_CHECK([test-lib], [0], []) AT_CLEANUP AT_SETUP([ofpbuf module]) AT_CHECK([ovstest test-ofpbuf], [0], []) AT_CLEANUP AT_SETUP([barrier module]) AT_KEYWORDS([barrier]) AT_CHECK([ovstest test-barrier], [0], []) AT_CLEANUP AT_SETUP([rcu]) AT_CHECK([ovstest test-rcu], [0], []) AT_CLEANUP AT_SETUP([stopwatch module]) AT_CHECK([ovstest test-stopwatch], [0], [...... ], [ignore]) AT_CLEANUP AT_SETUP([netlink policy]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test "$IS_BSD" = "yes"]) AT_CHECK([ovstest test-netlink-policy ll_addr], [0]) AT_CLEANUP AT_SETUP([mpsc-queue module]) AT_CHECK([ovstest test-mpsc-queue check], [0], [.... ]) AT_CLEANUP AT_SETUP([id-fpool module]) AT_CHECK([ovstest test-id-fpool check], [0], []) AT_CLEANUP AT_SETUP([uuidset module]) AT_CHECK([ovstest test-uuidset], [0], [], [ignore]) AT_CLEANUP AT_SETUP([cooperative-multitasking module]) AT_CHECK([ovstest test-cooperative-multitasking], [0], []) AT_CLEANUP AT_SETUP([cooperative-multitasking module nested yield detection]) AT_CHECK([ovstest test-cooperative-multitasking-nested-yield], [0], [], [dnl cooperative_multitasking|ERR|Nested yield avoided, this is a bug! Enable debug logging for more details. ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/lockfile.at000066400000000000000000000042251514270232600222430ustar00rootroot00000000000000AT_BANNER([lockfile unit tests]) # CHECK_LOCKFILE([test-name], [number-children], [error-message] # [skip-test-windows]) m4_define([CHECK_LOCKFILE], [AT_SETUP([m4_translit([$1], [_], [ ])]) m4_if([$4], [yes], [AT_SKIP_IF([test "$IS_WIN32" = "yes"])]) AT_KEYWORDS([lockfile]) AT_CHECK([ovstest test-lockfile $1], [0], [$1: success (m4_if( [$2], [1], [$2 child], [$2 children])) ], [stderr]) AT_CHECK([sed 's/pid [[0-9]]*/pid /' stderr], [0], [$3]) AT_CLEANUP]) CHECK_LOCKFILE([lock_and_unlock], [0]) CHECK_LOCKFILE([lock_and_unlock_twice], [0]) CHECK_LOCKFILE([lock_blocks_same_process], [0], [lockfile|WARN|.file.~lock~: cannot lock file because this process has already locked it ]) CHECK_LOCKFILE([lock_blocks_same_process_twice], [0], [lockfile|WARN|.file.~lock~: cannot lock file because this process has already locked it lockfile|WARN|.file.~lock~: cannot lock file because this process has already locked it ]) CHECK_LOCKFILE([lock_blocks_other_process], [1], [lockfile|WARN|.file.~lock~: child does not inherit lock lockfile|WARN|.file.~lock~: cannot lock file because it is already locked by pid ], [yes]) CHECK_LOCKFILE([lock_twice_blocks_other_process], [1], [lockfile|WARN|.file.~lock~: cannot lock file because this process has already locked it lockfile|WARN|.file.~lock~: child does not inherit lock lockfile|WARN|.file.~lock~: cannot lock file because it is already locked by pid ], [yes]) CHECK_LOCKFILE([lock_and_unlock_allows_other_process], [1], [], [yes]) CHECK_LOCKFILE([lock_multiple], [0], [lockfile|WARN|.a.~lock~: cannot lock file because this process has already locked it ]) CHECK_LOCKFILE([lock_symlink], [0], [lockfile|WARN|.a.~lock~: cannot lock file because this process has already locked it lockfile|WARN|.b.~lock~: cannot lock file because this process has already locked it lockfile|WARN|.b.~lock~: cannot lock file because this process has already locked it lockfile|WARN|.a.~lock~: cannot lock file because this process has already locked it ], [yes]) CHECK_LOCKFILE([lock_symlink_to_dir], [0], [lockfile|WARN|dir/.b.~lock~: cannot lock file because this process has already locked it ], [yes]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/mcast-snooping.at000066400000000000000000000635021514270232600234170ustar00rootroot00000000000000AT_BANNER([mcast snooping]) AT_SETUP([mcast - check multicasts to trunk ports are not duplicated]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=true ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) # Create an access port p1 on vlan 1725, and a trunk port p2. AT_CHECK([ ovs-vsctl add-port br0 p1 tag=1725 -- set Interface p1 type=dummy \ other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/2: (dummy) ]) ovs-appctl time/stop # Send IGMPv3 query on p2 with vlan 1725. # 5c:8a:38:55:25:52 > 01:00:5e:00:00:01, ethertype 802.1Q (0x8100), length 64: vlan 1725, p 0, ethertype IPv4, # 172.17.25.1 > 224.0.0.1: igmp query v3 AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ '01005e0000015c8a38552552810006bd080046c000240000000001027f00ac111901e0000001940400001164ec1e00000000027d000000000000000000000000']) # Send IGMPv3 query on p2 with vlan 1728. # 5c:8a:38:55:25:52 > 01:00:5e:00:00:01, ethertype 802.1Q (0x8100), length 64: vlan 1728, p 0, ethertype IPv4, # 172.17.28.1 > 224.0.0.1: igmp query v3 AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ '01005e0000015c8a38552552810006c0080046c000240000000001027c00ac111c01e0000001940400001164ec1e00000000027d000000000000000000000000']) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 2 1725 UNKNOWN querier 0 2 1728 UNKNOWN querier 0 ]) AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap]) # Send a multicast packet on p1. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ 'in_port(1),eth(src=aa:55:aa:55:00:01,dst=01:00:5e:5e:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=239.94.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)' ]) # Check this packet was forwarded exactly once to p2 and has vlan tag 1725. # aa:55:aa:55:00:01 > 01:00:5e:5e:01:01, ethertype 802.1Q (0x8100), length 46: vlan 1725, p 0, ethertype IPv4, # 10.0.0.1.0 > 239.94.1.1.8000: UDP, length 0 AT_CHECK([ovs-pcap p2.pcap > p2.pcap.txt 2>&1]) AT_CHECK([cat p2.pcap.txt], [0], [dnl 01005e5e0101aa55aa550001810006bd08004500005c00000000401180310a000001ef5e010100001f40004801ba000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f ]) # Clear the mdb, send a IGMP packet with invalid checksum and make sure it # does not end up in the mdb. AT_CHECK([ovs-appctl mdb/flush br0], [0], [dnl table successfully flushed ]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ '01005e0000015c8a38552552810006bd080046c000240000000001027f00ac111901e0000001940400001164ec1000000000027d000000000000000000000000']) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age ]) # First send a valid packet to make sure it populates the mdb. Than Clear # the mdb, send a MLD packet with invalid checksum and make sure it does # not end up in the mdb. AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ '3333ff0e4c67000c290e4c6786dd600000000020000100000000000000000000000000000000ff0200000000000000000001ff0e4c673a000502000001008300e7b800000000ff0200000000000000000001ff0e4c67']) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 2 0 MLDv1 ff02::1:ff0e:4c67 0 ]) AT_CHECK([ovs-appctl mdb/flush br0], [0], [dnl table successfully flushed ]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ '3333ff0e4c67000c290e4c6786dd600000000020000100000000000000000000000000000000ff0200000000000000000001ff0e4c673a000502000001008300e7b000000000ff0200000000000000000001ff0e4c67']) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mcast - check multicast per port flooding]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=false ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 \ -- set Interface p1 type=dummy other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 \ -- set Interface p2 type=dummy other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 \ -- add-port br0 p3 \ -- set Interface p3 type=dummy other-config:hwaddr=aa:55:aa:55:00:03 ofport_request=3 \ ], [0]) ovs-appctl time/stop AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [stdout]) AT_CHECK([grep -v 'Datapath actions:' stdout], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> unregistered multicast, flooding Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no ]) AT_CHECK([sed -ne 's/^Datapath actions: \(.*\)$/\1/p' stdout | tr "," "\n" | sort -n], [0], [dnl 1 2 100 ]) # Send report packets. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 0 IGMPv1 224.1.1.1 0 ]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast group port Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 1 ]) AT_CHECK([ovs-vsctl set port p2 other_config:mcast-snooping-flood=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast group port -> forwarding to mcast flood port Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 1,2 ]) AT_CHECK([ovs-vsctl set port p3 other_config:mcast-snooping-flood=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast group port -> forwarding to mcast flood port -> mcast flood port is input port, dropping Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 1,2 ]) # Change p2 ofport to force a ofbundle change and check that the mdb contains # no stale port. AT_CHECK([ovs-vsctl set interface p2 ofport_request=4]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast group port -> mcast flood port is input port, dropping -> forwarding to mcast flood port Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 1,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mcast - check multicast per port flooding (unregistered flood disabled)]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=true ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 \ -- set Interface p1 type=dummy other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 \ -- set Interface p2 type=dummy other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 \ -- add-port br0 p3 \ -- set Interface p3 type=dummy other-config:hwaddr=aa:55:aa:55:00:03 ofport_request=3 \ ], [0]) ovs-appctl time/stop AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: drop ]) AT_CHECK([ovs-vsctl set port p2 other_config:mcast-snooping-flood=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast flood port Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 2 ]) AT_CHECK([ovs-vsctl set port p3 other_config:mcast-snooping-flood=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(3),eth(src=aa:55:aa:55:00:ff,dst=01:00:5e:01:01:01),eth_type(0x0800),ipv4(src=10.0.0.1,dst=224.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=0,dst=8000)"], [0], [dnl Flow: udp,in_port=3,vlan_tci=0x0000,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_src=10.0.0.1,nw_dst=224.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=8000 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding to mcast flood port -> mcast flood port is input port, dropping Final flow: unchanged Megaflow: recirc_id=0,eth,udp,in_port=3,dl_src=aa:55:aa:55:00:ff,dl_dst=01:00:5e:01:01:01,nw_dst=224.1.1.1,nw_frag=no Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mcast - check reports per port flooding]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=false ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 \ -- set Interface p1 type=dummy other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 \ -- set Interface p2 type=dummy other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 \ -- add-port br0 p3 \ -- set Interface p3 type=dummy other-config:hwaddr=aa:55:aa:55:00:03 ofport_request=3 \ ], [0]) ovs-appctl time/stop AT_CHECK([ovs-appctl ofproto/trace "in_port(1)" '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101'], [0], [dnl Flow: ip,in_port=1,vlan_tci=0x0000,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_src=172.16.34.30,nw_dst=224.1.1.1,nw_proto=2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=18,tp_dst=20 bridge("br0") ------------- 0. priority 32768 NORMAL -> learned that 00:0c:29:a0:27:a1 is on port p1 in VLAN 0 -> multicast snooping learned that 224.1.1.1 is on port p1 in VLAN 0 Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_proto=2,nw_frag=no Datapath actions: drop This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) AT_CHECK([ovs-vsctl set port p3 other_config:mcast-snooping-flood-reports=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(1)" '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101'], [0], [dnl Flow: ip,in_port=1,vlan_tci=0x0000,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_src=172.16.34.30,nw_dst=224.1.1.1,nw_proto=2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=18,tp_dst=20 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding report to mcast flagged port Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_proto=2,nw_frag=no Datapath actions: 3 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) AT_CHECK([ovs-vsctl set port p2 other_config:mcast-snooping-flood-reports=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(1)" '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101'], [0], [dnl Flow: ip,in_port=1,vlan_tci=0x0000,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_src=172.16.34.30,nw_dst=224.1.1.1,nw_proto=2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=18,tp_dst=20 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding report to mcast flagged port -> forwarding report to mcast flagged port Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_proto=2,nw_frag=no Datapath actions: 3,2 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) AT_CHECK([ovs-vsctl set port p1 other_config:mcast-snooping-flood-reports=true]) AT_CHECK([ovs-appctl ofproto/trace "in_port(1)" '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101'], [0], [dnl Flow: ip,in_port=1,vlan_tci=0x0000,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_src=172.16.34.30,nw_dst=224.1.1.1,nw_proto=2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=18,tp_dst=20 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding report to mcast flagged port -> forwarding report to mcast flagged port -> mcast port is input port, dropping the Report Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_proto=2,nw_frag=no Datapath actions: 3,2 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) # Change p2 ofport to force a ofbundle change and check that the mdb contains # no stale port. AT_CHECK([ovs-vsctl set interface p3 ofport_request=4]) AT_CHECK([ovs-appctl ofproto/trace "in_port(1)" '01005E010101000C29A027A108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101'], [0], [dnl Flow: ip,in_port=1,vlan_tci=0x0000,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_src=172.16.34.30,nw_dst=224.1.1.1,nw_proto=2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=18,tp_dst=20 bridge("br0") ------------- 0. priority 32768 NORMAL -> forwarding report to mcast flagged port -> mcast port is input port, dropping the Report -> forwarding report to mcast flagged port Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_src=00:0c:29:a0:27:a1,dl_dst=01:00:5e:01:01:01,nw_proto=2,nw_frag=no Datapath actions: 2,3 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mcast - delete the port mdb when vlan configuration changed]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=false ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy \ other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 \ -- set Interface p2 type=dummy other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 \ -- add-port br0 p3 \ -- set Interface p3 type=dummy other-config:hwaddr=aa:55:aa:55:00:03 ofport_request=3 ], [0]) ovs-appctl time/stop # Send report packets. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A18100000108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A28100000208004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ], [0]) # Send query packets. AT_CHECK([ ovs-appctl netdev-dummy/receive p3 \ '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000' ovs-appctl netdev-dummy/receive p3 \ '01005E010101000C29A027D28100000208004500001C000100004002CBCAAC102202E00101011114EEEB00000000' ], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 1 IGMPv1 224.1.1.1 0 1 2 IGMPv1 224.1.1.1 0 3 1 UNKNOWN querier 0 3 2 UNKNOWN querier 0 ]) AT_CHECK([ovs-vsctl set port p3 tag=2], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 1 IGMPv1 224.1.1.1 0 1 2 IGMPv1 224.1.1.1 0 ]) AT_CLEANUP AT_SETUP([mcast - delete the port mdb when port destroyed]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ other-config:mcast-snooping-disable-flood-unregistered=false ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy \ other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ -- add-port br0 p2 \ -- set Interface p2 type=dummy other-config:hwaddr=aa:55:aa:55:00:02 ofport_request=2 \ ], [0]) ovs-appctl time/stop # Send report packets. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A18100000108004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A28100000208004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ], [0]) # Send query packets. AT_CHECK([ ovs-appctl netdev-dummy/receive p2 \ '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000' ovs-appctl netdev-dummy/receive p2 \ '01005E010101000C29A027D28100000208004500001C000100004002CBCAAC102202E00101011114EEEB00000000' ], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 1 IGMPv1 224.1.1.1 0 1 2 IGMPv1 224.1.1.1 0 2 1 UNKNOWN querier 0 2 2 UNKNOWN querier 0 ]) AT_CHECK([ovs-vsctl del-port br0 p2], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 1 IGMPv1 224.1.1.1 0 1 2 IGMPv1 224.1.1.1 0 ]) AT_CLEANUP AT_SETUP([mcast - igmp flood for non-snoop enabled]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy], [0]) add_of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 action=normal]) ovs-appctl time/stop dnl Basic scenario - needs to flood for IGMP followed by unicast ICMP dnl in reverse direction AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 \ 'aa55aa5500010101000c29a008004500001c00010000400164dc0a0101010a0101020800f7ffffffffff']) AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl strip_stats | strip_used | strip_recirc | dnl sed -e 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], [0], [dnl recirc_id(),in_port(1),eth(src=aa:55:aa:55:00:01,dst=01:01:00:0c:29:a0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:100,2 recirc_id(),in_port(2),eth(src=01:01:00:0c:29:a0,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:1 ]) ovs-appctl time/warp 100000 dnl Next we should clear the flows and install a complex case AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0, arp actions=NORMAL table=0, ip,in_port=1 actions=ct(table=1,zone=64000) table=0, in_port=2 actions=output:1 table=1, ip,ct_state=+trk+inv actions=drop table=1 ip,in_port=1,icmp,ct_state=+trk+new actions=output:2 table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) table=1, in_port=1,ip,ct_state=+trk+est actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ovs-appctl time/warp 100000 dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ 'aa55aa550001aa55aa55000208004500001c00010000400164dc0a0101010a0101020800f7ffffffffff']) AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl strip_stats | strip_used | strip_recirc | dnl sed 's/pid=[[0-9]]*,// s/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], [0], [dnl recirc_id(),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct(zone=64000),recirc() recirc_id(),in_port(1),ct_state(+new-inv+trk),eth_type(0x0800),ipv4(proto=1,frag=no), packets:0, bytes:0, used:never, actions:2 recirc_id(),in_port(1),ct_state(+new-inv+trk),eth_type(0x0800),ipv4(proto=2,frag=no), packets:0, bytes:0, used:never, actions:userspace(controller(reason=1,dont_send=0,continuation=0,recirc_id=,rule_cookie=0,controller_id=0,max_len=65535)) ]) AT_CLEANUP AT_SETUP([mcast - mcast_group protocol updated in mdb]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 \ datapath_type=dummy \ mcast_snooping_enable=true \ ], [0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy \ other-config:hwaddr=aa:55:aa:55:00:01 ofport_request=1 \ ], [0]) AT_CHECK([ovs-appctl time/stop]) # Send IGMPv1 report packet. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005E010101000C29A027A18100000008004500001C000100004002CBAEAC10221EE001010112140CE9E0010101' ], [0]) # Send IGMPv2 report packet. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005e010102505400000103080046c00020000040000102f8110a000103e001010294040000160008fce0010102' ], [0]) # Send IGMPv3 report packet. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005e000016505400000003080046c00028000040000102f9f60a000003e0000016940400002200e3e10000000104000000e9360ce6' ], [0]) # Check that all the ipv4 mcast groups were updated in # the mdb with the appropriate protocol. AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 0 IGMPv1 224.1.1.1 0 1 0 IGMPv2 224.1.1.2 0 1 0 IGMPv3 233.54.12.230 0 ]) # Send IGMPv1 report packet to address 224.1.1.2 # and make sure that the protocol will be updated to # IGMPV1. AT_CHECK([ ovs-appctl netdev-dummy/receive p1 \ '01005e010102505400000103080046c00020000040000102f8110a000103e00101029404000012000cfce0010102' ], [0]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 0 IGMPv1 224.1.1.1 0 1 0 IGMPv3 233.54.12.230 0 1 0 IGMPv1 224.1.1.2 0 ]) # Flush the mdb. AT_CHECK([ovs-appctl mdb/flush br0], [0], [dnl table successfully flushed ]) # Send MLDV2 packet. AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ '333300000016d0509956ddf986dd60000000001c3a01fe80000000000000712065589886fa88ff0200000000000000000000000000168f00134d0000000104000000ff0200000000000000000001ff52f3e1']) # Send MLDV1 packet. AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ '3333ff0e4c67000c290e4c6786dd600000000020000100000000000000000000000000000000ff0200000000000000000001ff0e4c673a000502000001008300e7b800000000ff0200000000000000000001ff0e4c67']) # Check that all the ipv6 mcast groups were updated in # the mdb with the appropriate protocol. AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age 1 0 MLDv2 ff02::1:ff52:f3e1 0 1 0 MLDv1 ff02::1:ff0e:4c67 0 ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/mpls-xlate.at000066400000000000000000000436611514270232600225500ustar00rootroot00000000000000AT_BANNER([mpls-xlate]) AT_SETUP([MPLS xlate action]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 -- \ add-port br0 p1 -- set Interface p1 type=patch \ options:peer=p2 ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p2 -- set Interface p2 type=patch \ options:peer=p1]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) p1 2/none: (patch: peer=p2) br1: br1 65534/101: (dummy-internal) p2 1/none: (patch: peer=p1) ]) dnl Setup single MPLS tags. AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 group_id=1232,type=select,selection_method=hash,bucket=output:LOCAL]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 group_id=1233,type=all,bucket=output:LOCAL]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 group_id=1234,type=all,bucket=dec_ttl,output:LOCAL]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,dl_type=0x0800,action=push_mpls:0x8847,set_field:10-\>mpls_label,output:1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=20,action=pop_mpls:0x0800,output:LOCAL]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=21,action=pop_mpls:0x0800,dec_ttl,output:LOCAL]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=22,action=pop_mpls:0x0800,group:1232]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=23,action=pop_mpls:0x0800,group:1233]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=24,action=pop_mpls:0x0800,group:1234]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 dl_type=0x8847,in_port=1,mpls_label=25,action=pop_mpls:0x0800,output:2]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 in_port=1,action=output:LOCAL]) dnl The following is needed on slow systems, because the flows in the datapath dnl will be evicted before the packet can match the recirculation context ovs-appctl time/stop dnl Test MPLS push AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=7777,dst=80)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: push_mpls(label=10,tc=0,ttl=64,bos=1,eth_type=0x8847),1 ]) dnl Test MPLS pop then output (actions do not trigger reciculation) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=20,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),100 ]) dnl Test MPLS pop, dec_ttl, output (actions trigger recirculation) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=21,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x1) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(1),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(ipv4(ttl=63)),100 ]) dnl Test MPLS pop then select group output (group type triggers recirculation) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=22,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x2) ]) for d in 0 1 2 3; do pkt="in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=22,tc=0,ttl=64,bos=1)" AT_CHECK([ovs-appctl netdev-dummy/receive p0 $pkt]) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/packets.*actions:1/actions:1/' | strip_used | strip_ufid | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8847),mpls(label=22/0xfffff,tc=0/0,ttl=64/0x0,bos=1/1), packets:3, bytes:54, used:0.0s, actions:pop_mpls(eth_type=0x800),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=0.0.0.0,dst=0.0.0.0,proto=0,frag=no), actions:100 ]) dnl Test MPLS pop then all group output (bucket actions do not trigger recirculation) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=23,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),100 ]) dnl Test MPLS pop then all group output (bucket actions trigger recirculation) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=24,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x4) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(4),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(ipv4(ttl=63)),100 ]) dnl Test MPLS pop then all output to patch port AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=25,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_mpls(eth_type=0x800),recirc(0x5) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(5),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 101 ]) dnl Setup multiple MPLS tags. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,dl_type=0x0800,action=push_mpls:0x8847,set_field:10-\>mpls_label,push_mpls:0x8847,set_field:20-\>mpls_label,output:1]) # The resubmits will be executed after recirculation, which preserves the # register values. AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 cookie=0xa,table=0,dl_type=0x8847,in_port=1,mpls_label=60,action=set_field:10-\>reg0,pop_mpls:0x8847,goto_table:1]) # The pop_mpls below recirculates from within a resubmit # After recirculation the (restored) register value is moved to IP ttl. AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 cookie=0xb,table=1,dl_type=0x8847,in_port=1,mpls_label=50,action=push:NXM_NX_REG0[[0..7]],pop_mpls:0x0800,set_field:0-\>nw_ttl,pop:NXM_NX_REG1[[0..7]],move:NXM_NX_REG1[[0..7]]-\>NXM_NX_IP_TTL[[]],output:LOCAL]) dnl Double MPLS push AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: push_mpls(label=10,tc=0,ttl=64,bos=1,eth_type=0x8847),push_mpls(label=20,tc=0,ttl=64,bos=0,eth_type=0x8847),1 ]) dnl Double MPLS pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x8847),mpls(label=60,tc=0,ttl=64,bos=0,label=50,tc=0,ttl=64,bos=1)'], [0], [stdout]) AT_CHECK([tail -1 stdout | sed 's/recirc(0x[[0-9a-f]]*)/recirc(?)/'], [0], [Datapath actions: pop_mpls(eth_type=0x8847),pop_mpls(eth_type=0x800),recirc(?) ]) recirc_id=$(tail -1 stdout | sed 's/.*recirc(0x\([[0-9a-f]]*\)).*/\1/') echo "recirc_id $recirc_id" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "recirc_id($recirc_id),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(ipv4(ttl=10)),100 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([MPLS xlate action - patch-port]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 -- \ add-port br0 p1 -- set Interface p1 type=patch \ options:peer=p2 ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p2 -- set Interface p2 type=patch \ options:peer=p1 -- \ add-port br1 p3 -- set Interface p3 type=dummy ofport_request=3]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) p1 2/none: (patch: peer=p2) br1: br1 65534/101: (dummy-internal) p2 1/none: (patch: peer=p1) p3 3/3: (dummy) ]) dnl MPLS PUSH + POP. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,ip,actions=2,1,1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 in_port=1,ip,actions=dec_ttl,push_mpls:0x8847,3]) dnl MPLS push+pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=10.1.1.22,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=53295,dst=8080)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(ipv4(ttl=63)),push_mpls(label=0,tc=0,ttl=63,bos=1,eth_type=0x8847),3,pop_mpls(eth_type=0x800),set(ipv4(ttl=64)),1,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([MPLS xlate action - group bucket]) OVS_VSWITCHD_START add_of_ports br0 1 AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 'group_id=1234,type=all,bucket=push_mpls:0x8847,output:1']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,ip,actions=group:1234,output:1,output:1]) dnl MPLS push in a bucket AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=10.1.1.22,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=53295,dst=8080)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: push_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),1,pop_mpls(eth_type=0x800),1,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([Encap Decap MPLS xlate action]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=patch \ options:peer=p3 ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p3 -- set Interface p3 type=patch \ options:peer=p2 ofport_request=3 -- \ add-port br1 p4 -- set Interface p4 type=dummy ofport_request=4]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/none: (patch: peer=p3) br1: br1 65534/101: (dummy-internal) p3 3/none: (patch: peer=p2) p4 4/4: (dummy) ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=p1,actions=encap(mpls),encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 "in_port=p3,dl_type=0x8847 actions=decap(),decap(packet_type(ns=0,type=0)),output:p4"]) # Now send two real ICMP echo request packets in on port p1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:add_mpls(label=0,tc=0,ttl=64,bos=1,eth_type=0x8847),push_eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),pop_eth,pop_mpls(eth_type=0x6558),recirc(0x1) recirc_id(0x1),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 ]) AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=3a:6d:d2:09:9c:ab,dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:userspace(pid=0,slow_path(action)) recirc_id(0x2),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:4 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([Encap MPLS xlate action - max labels]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/2: (dummy) ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "in_port=p1,actions=encap(mpls),set_field:1->mpls_label,encap(mpls),set_field:2->mpls_label,encap(mpls),set_field:3->mpls_label,encap(mpls),set_field:4->mpls_label,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:p2"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP(["/ofproto_dpif_xlate|WARN|dropping packet on which an encap MPLS action can't be performed as it would have more MPLS LSEs than the 3 supported. on bridge br0 while processing mpls,in_port=1,vlan_tci=0x0000,dl_src=3a:6d:d2:09:9c:ab,dl_dst=1e:2c:e9:2a:66:9e,mpls_label=3,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=8256,mpls_lse2=4416/d"]) AT_CLEANUP AT_SETUP([Decap MPLS xlate action - max labels]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/2: (dummy) ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847 actions=decap(),decap(packet_type(ns=0,type=0)),output:p2"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 00000000000200000000000188470000204000002040000020400000204036b1ee7c010236b1ee7c010308004500005403444000400121610a0101010a0101020800efac7ce400035b2c1f6100000000500b020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 00000000000200000000000188470000204000002040000020400000204036b1ee7c010236b1ee7c010308004500005403444000400121610a0101010a0101020800efac7ce400035b2c1f6100000000500b020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637] ,[0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 |sort], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8847),mpls(label=2/0x0,tc=0/0,ttl=64/0x0,bos=0/1,label=2/0x0,tc=0/0,ttl=64/0x0,bos=0/1,label=2/0x0,tc=0/0,ttl=64/0x0,bos=0/1), packets:1, bytes:128, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP(["/ofproto_dpif_xlate|WARN|dropping packet on which an MPLS decap can't be performed as it has more MPLS LSEs than the 3 supported. on bridge br0 while processing packet_type=(1,0x8847),in_port=1,mpls_label=2,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=8256,mpls_lse2=8256/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/multipath.at000066400000000000000000001043101514270232600224560ustar00rootroot00000000000000AT_BANNER([multipath link selection]) # The test-multipath program prints a lot of output on stdout, but each of the # tests below ignores it because it will vary a bit depending on endianness and # floating point precision. test-multipath will output an error message on # stderr and return with exit code 1 if anything really goes wrong. In each # case, we list the (approximate) expected output in a comment to aid debugging # if the test does fail. AT_SETUP([modulo_n multipath link selection]) AT_CHECK([[ovstest test-multipath 'eth_src,50,modulo_n,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.66 (perfect=0.33); stddev/expected=0.0023 # 3 -> 4: disruption=0.75 (perfect=0.25); stddev/expected=0.0061 # 4 -> 5: disruption=0.80 (perfect=0.20); stddev/expected=0.0082 # 5 -> 6: disruption=0.83 (perfect=0.17); stddev/expected=0.0083 # 6 -> 7: disruption=0.86 (perfect=0.14); stddev/expected=0.0061 # 7 -> 8: disruption=0.88 (perfect=0.12); stddev/expected=0.0103 # 8 -> 9: disruption=0.89 (perfect=0.11); stddev/expected=0.0129 # 9 -> 10: disruption=0.90 (perfect=0.10); stddev/expected=0.0091 #10 -> 11: disruption=0.91 (perfect=0.09); stddev/expected=0.0114 #11 -> 12: disruption=0.91 (perfect=0.08); stddev/expected=0.0073 #12 -> 13: disruption=0.92 (perfect=0.08); stddev/expected=0.0165 #13 -> 14: disruption=0.93 (perfect=0.07); stddev/expected=0.0149 #14 -> 15: disruption=0.93 (perfect=0.07); stddev/expected=0.0127 #15 -> 16: disruption=0.94 (perfect=0.06); stddev/expected=0.0142 #16 -> 17: disruption=0.94 (perfect=0.06); stddev/expected=0.0098 #17 -> 18: disruption=0.94 (perfect=0.06); stddev/expected=0.0159 #18 -> 19: disruption=0.95 (perfect=0.05); stddev/expected=0.0121 #19 -> 20: disruption=0.95 (perfect=0.05); stddev/expected=0.0195 #20 -> 21: disruption=0.95 (perfect=0.05); stddev/expected=0.0120 #21 -> 22: disruption=0.95 (perfect=0.05); stddev/expected=0.0181 #22 -> 23: disruption=0.96 (perfect=0.04); stddev/expected=0.0222 #23 -> 24: disruption=0.96 (perfect=0.04); stddev/expected=0.0164 #24 -> 25: disruption=0.96 (perfect=0.04); stddev/expected=0.0146 #25 -> 26: disruption=0.96 (perfect=0.04); stddev/expected=0.0175 #26 -> 27: disruption=0.96 (perfect=0.04); stddev/expected=0.0231 #27 -> 28: disruption=0.96 (perfect=0.04); stddev/expected=0.0172 #28 -> 29: disruption=0.97 (perfect=0.03); stddev/expected=0.0211 #29 -> 30: disruption=0.97 (perfect=0.03); stddev/expected=0.0213 #30 -> 31: disruption=0.97 (perfect=0.03); stddev/expected=0.0253 #31 -> 32: disruption=0.97 (perfect=0.03); stddev/expected=0.0208 #32 -> 33: disruption=0.97 (perfect=0.03); stddev/expected=0.0223 #33 -> 34: disruption=0.97 (perfect=0.03); stddev/expected=0.0215 #34 -> 35: disruption=0.97 (perfect=0.03); stddev/expected=0.0201 #35 -> 36: disruption=0.97 (perfect=0.03); stddev/expected=0.0220 #36 -> 37: disruption=0.97 (perfect=0.03); stddev/expected=0.0221 #37 -> 38: disruption=0.97 (perfect=0.03); stddev/expected=0.0201 #38 -> 39: disruption=0.97 (perfect=0.03); stddev/expected=0.0215 #39 -> 40: disruption=0.97 (perfect=0.03); stddev/expected=0.0271 #40 -> 41: disruption=0.98 (perfect=0.02); stddev/expected=0.0272 #41 -> 42: disruption=0.98 (perfect=0.02); stddev/expected=0.0208 #42 -> 43: disruption=0.98 (perfect=0.02); stddev/expected=0.0226 #43 -> 44: disruption=0.98 (perfect=0.02); stddev/expected=0.0264 #44 -> 45: disruption=0.98 (perfect=0.02); stddev/expected=0.0233 #45 -> 46: disruption=0.98 (perfect=0.02); stddev/expected=0.0285 #46 -> 47: disruption=0.98 (perfect=0.02); stddev/expected=0.0246 #47 -> 48: disruption=0.98 (perfect=0.02); stddev/expected=0.0282 #48 -> 49: disruption=0.98 (perfect=0.02); stddev/expected=0.0233 #49 -> 50: disruption=0.98 (perfect=0.02); stddev/expected=0.0197 #50 -> 51: disruption=0.98 (perfect=0.02); stddev/expected=0.0317 #51 -> 52: disruption=0.98 (perfect=0.02); stddev/expected=0.0283 #52 -> 53: disruption=0.98 (perfect=0.02); stddev/expected=0.0282 #53 -> 54: disruption=0.98 (perfect=0.02); stddev/expected=0.0273 #54 -> 55: disruption=0.98 (perfect=0.02); stddev/expected=0.0283 #55 -> 56: disruption=0.98 (perfect=0.02); stddev/expected=0.0288 #56 -> 57: disruption=0.98 (perfect=0.02); stddev/expected=0.0263 #57 -> 58: disruption=0.98 (perfect=0.02); stddev/expected=0.0339 #58 -> 59: disruption=0.98 (perfect=0.02); stddev/expected=0.0262 #59 -> 60: disruption=0.98 (perfect=0.02); stddev/expected=0.0309 #60 -> 61: disruption=0.98 (perfect=0.02); stddev/expected=0.0285 #61 -> 62: disruption=0.98 (perfect=0.02); stddev/expected=0.0288 #62 -> 63: disruption=0.98 (perfect=0.02); stddev/expected=0.0298 #63 -> 64: disruption=0.98 (perfect=0.02); stddev/expected=0.0277 AT_CLEANUP AT_SETUP([hash_threshold multipath link selection]) AT_CHECK([[ovstest test-multipath 'eth_src,50,hash_threshold,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.50 (perfect=0.33); stddev/expected=0.0056 # 3 -> 4: disruption=0.50 (perfect=0.25); stddev/expected=0.0050 # 4 -> 5: disruption=0.50 (perfect=0.20); stddev/expected=0.0074 # 5 -> 6: disruption=0.50 (perfect=0.17); stddev/expected=0.0031 # 6 -> 7: disruption=0.50 (perfect=0.14); stddev/expected=0.0078 # 7 -> 8: disruption=0.50 (perfect=0.12); stddev/expected=0.0085 # 8 -> 9: disruption=0.50 (perfect=0.11); stddev/expected=0.0093 # 9 -> 10: disruption=0.50 (perfect=0.10); stddev/expected=0.0083 #10 -> 11: disruption=0.51 (perfect=0.09); stddev/expected=0.0110 #11 -> 12: disruption=0.50 (perfect=0.08); stddev/expected=0.0124 #12 -> 13: disruption=0.50 (perfect=0.08); stddev/expected=0.0143 #13 -> 14: disruption=0.50 (perfect=0.07); stddev/expected=0.0148 #14 -> 15: disruption=0.50 (perfect=0.07); stddev/expected=0.0099 #15 -> 16: disruption=0.50 (perfect=0.06); stddev/expected=0.0166 #16 -> 17: disruption=0.50 (perfect=0.06); stddev/expected=0.0099 #17 -> 18: disruption=0.50 (perfect=0.06); stddev/expected=0.0194 #18 -> 19: disruption=0.50 (perfect=0.05); stddev/expected=0.0169 #19 -> 20: disruption=0.50 (perfect=0.05); stddev/expected=0.0169 #20 -> 21: disruption=0.50 (perfect=0.05); stddev/expected=0.0185 #21 -> 22: disruption=0.50 (perfect=0.05); stddev/expected=0.0160 #22 -> 23: disruption=0.50 (perfect=0.04); stddev/expected=0.0236 #23 -> 24: disruption=0.50 (perfect=0.04); stddev/expected=0.0147 #24 -> 25: disruption=0.50 (perfect=0.04); stddev/expected=0.0195 #25 -> 26: disruption=0.50 (perfect=0.04); stddev/expected=0.0199 #26 -> 27: disruption=0.50 (perfect=0.04); stddev/expected=0.0227 #27 -> 28: disruption=0.50 (perfect=0.04); stddev/expected=0.0198 #28 -> 29: disruption=0.50 (perfect=0.03); stddev/expected=0.0216 #29 -> 30: disruption=0.50 (perfect=0.03); stddev/expected=0.0233 #30 -> 31: disruption=0.50 (perfect=0.03); stddev/expected=0.0266 #31 -> 32: disruption=0.51 (perfect=0.03); stddev/expected=0.0238 #32 -> 33: disruption=0.50 (perfect=0.03); stddev/expected=0.0194 #33 -> 34: disruption=0.50 (perfect=0.03); stddev/expected=0.0173 #34 -> 35: disruption=0.50 (perfect=0.03); stddev/expected=0.0223 #35 -> 36: disruption=0.50 (perfect=0.03); stddev/expected=0.0220 #36 -> 37: disruption=0.50 (perfect=0.03); stddev/expected=0.0237 #37 -> 38: disruption=0.50 (perfect=0.03); stddev/expected=0.0237 #38 -> 39: disruption=0.50 (perfect=0.03); stddev/expected=0.0251 #39 -> 40: disruption=0.50 (perfect=0.03); stddev/expected=0.0212 #40 -> 41: disruption=0.50 (perfect=0.02); stddev/expected=0.0267 #41 -> 42: disruption=0.50 (perfect=0.02); stddev/expected=0.0242 #42 -> 43: disruption=0.50 (perfect=0.02); stddev/expected=0.0222 #43 -> 44: disruption=0.50 (perfect=0.02); stddev/expected=0.0244 #44 -> 45: disruption=0.50 (perfect=0.02); stddev/expected=0.0231 #45 -> 46: disruption=0.50 (perfect=0.02); stddev/expected=0.0299 #46 -> 47: disruption=0.50 (perfect=0.02); stddev/expected=0.0263 #47 -> 48: disruption=0.50 (perfect=0.02); stddev/expected=0.0307 #48 -> 49: disruption=0.50 (perfect=0.02); stddev/expected=0.0253 #49 -> 50: disruption=0.50 (perfect=0.02); stddev/expected=0.0228 #50 -> 51: disruption=0.50 (perfect=0.02); stddev/expected=0.0273 #51 -> 52: disruption=0.50 (perfect=0.02); stddev/expected=0.0243 #52 -> 53: disruption=0.50 (perfect=0.02); stddev/expected=0.0268 #53 -> 54: disruption=0.50 (perfect=0.02); stddev/expected=0.0251 #54 -> 55: disruption=0.50 (perfect=0.02); stddev/expected=0.0297 #55 -> 56: disruption=0.50 (perfect=0.02); stddev/expected=0.0287 #56 -> 57: disruption=0.50 (perfect=0.02); stddev/expected=0.0299 #57 -> 58: disruption=0.50 (perfect=0.02); stddev/expected=0.0272 #58 -> 59: disruption=0.50 (perfect=0.02); stddev/expected=0.0295 #59 -> 60: disruption=0.50 (perfect=0.02); stddev/expected=0.0312 #60 -> 61: disruption=0.50 (perfect=0.02); stddev/expected=0.0361 #61 -> 62: disruption=0.50 (perfect=0.02); stddev/expected=0.0308 #62 -> 63: disruption=0.50 (perfect=0.02); stddev/expected=0.0283 #63 -> 64: disruption=0.50 (perfect=0.02); stddev/expected=0.0325 AT_CLEANUP AT_SETUP([hrw multipath link selection]) AT_CHECK([[ovstest test-multipath 'eth_src,50,hrw,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.33 (perfect=0.33); stddev/expected=0.0033 # 3 -> 4: disruption=0.25 (perfect=0.25); stddev/expected=0.0076 # 4 -> 5: disruption=0.20 (perfect=0.20); stddev/expected=0.0059 # 5 -> 6: disruption=0.17 (perfect=0.17); stddev/expected=0.0030 # 6 -> 7: disruption=0.14 (perfect=0.14); stddev/expected=0.0124 # 7 -> 8: disruption=0.13 (perfect=0.12); stddev/expected=0.0072 # 8 -> 9: disruption=0.11 (perfect=0.11); stddev/expected=0.0074 # 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0161 #10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0055 #11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0092 #12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0134 #13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0124 #14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0156 #15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0182 #16 -> 17: disruption=0.06 (perfect=0.06); stddev/expected=0.0150 #17 -> 18: disruption=0.06 (perfect=0.06); stddev/expected=0.0109 #18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0162 #19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0149 #20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0148 #21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0230 #22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0208 #23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0210 #24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0228 #25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0155 #26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0208 #27 -> 28: disruption=0.04 (perfect=0.04); stddev/expected=0.0218 #28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0193 #29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0169 #30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0163 #31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0192 #32 -> 33: disruption=0.03 (perfect=0.03); stddev/expected=0.0212 #33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0240 #34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0227 #35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0230 #36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0183 #37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0227 #38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0255 #39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0247 #40 -> 41: disruption=0.02 (perfect=0.02); stddev/expected=0.0228 #41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0247 #42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0265 #43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0250 #44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0258 #45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0196 #46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0235 #47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0314 #48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0293 #49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0241 #50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0291 #51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0304 #52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 #53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0250 #54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0290 #55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0284 #56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0272 #57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0272 #58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0304 #59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0345 #60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0251 #61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0249 #62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0285 #63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0285 AT_CLEANUP AT_SETUP([iter_hash multipath link selection]) AT_CHECK([[ovstest test-multipath 'eth_src,50,iter_hash,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.42 (perfect=0.33); stddev/expected=0.0034 # 3 -> 4: disruption=0.25 (perfect=0.25); stddev/expected=0.0082 # 4 -> 5: disruption=0.42 (perfect=0.20); stddev/expected=0.0073 # 5 -> 6: disruption=0.17 (perfect=0.17); stddev/expected=0.0040 # 6 -> 7: disruption=0.14 (perfect=0.14); stddev/expected=0.0069 # 7 -> 8: disruption=0.13 (perfect=0.12); stddev/expected=0.0131 # 8 -> 9: disruption=0.45 (perfect=0.11); stddev/expected=0.0093 # 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0127 #10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0134 #11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0101 #12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0127 #13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0115 #14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0100 #15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0111 #16 -> 17: disruption=0.47 (perfect=0.06); stddev/expected=0.0137 #17 -> 18: disruption=0.05 (perfect=0.06); stddev/expected=0.0204 #18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0082 #19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0124 #20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0203 #21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0196 #22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0183 #23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0212 #24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0176 #25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0173 #26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0159 #27 -> 28: disruption=0.03 (perfect=0.04); stddev/expected=0.0168 #28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0190 #29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0305 #30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0282 #31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0255 #32 -> 33: disruption=0.49 (perfect=0.03); stddev/expected=0.0220 #33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0188 #34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0203 #35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0207 #36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0261 #37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0226 #38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0233 #39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0161 #40 -> 41: disruption=0.03 (perfect=0.02); stddev/expected=0.0303 #41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0249 #42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0262 #43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0260 #44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0266 #45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0287 #46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0213 #47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0301 #48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0230 #49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0248 #50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0203 #51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0235 #52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0340 #53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0264 #54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0246 #56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0270 #57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0299 #58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 #59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0275 #60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0289 #61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 AT_CLEANUP AT_SETUP([modulo_n multipath symmetric_l3 link selection]) AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,modulo_n,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.66 (perfect=0.33); stddev/expected=0.0023 # 3 -> 4: disruption=0.75 (perfect=0.25); stddev/expected=0.0061 # 4 -> 5: disruption=0.80 (perfect=0.20); stddev/expected=0.0082 # 5 -> 6: disruption=0.83 (perfect=0.17); stddev/expected=0.0083 # 6 -> 7: disruption=0.86 (perfect=0.14); stddev/expected=0.0061 # 7 -> 8: disruption=0.88 (perfect=0.12); stddev/expected=0.0103 # 8 -> 9: disruption=0.89 (perfect=0.11); stddev/expected=0.0129 # 9 -> 10: disruption=0.90 (perfect=0.10); stddev/expected=0.0091 #10 -> 11: disruption=0.91 (perfect=0.09); stddev/expected=0.0114 #11 -> 12: disruption=0.91 (perfect=0.08); stddev/expected=0.0073 #12 -> 13: disruption=0.92 (perfect=0.08); stddev/expected=0.0165 #13 -> 14: disruption=0.93 (perfect=0.07); stddev/expected=0.0149 #14 -> 15: disruption=0.93 (perfect=0.07); stddev/expected=0.0127 #15 -> 16: disruption=0.94 (perfect=0.06); stddev/expected=0.0142 #16 -> 17: disruption=0.94 (perfect=0.06); stddev/expected=0.0098 #17 -> 18: disruption=0.94 (perfect=0.06); stddev/expected=0.0159 #18 -> 19: disruption=0.95 (perfect=0.05); stddev/expected=0.0121 #19 -> 20: disruption=0.95 (perfect=0.05); stddev/expected=0.0195 #20 -> 21: disruption=0.95 (perfect=0.05); stddev/expected=0.0120 #21 -> 22: disruption=0.95 (perfect=0.05); stddev/expected=0.0181 #22 -> 23: disruption=0.96 (perfect=0.04); stddev/expected=0.0222 #23 -> 24: disruption=0.96 (perfect=0.04); stddev/expected=0.0164 #24 -> 25: disruption=0.96 (perfect=0.04); stddev/expected=0.0146 #25 -> 26: disruption=0.96 (perfect=0.04); stddev/expected=0.0175 #26 -> 27: disruption=0.96 (perfect=0.04); stddev/expected=0.0231 #27 -> 28: disruption=0.96 (perfect=0.04); stddev/expected=0.0172 #28 -> 29: disruption=0.97 (perfect=0.03); stddev/expected=0.0211 #29 -> 30: disruption=0.97 (perfect=0.03); stddev/expected=0.0213 #30 -> 31: disruption=0.97 (perfect=0.03); stddev/expected=0.0253 #31 -> 32: disruption=0.97 (perfect=0.03); stddev/expected=0.0208 #32 -> 33: disruption=0.97 (perfect=0.03); stddev/expected=0.0223 #33 -> 34: disruption=0.97 (perfect=0.03); stddev/expected=0.0215 #34 -> 35: disruption=0.97 (perfect=0.03); stddev/expected=0.0201 #35 -> 36: disruption=0.97 (perfect=0.03); stddev/expected=0.0220 #36 -> 37: disruption=0.97 (perfect=0.03); stddev/expected=0.0221 #37 -> 38: disruption=0.97 (perfect=0.03); stddev/expected=0.0201 #38 -> 39: disruption=0.97 (perfect=0.03); stddev/expected=0.0215 #39 -> 40: disruption=0.97 (perfect=0.03); stddev/expected=0.0271 #40 -> 41: disruption=0.98 (perfect=0.02); stddev/expected=0.0272 #41 -> 42: disruption=0.98 (perfect=0.02); stddev/expected=0.0208 #42 -> 43: disruption=0.98 (perfect=0.02); stddev/expected=0.0226 #43 -> 44: disruption=0.98 (perfect=0.02); stddev/expected=0.0264 #44 -> 45: disruption=0.98 (perfect=0.02); stddev/expected=0.0233 #45 -> 46: disruption=0.98 (perfect=0.02); stddev/expected=0.0285 #46 -> 47: disruption=0.98 (perfect=0.02); stddev/expected=0.0246 #47 -> 48: disruption=0.98 (perfect=0.02); stddev/expected=0.0282 #48 -> 49: disruption=0.98 (perfect=0.02); stddev/expected=0.0233 #49 -> 50: disruption=0.98 (perfect=0.02); stddev/expected=0.0197 #50 -> 51: disruption=0.98 (perfect=0.02); stddev/expected=0.0317 #51 -> 52: disruption=0.98 (perfect=0.02); stddev/expected=0.0283 #52 -> 53: disruption=0.98 (perfect=0.02); stddev/expected=0.0282 #53 -> 54: disruption=0.98 (perfect=0.02); stddev/expected=0.0273 #54 -> 55: disruption=0.98 (perfect=0.02); stddev/expected=0.0283 #55 -> 56: disruption=0.98 (perfect=0.02); stddev/expected=0.0288 #56 -> 57: disruption=0.98 (perfect=0.02); stddev/expected=0.0263 #57 -> 58: disruption=0.98 (perfect=0.02); stddev/expected=0.0339 #58 -> 59: disruption=0.98 (perfect=0.02); stddev/expected=0.0262 #59 -> 60: disruption=0.98 (perfect=0.02); stddev/expected=0.0309 #60 -> 61: disruption=0.98 (perfect=0.02); stddev/expected=0.0285 #61 -> 62: disruption=0.98 (perfect=0.02); stddev/expected=0.0288 #62 -> 63: disruption=0.98 (perfect=0.02); stddev/expected=0.0298 #63 -> 64: disruption=0.98 (perfect=0.02); stddev/expected=0.0277 AT_CLEANUP AT_SETUP([hash_threshold multipath symmetric_l3 link selection]) AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,hash_threshold,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.50 (perfect=0.33); stddev/expected=0.0056 # 3 -> 4: disruption=0.50 (perfect=0.25); stddev/expected=0.0050 # 4 -> 5: disruption=0.50 (perfect=0.20); stddev/expected=0.0074 # 5 -> 6: disruption=0.50 (perfect=0.17); stddev/expected=0.0031 # 6 -> 7: disruption=0.50 (perfect=0.14); stddev/expected=0.0078 # 7 -> 8: disruption=0.50 (perfect=0.12); stddev/expected=0.0085 # 8 -> 9: disruption=0.50 (perfect=0.11); stddev/expected=0.0093 # 9 -> 10: disruption=0.50 (perfect=0.10); stddev/expected=0.0083 #10 -> 11: disruption=0.51 (perfect=0.09); stddev/expected=0.0110 #11 -> 12: disruption=0.50 (perfect=0.08); stddev/expected=0.0124 #12 -> 13: disruption=0.50 (perfect=0.08); stddev/expected=0.0143 #13 -> 14: disruption=0.50 (perfect=0.07); stddev/expected=0.0148 #14 -> 15: disruption=0.50 (perfect=0.07); stddev/expected=0.0099 #15 -> 16: disruption=0.50 (perfect=0.06); stddev/expected=0.0166 #16 -> 17: disruption=0.50 (perfect=0.06); stddev/expected=0.0099 #17 -> 18: disruption=0.50 (perfect=0.06); stddev/expected=0.0194 #18 -> 19: disruption=0.50 (perfect=0.05); stddev/expected=0.0169 #19 -> 20: disruption=0.50 (perfect=0.05); stddev/expected=0.0169 #20 -> 21: disruption=0.50 (perfect=0.05); stddev/expected=0.0185 #21 -> 22: disruption=0.50 (perfect=0.05); stddev/expected=0.0160 #22 -> 23: disruption=0.50 (perfect=0.04); stddev/expected=0.0236 #23 -> 24: disruption=0.50 (perfect=0.04); stddev/expected=0.0147 #24 -> 25: disruption=0.50 (perfect=0.04); stddev/expected=0.0195 #25 -> 26: disruption=0.50 (perfect=0.04); stddev/expected=0.0199 #26 -> 27: disruption=0.50 (perfect=0.04); stddev/expected=0.0227 #27 -> 28: disruption=0.50 (perfect=0.04); stddev/expected=0.0198 #28 -> 29: disruption=0.50 (perfect=0.03); stddev/expected=0.0216 #29 -> 30: disruption=0.50 (perfect=0.03); stddev/expected=0.0233 #30 -> 31: disruption=0.50 (perfect=0.03); stddev/expected=0.0266 #31 -> 32: disruption=0.51 (perfect=0.03); stddev/expected=0.0238 #32 -> 33: disruption=0.50 (perfect=0.03); stddev/expected=0.0194 #33 -> 34: disruption=0.50 (perfect=0.03); stddev/expected=0.0173 #34 -> 35: disruption=0.50 (perfect=0.03); stddev/expected=0.0223 #35 -> 36: disruption=0.50 (perfect=0.03); stddev/expected=0.0220 #36 -> 37: disruption=0.50 (perfect=0.03); stddev/expected=0.0237 #37 -> 38: disruption=0.50 (perfect=0.03); stddev/expected=0.0237 #38 -> 39: disruption=0.50 (perfect=0.03); stddev/expected=0.0251 #39 -> 40: disruption=0.50 (perfect=0.03); stddev/expected=0.0212 #40 -> 41: disruption=0.50 (perfect=0.02); stddev/expected=0.0267 #41 -> 42: disruption=0.50 (perfect=0.02); stddev/expected=0.0242 #42 -> 43: disruption=0.50 (perfect=0.02); stddev/expected=0.0222 #43 -> 44: disruption=0.50 (perfect=0.02); stddev/expected=0.0244 #44 -> 45: disruption=0.50 (perfect=0.02); stddev/expected=0.0231 #45 -> 46: disruption=0.50 (perfect=0.02); stddev/expected=0.0299 #46 -> 47: disruption=0.50 (perfect=0.02); stddev/expected=0.0263 #47 -> 48: disruption=0.50 (perfect=0.02); stddev/expected=0.0307 #48 -> 49: disruption=0.50 (perfect=0.02); stddev/expected=0.0253 #49 -> 50: disruption=0.50 (perfect=0.02); stddev/expected=0.0228 #50 -> 51: disruption=0.50 (perfect=0.02); stddev/expected=0.0273 #51 -> 52: disruption=0.50 (perfect=0.02); stddev/expected=0.0243 #52 -> 53: disruption=0.50 (perfect=0.02); stddev/expected=0.0268 #53 -> 54: disruption=0.50 (perfect=0.02); stddev/expected=0.0251 #54 -> 55: disruption=0.50 (perfect=0.02); stddev/expected=0.0297 #55 -> 56: disruption=0.50 (perfect=0.02); stddev/expected=0.0287 #56 -> 57: disruption=0.50 (perfect=0.02); stddev/expected=0.0299 #57 -> 58: disruption=0.50 (perfect=0.02); stddev/expected=0.0272 #58 -> 59: disruption=0.50 (perfect=0.02); stddev/expected=0.0295 #59 -> 60: disruption=0.50 (perfect=0.02); stddev/expected=0.0312 #60 -> 61: disruption=0.50 (perfect=0.02); stddev/expected=0.0361 #61 -> 62: disruption=0.50 (perfect=0.02); stddev/expected=0.0308 #62 -> 63: disruption=0.50 (perfect=0.02); stddev/expected=0.0283 #63 -> 64: disruption=0.50 (perfect=0.02); stddev/expected=0.0325 AT_CLEANUP AT_SETUP([hrw multipath symmetric_l3 link selection]) AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,hrw,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.33 (perfect=0.33); stddev/expected=0.0033 # 3 -> 4: disruption=0.25 (perfect=0.25); stddev/expected=0.0076 # 4 -> 5: disruption=0.20 (perfect=0.20); stddev/expected=0.0059 # 5 -> 6: disruption=0.17 (perfect=0.17); stddev/expected=0.0030 # 6 -> 7: disruption=0.14 (perfect=0.14); stddev/expected=0.0124 # 7 -> 8: disruption=0.13 (perfect=0.12); stddev/expected=0.0072 # 8 -> 9: disruption=0.11 (perfect=0.11); stddev/expected=0.0074 # 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0161 #10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0055 #11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0092 #12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0134 #13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0124 #14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0156 #15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0182 #16 -> 17: disruption=0.06 (perfect=0.06); stddev/expected=0.0150 #17 -> 18: disruption=0.06 (perfect=0.06); stddev/expected=0.0109 #18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0162 #19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0149 #20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0148 #21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0230 #22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0208 #23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0210 #24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0228 #25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0155 #26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0208 #27 -> 28: disruption=0.04 (perfect=0.04); stddev/expected=0.0218 #28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0193 #29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0169 #30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0163 #31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0192 #32 -> 33: disruption=0.03 (perfect=0.03); stddev/expected=0.0212 #33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0240 #34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0227 #35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0230 #36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0183 #37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0227 #38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0255 #39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0247 #40 -> 41: disruption=0.02 (perfect=0.02); stddev/expected=0.0228 #41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0247 #42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0265 #43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0250 #44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0258 #45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0196 #46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0235 #47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0314 #48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0293 #49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0241 #50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0291 #51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0304 #52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 #53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0250 #54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0290 #55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0284 #56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0272 #57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0272 #58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0304 #59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0345 #60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0251 #61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0249 #62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0285 #63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0285 AT_CLEANUP AT_SETUP([iter_hash symmetric_l3 multipath link selection]) AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,iter_hash,1,0,NXM_NX_REG0[]']], [0], [ignore]) # 1 -> 2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000 # 2 -> 3: disruption=0.42 (perfect=0.33); stddev/expected=0.0034 # 3 -> 4: disruption=0.25 (perfect=0.25); stddev/expected=0.0082 # 4 -> 5: disruption=0.42 (perfect=0.20); stddev/expected=0.0073 # 5 -> 6: disruption=0.17 (perfect=0.17); stddev/expected=0.0040 # 6 -> 7: disruption=0.14 (perfect=0.14); stddev/expected=0.0069 # 7 -> 8: disruption=0.13 (perfect=0.12); stddev/expected=0.0131 # 8 -> 9: disruption=0.45 (perfect=0.11); stddev/expected=0.0093 # 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0127 #10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0134 #11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0101 #12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0127 #13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0115 #14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0100 #15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0111 #16 -> 17: disruption=0.47 (perfect=0.06); stddev/expected=0.0137 #17 -> 18: disruption=0.05 (perfect=0.06); stddev/expected=0.0204 #18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0082 #19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0124 #20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0203 #21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0196 #22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0183 #23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0212 #24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0176 #25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0173 #26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0159 #27 -> 28: disruption=0.03 (perfect=0.04); stddev/expected=0.0168 #28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0190 #29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0305 #30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0282 #31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0255 #32 -> 33: disruption=0.49 (perfect=0.03); stddev/expected=0.0220 #33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0188 #34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0203 #35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0207 #36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0261 #37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0226 #38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0233 #39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0161 #40 -> 41: disruption=0.03 (perfect=0.02); stddev/expected=0.0303 #41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0249 #42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0262 #43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0260 #44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0266 #45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0287 #46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0213 #47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0301 #48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0230 #49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0248 #50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0203 #51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0235 #52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0340 #53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0264 #54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0246 #56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0270 #57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0299 #58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 #59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0275 #60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0289 #61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0292 #63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0307 AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/netdev-type.at000066400000000000000000000012761514270232600227220ustar00rootroot00000000000000AT_BANNER([netdev-type]) dnl Setting MAC address of netdev internal port fails AT_SETUP([bridge - set MAC address of internal port]) OVS_VSWITCHD_START # Add an internal port and make sure that it shows up in the datapath. add_of_ports br0 1 AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy) ]) # # Set MAC address of dummy device and check that it has been set AT_CHECK([ovs-vsctl set Interface p1 type=internal mac=\"aa:55:c0:ff:ee:00\"]) AT_CHECK([ovs-vsctl get Interface p1 mac_in_use], [0], [dnl "aa:55:c0:ff:ee:00" ]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/nsh.at000066400000000000000000001325211514270232600212440ustar00rootroot00000000000000AT_BANNER([network service header (NSH)]) ### ----------------------------------------------------------------- ### Simple NSH matching test case ### ----------------------------------------------------------------- AT_SETUP([nsh - match and set]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_DATA([flows.txt], [dnl table=0,in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,dnl actions=set_field:0x2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,load:0x77->nsh_c3[[8..15]],2 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl [ in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344]dnl [ actions=set_field:2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,set_field:0x7700/0xff00->nsh_c3,output:2] ]) m4_define([NSH_HEADER], [m4_join([,], [nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255], [nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00])]) m4_define([NSH_HEADER2], [m4_join([,], [nsh_flags=2,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254], [nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aa77cc,nsh_c4=0xddeeff00])]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,eth_type=0x894f,NSH_HEADER'], [0], [dnl Flow: in_port=1,vlan_tci=0x0000,dnl dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,NSH_HEADER,dnl nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no bridge("br0") ------------- 0. in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768 set_field:2->nsh_flags set_field:254->nsh_si set_field:0x44332211->nsh_c1 set_field:0x7700/0xff00->nsh_c3 output:2 Final flow: in_port=1,vlan_tci=0x0000,dnl dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,NSH_HEADER2,dnl nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,dnl nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,dnl nsh_c1=0x11223344,nsh_c3=0xbb00/0xff00 Datapath actions: set(nsh(flags=2,ttl=63,spi=0x123456,si=254,c1=0x44332211,c3=0x7700/0x0000ff00)),2 ]) dnl Check that non-masked action variant is also correct. AT_CHECK([ovs-appctl dpif/set-dp-features br0 masked_set_action false]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,eth_type=0x894f,NSH_HEADER'], [0], [dnl Flow: in_port=1,vlan_tci=0x0000,dnl dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,NSH_HEADER,dnl nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no bridge("br0") ------------- 0. in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768 set_field:2->nsh_flags set_field:254->nsh_si set_field:0x44332211->nsh_c1 set_field:0x7700/0xff00->nsh_c3 output:2 Final flow: in_port=1,vlan_tci=0x0000,dnl dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,NSH_HEADER2,dnl nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,dnl nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,dnl nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00 Datapath actions: set(nsh(flags=2,ttl=63,mdtype=1,np=3,spi=0x123456,si=254,c1=0x44332211,c2=0x55667788,c3=0x99aa77cc,c4=0xddeeff00)),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP ### ----------------------------------------------------------------- ### NSH MD1 on Ethernet encapsulation over veth link ### ----------------------------------------------------------------- AT_SETUP([nsh - md1 encap over a veth link]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 v3 -- set Interface v3 type=patch options:peer=v4 ofport_request=3 -- \ add-port br0 v4 -- set Interface v4 type=patch options:peer=v3 ofport_request=4]) AT_DATA([flows.txt], [dnl table=0,in_port=1,ip,actions=encap(nsh(md_type=1)),set_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3 table=0,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),decap(),2 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344 actions=decap(),decap(),output:2 ip,in_port=1 actions=encap(nsh(md_type=1)),set_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3 ]) # TODO: # The fields nw_proto, nw_tos, nw_ecn, nw_ttl in final flow seem unnecessary. Can they be avoided? # The match on dl_dst=66:77:88:99:aa:bb in the Megaflow is a side effect of setting the dl_dst in the pushed outer # Ethernet header. It is a consequence of using wc->masks both for tracking matched and set bits and seems hard to # avoid except by using separate masks for both purposes. AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=1,icmp,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_dst=10.10.10.10,nw_src=20.20.20.20' ], [0], [dnl Flow: icmp,in_port=1,vlan_tci=0x0000,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_src=20.20.20.20,nw_dst=10.10.10.10,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,icmp_type=0,icmp_code=0 bridge("br0") ------------- 0. ip,in_port=1, priority 32768 encap(nsh(md_type=1)) set_field:0x1234->nsh_spi set_field:0x11223344->nsh_c1 encap(ethernet) set_field:11:22:33:44:55:66->eth_dst output:3 bridge("br0") ------------- 0. in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344, priority 32768 decap() decap() Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x1) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344' ], [0], [dnl Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no bridge("br0") ------------- 0. in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344, priority 32768 decap() decap() Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344 Datapath actions: pop_eth,pop_nsh(),recirc(0x2) ]) # Now send two real ICMP echo request packets in on port p1 AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 # A packet count of 1 in the megaflow entries means the first packet was processed by # the ofproto slow path and the second successfully by the datapath flow entry. AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 ]) # Verify, that VLAN tagged packets can be encapsulated by NSH header. ovs-appctl time/warp 10000 AT_DATA([flows.txt], [dnl table=0,in_port=1,actions=push_vlan:0x8100,mod_vlan_vid:100,3 table=0,in_port=4,actions=encap(nsh),decap(),2 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=1 actions=push_vlan:0x8100,set_field:4196->vlan_vid,output:3 in_port=4 actions=encap(nsh),decap(),output:2 ]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4) recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2 ]) OVS_VSWITCHD_STOP AT_CLEANUP ### ----------------------------------------------------------------- ### NSH MD2 on Ethernet encapsulation over veth link ### ----------------------------------------------------------------- AT_SETUP([nsh - md2 encap over a veth link]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 v3 -- set Interface v3 type=patch options:peer=v4 ofport_request=3 -- \ add-port br0 v4 -- set Interface v4 type=patch options:peer=v3 ofport_request=4]) AT_DATA([flows.txt], [dnl table=0,in_port=1,ip,actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3 table=0,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234,actions=decap(),decap(),2 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234 actions=decap(),decap(),output:2 ip,in_port=1 actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3 ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=1,icmp,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_dst=10.10.10.10,nw_src=20.20.20.20' ], [0], [dnl Flow: icmp,in_port=1,vlan_tci=0x0000,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_src=20.20.20.20,nw_dst=10.10.10.10,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,icmp_type=0,icmp_code=0 bridge("br0") ------------- 0. ip,in_port=1, priority 32768 encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))) set_field:0x1234->nsh_spi encap(ethernet) set_field:11:22:33:44:55:66->eth_dst output:3 bridge("br0") ------------- 0. in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234, priority 32768 decap() decap() Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no Datapath actions: push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x1) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234' ], [0], [dnl Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no bridge("br0") ------------- 0. in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234, priority 32768 decap() decap() Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234 Datapath actions: pop_eth,pop_nsh(),recirc(0x2) ]) # Now send two real ICMP echo request packets in on port p1 AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 # A packet count of 1 in the megaflow entries means the first packet was processed by # the ofproto slow path and the second successfully by the datapath flow entry. AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 ]) OVS_VSWITCHD_STOP AT_CLEANUP ### ----------------------------------------------------------------- ### Double NSH MD1 encapsulation using groups over veth link ### ----------------------------------------------------------------- AT_SETUP([nsh - double encap over veth link using groups]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 v3 -- set Interface v3 type=patch options:peer=v4 ofport_request=3 -- \ add-port br0 v4 -- set Interface v4 type=patch options:peer=v3 ofport_request=4]) AT_DATA([flows.txt], [dnl table=0,in_port=1,ip,actions=group:100 table=0,in_port=4,packet_type=(0,0),dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788,actions=decap(),goto_table:1 table=1,packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788,actions=decap(),goto_table:2 table=2,packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344,actions=decap(),output:2 ]) AT_DATA([groups.txt], [dnl add group_id=100,type=indirect,bucket=actions=encap(nsh(md_type=1)),set_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1,group:200 add group_id=200,type=indirect,bucket=actions=encap(nsh(md_type=1)),set_field:0x5678->nsh_spi,set_field:0x55667788->nsh_c1,group:300 add group_id=300,type=indirect,bucket=actions=encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3 ]) AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-groups br0 groups.txt ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788 actions=decap(),goto_table:1 ip,in_port=1 actions=group:100 table=1, packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788 actions=decap(),goto_table:2 table=2, packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344 actions=decap(),output:2 ]) # TODO: # The fields nw_proto, nw_tos, nw_ecn, nw_ttl in final flow seem unnecessary. Can they be avoided? # The match on dl_dst=66:77:88:99:aa:bb in the Megaflow is a side effect of setting the dl_dst in the pushed outer # Ethernet header. It is a consequence of using wc->masks both for tracking matched and set bits and seems hard to # avoid except by using separate masks for both purposes. AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=1,icmp,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_dst=10.10.10.10,nw_src=20.20.20.20' ], [0], [dnl Flow: icmp,in_port=1,vlan_tci=0x0000,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:99:aa:bb,nw_src=20.20.20.20,nw_dst=10.10.10.10,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,icmp_type=0,icmp_code=0 bridge("br0") ------------- 0. ip,in_port=1, priority 32768 group:100 bucket 0 encap(nsh(md_type=1)) set_field:0x1234->nsh_spi set_field:0x11223344->nsh_c1 group:200 bucket 0 encap(nsh(md_type=1)) set_field:0x5678->nsh_spi set_field:0x55667788->nsh_c1 group:300 bucket 0 encap(ethernet) set_field:11:22:33:44:55:66->eth_dst output:3 bridge("br0") ------------- 0. in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788, priority 32768 decap() goto_table:1 1. packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x5678,nsh_c1=0x55667788, priority 32768 decap() Final flow: unchanged Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_nsh(flags=0,ttl=63,mdtype=1,np=4,spi=0x5678,si=255,c1=0x55667788,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x1) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'recirc_id=1,in_port=4,packet_type=(1,0x894f),nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344' ], [0], [dnl Flow: recirc_id=0x1,packet_type=(1,0x894f),in_port=4 bridge("br0") ------------- thaw Resuming from table 0 Restoring actions: resubmit(,2) resubmit(,2) 2. packet_type=(1,0x894f),nsh_mdtype=1,nsh_spi=0x1234,nsh_c1=0x11223344, priority 32768 decap() Final flow: recirc_id=0x1,eth,in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 Megaflow: recirc_id=0x1,packet_type=(1,0x894f),in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344 Datapath actions: pop_nsh(),recirc(0x2) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'recirc_id=2,in_port=4,ip' ], [0], [dnl Flow: recirc_id=0x2,eth,ip,in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no bridge("br0") ------------- thaw Resuming from table 0 Restoring actions: unroll_xlate(table=2, cookie=0),output:2 unroll_xlate(table=2, cookie=0) restored state: table=2, cookie=0 output:2 Final flow: unchanged Megaflow: recirc_id=0x2,eth,ip,in_port=4,nw_frag=no Datapath actions: 2 ]) # Now send two real ICMP echo request packets in on port p1 AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 # A packet count of 1 in the megaflow entries means the first packet was processed by # the ofproto slow path and the second successfully by the datapath flow entry. AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_nsh(flags=0,ttl=63,mdtype=1,np=4,spi=0x5678,si=255,c1=0x55667788,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=1,id=0x894f),eth_type(0x894f),nsh(mdtype=1,np=3,spi=0x1234,c1=0x11223344), packets:1, bytes:122, used:0.0s, actions:pop_nsh(),recirc(0x4) recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 ]) OVS_VSWITCHD_STOP AT_CLEANUP ### ----------------------------------------------------------------- ### Triangle bridge setup with VXLAN-GPE tunnels ### ----------------------------------------------------------------- ######################## # VxLAN-gpe + NSH tunneling test setup for PTAP bridge # # 192.168.10.10 192.168.10.20 192.168.10.30 # n1 n2 n3 # |ovs-n1 |ovs-n2 |ovs-n3 # +------o------+ +------o------+ +------o------+ # | br-in1 | | br-in2 | | br-in3 | # | (PTAP) | | (PTAP) | | (PTAP) | # +------o------+ +------o------+ +------o------+ # vxlan-gpe vxlan-gpe vxlan-gpe # 10.0.0.1 (10.0.0.2) (10.0.0.3) # (20.0.0.1) 20.0.0.2 (20.0.0.3) # (30.0.0.1) LOCAL (30.0.0.2) LOCAL 30.0.0.3 LOCAL # +-----------o-+ +-----------o-+ +-----------o-+ # | br-p1 | | br-p2 | | br-p3 | # +------o------+ +------o------+ +------o------+ # p1-0 | | p2-0 | p3-0 # p0-1 | | p0-2 | p0-3 # +--o------------------------o-------------------------o--+ # | br0 | # +--------------------------------------------------------+ # # VxLAN-gpe tunnel ports: # # No Bridge Name Packet-type Remote bridge & ports # ----------------------------------------------------------------------- # 1020 br-in1 vxlangpe12 ptap br-in2 2010 (ptap) # 1030 br-in1 vxlangpe13 ptap br-in3 3010 (ptap) # 2010 br-in2 vxlangpe21 ptap br-in1 1020 (ptap) # 2030 br-in2 vxlangpe13 ptap br-in3 3020 (ptap) # 3010 br-in1 vxlangpe31 ptap br-in1 1030 (ptap) # 3020 br-in1 vxlangpe32 ptap br-in2 2010 (ptap) AT_SETUP([nsh - triangle PTAP bridge setup with NSH over vxlan-gpe]) OVS_VSWITCHD_START([]) HWADDR_BRP1=aa:55:00:00:00:01 HWADDR_BRP2=aa:55:00:00:00:02 HWADDR_BRP3=aa:55:00:00:00:03 # Setup bridge infrastructure AT_CHECK([ ovs-vsctl add-br br-in1 -- \ set bridge br-in1 datapath_type=dummy fail-mode=standalone ovs-vsctl add-br br-in2 -- \ set bridge br-in2 datapath_type=dummy fail-mode=standalone ovs-vsctl add-br br-in3 -- \ set bridge br-in3 datapath_type=dummy fail-mode=standalone ovs-vsctl add-br br-p1 -- \ set bridge br-p1 datapath_type=dummy fail-mode=standalone other-config:hwaddr=$HWADDR_BRP1 ovs-vsctl add-br br-p2 -- \ set bridge br-p2 datapath_type=dummy fail-mode=standalone other-config:hwaddr=$HWADDR_BRP2 ovs-vsctl add-br br-p3 -- \ set bridge br-p3 datapath_type=dummy fail-mode=standalone other-config:hwaddr=$HWADDR_BRP3 ovs-vsctl add-port br-p1 p1-0 -- set interface p1-0 type=patch options:peer=p0-1 ofport_request=2 ovs-vsctl add-port br-p2 p2-0 -- set interface p2-0 type=patch options:peer=p0-2 ofport_request=2 ovs-vsctl add-port br-p3 p3-0 -- set interface p3-0 type=patch options:peer=p0-3 ofport_request=2 ovs-vsctl add-port br0 p0-1 -- set interface p0-1 type=patch options:peer=p1-0 ofport_request=10 ovs-vsctl add-port br0 p0-2 -- set interface p0-2 type=patch options:peer=p2-0 ofport_request=20 ovs-vsctl add-port br0 p0-3 -- set interface p0-3 type=patch options:peer=p3-0 ofport_request=30 # Populate the MAC table of br0 ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP1,actions=10 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP2,actions=20 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP3,actions=30 ovs-ofctl del-flows br-in1 ovs-ofctl del-flows br-in2 ovs-ofctl del-flows br-in3 ovs-ofctl del-flows br-p1 ovs-ofctl del-flows br-p2 ovs-ofctl del-flows br-p3 ], [0]) ### Setup vxlan-gpe tunnels AT_CHECK([ ovs-vsctl add-port br-in1 vxlangpe12 -- \ set interface vxlangpe12 type=vxlan options:exts=gpe options:remote_ip=10.0.0.2 options:packet_type=ptap ofport_request=1020 ovs-vsctl add-port br-in1 vxlangpe13 -- \ set interface vxlangpe13 type=vxlan options:exts=gpe options:remote_ip=10.0.0.3 options:packet_type=ptap ofport_request=1030 ovs-vsctl add-port br-in2 vxlangpe21 -- \ set interface vxlangpe21 type=vxlan options:exts=gpe options:remote_ip=20.0.0.1 options:packet_type=ptap ofport_request=2010 ovs-vsctl add-port br-in2 vxlangpe23 -- \ set interface vxlangpe23 type=vxlan options:exts=gpe options:remote_ip=20.0.0.3 options:packet_type=ptap ofport_request=2030 ovs-vsctl add-port br-in3 vxlangpe31 -- \ set interface vxlangpe31 type=vxlan options:exts=gpe options:remote_ip=30.0.0.1 options:packet_type=ptap ofport_request=3010 ovs-vsctl add-port br-in3 vxlangpe32 -- \ set interface vxlangpe32 type=vxlan options:exts=gpe options:remote_ip=30.0.0.2 options:packet_type=ptap ofport_request=3020 ovs-appctl netdev-dummy/ip4addr br-p1 10.0.0.1/24 ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3 ovs-appctl netdev-dummy/ip4addr br-p2 20.0.0.2/24 ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3 ovs-appctl netdev-dummy/ip4addr br-p3 30.0.0.3/24 ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ovs-appctl ovs/route/show | grep Cached: | sort ], [0], [dnl Cached: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1 Cached: 10.0.0.1/32 dev br-p1 SRC 10.0.0.1 local Cached: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2 Cached: 20.0.0.2/32 dev br-p2 SRC 20.0.0.2 local Cached: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3 Cached: 30.0.0.3/32 dev br-p3 SRC 30.0.0.3 local ]) AT_CHECK([ ovs-appctl tnl/neigh/show | grep br-p | sort ], [0], [stdout]) ### Flows in br-pto twist TEP IP addresses in tunnel IP headers AT_CHECK([ ovs-ofctl add-flow br-p1 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:20.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:30.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL ovs-ofctl add-flow br-p2 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:10.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:30.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL ovs-ofctl add-flow br-p3 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:10.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:20.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ], [0]) AT_CHECK([ ovs-ofctl dump-flows br-p1 | ofctl_strip | sort | grep actions ovs-ofctl dump-flows br-p2 | ofctl_strip | sort | grep actions ovs-ofctl dump-flows br-p3 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=20.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ip,in_port=2,nw_dst=30.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ip,in_port=2,nw_dst=30.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ip,in_port=2,nw_dst=20.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ]) ### Setup test ports for traffic injection N1_IP=192.168.10.10 N2_IP=192.168.10.20 N3_IP=192.168.10.30 N1_MAC=aa:55:aa:55:00:01 N2_MAC=aa:55:aa:55:00:02 N3_MAC=aa:55:aa:55:00:03 N1_OFPORT=10 N2_OFPORT=20 N3_OFPORT=30 AT_CHECK([ ovs-vsctl add-port br-in1 n1 -- \ set interface n1 type=dummy ofport_request=$N1_OFPORT options:tx_pcap=n1.pcap ovs-vsctl add-port br-in2 n2 -- \ set interface n2 type=dummy ofport_request=$N2_OFPORT options:tx_pcap=n2.pcap ovs-vsctl add-port br-in3 n3 -- \ set interface n3 type=dummy ofport_request=$N3_OFPORT options:tx_pcap=n3.pcap ], [0]) #N1_DPPORT=$(ovs-appctl dpif/show | grep "n1 10" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N2_DPPORT=$(ovs-appctl dpif/show | grep "n2 20" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N3_DPPORT=$(ovs-appctl dpif/show | grep "n3 30" | sed 's|.*/\([[0-9]]*\):.*|\1|') ### Verify datapath configuration AT_CHECK([ovs-appctl dpif/show | grep -v hit], [0], [dnl br-in1: br-in1 65534/101: (dummy-internal) n1 10/4: (dummy) vxlangpe12 1020/4789: (vxlan: packet_type=ptap, remote_ip=10.0.0.2) vxlangpe13 1030/4789: (vxlan: packet_type=ptap, remote_ip=10.0.0.3) br-in2: br-in2 65534/102: (dummy-internal) n2 20/5: (dummy) vxlangpe21 2010/4789: (vxlan: packet_type=ptap, remote_ip=20.0.0.1) vxlangpe23 2030/4789: (vxlan: packet_type=ptap, remote_ip=20.0.0.3) br-in3: br-in3 65534/103: (dummy-internal) n3 30/6: (dummy) vxlangpe31 3010/4789: (vxlan: packet_type=ptap, remote_ip=30.0.0.1) vxlangpe32 3020/4789: (vxlan: packet_type=ptap, remote_ip=30.0.0.2) br-p1: br-p1 65534/1: (dummy-internal) p1-0 2/none: (patch: peer=p0-1) br-p2: br-p2 65534/2: (dummy-internal) p2-0 2/none: (patch: peer=p0-2) br-p3: br-p3 65534/3: (dummy-internal) p3-0 2/none: (patch: peer=p0-3) br0: br0 65534/100: (dummy-internal) p0-1 10/none: (patch: peer=p1-0) p0-2 20/none: (patch: peer=p2-0) p0-3 30/none: (patch: peer=p3-0) ]) ### Setup SFC flow configuration # br-in1 is SFC classifier (table 1) and final SFF (tables 2,3) AT_DATA([br-in1.txt], [dnl table=0,in_port=10,ip,actions=decap(),goto_table:1 table=0,in_port=10,packet_type=(1,0x800),actions=goto_table:1 table=0,in_port=1020,packet_type=(1,0x894f),actions=goto_table:2 table=0,in_port=1030,packet_type=(1,0x894f),actions=goto_table:2 table=1,priority=32,packet_type=(1,0x800),nw_dst=192.168.10.30,actions=encap(nsh(md_type=1)),set_field:0x3000->nsh_spi,output:1030 table=1,priority=0,packet_type=(1,0x800),nw_dst=0.0.0.0/0,actions=encap(nsh(md_type=1)),set_field:0x3020->nsh_spi,output:1020 table=2,packet_type=(1,0x894f),nsh_spi=0x1000,nsh_si=255,actions=decap(),goto_table:3 table=2,packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254,actions=decap(),goto_table:3 table=3,ip,actions=set_field:aa:55:aa:55:00:01->dl_dst,output:10 table=3,packet_type=(1,0x800),actions=encap(ethernet),set_field:aa:55:aa:55:00:01->dl_dst,output:10 ]) # br-in2 is intermediate SFF (table 2) and simulated SF (table 4) AT_DATA([br-in2.txt], [dnl table=0,in_port=2010,packet_type=(1,0x894f),actions=goto_table:2 table=0,in_port=2030,packet_type=(1,0x894f),actions=goto_table:2 table=2,packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=255,actions=encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,goto_table:4 table=2,packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254,actions=output:2030 table=2,packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=255,actions=encap(ethernet),set_field:77:88:99:aa:bb:cc->dl_dst,goto_table:4 table=2,packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254,actions=output:2010 table=4,dl_type=0x894f,dl_dst=11:22:33:44:55:66,actions=set_field:254->nsh_si,decap(),resubmit(,2) table=4,dl_type=0x894f,dl_dst=77:88:99:aa:bb:cc,actions=dec_nsh_ttl,decap(),resubmit(,2) ]) # br-in3 is SFC classifier (table 1) and final SFF (tables 2,3) AT_DATA([br-in3.txt], [dnl table=0,in_port=30,ip,actions=decap(),goto_table:1 table=0,in_port=30,packet_type=(1,0x800),actions=goto_table:1 table=0,in_port=3010,packet_type=(1,0x894f),actions=goto_table:2 table=0,in_port=3020,packet_type=(1,0x894f),actions=goto_table:2 table=1,priority=32,packet_type=(1,0x800),nw_dst=192.168.10.10,actions=encap(nsh(md_type=1)),set_field:0x1000->nsh_spi,3010 table=1,priority=0,packet_type=(1,0x800),nw_dst=0.0.0.0/0,actions=encap(nsh(md_type=1)),set_field:0x1020->nsh_spi,3020 table=2,packet_type=(1,0x894f),nsh_spi=0x3000,nsh_si=255,actions=decap(),goto_table:3 table=2,packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254,actions=decap(),goto_table:3 table=3,ip,actions=set_field:aa:55:aa:55:00:03->dl_dst,output:30 table=3,packet_type=(1,0x800),actions=encap(ethernet),set_field:aa:55:aa:55:00:03->dl_dst,output:30 ]) AT_CHECK([ ovs-ofctl -Oopenflow13 add-flows br-in1 br-in1.txt ovs-ofctl -Oopenflow13 add-flows br-in2 br-in2.txt ovs-ofctl -Oopenflow13 add-flows br-in3 br-in3.txt ]) ### Inject ICMP Echo request test packets # N1 to N3, via the direct SF path with spi 0x3000 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789) recirc_id(0),tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(+key)),in_port(4789),packet_type(ns=1,id=0x894f),eth_type(0x894f),nsh(np=1,spi=0x3000,si=255), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x1) recirc_id(0x1),tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(+key)),in_port(4789),packet_type(ns=1,id=0x800),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6 ]) AT_CHECK([ ovs-ofctl -Oopenflow13 dump-flows br-in1 | ofctl_strip | sort | grep actions ovs-ofctl -Oopenflow13 dump-flows br-in2 | ofctl_strip | sort | grep actions ovs-ofctl -Oopenflow13 dump-flows br-in3 | ofctl_strip | sort | grep actions ], [0], [dnl n_packets=2, n_bytes=196, ip,in_port=10 actions=decap(),goto_table:1 packet_type=(1,0x800),in_port=10 actions=goto_table:1 packet_type=(1,0x894f),in_port=1020 actions=goto_table:2 packet_type=(1,0x894f),in_port=1030 actions=goto_table:2 table=1, n_packets=2, n_bytes=196, priority=32,packet_type=(1,0x800),nw_dst=192.168.10.30 actions=encap(nsh(md_type=1)),set_field:0x3000->nsh_spi,output:1030 table=1, priority=0,packet_type=(1,0x800) actions=encap(nsh(md_type=1)),set_field:0x3020->nsh_spi,output:1020 table=2, packet_type=(1,0x894f),nsh_spi=0x1000,nsh_si=255 actions=decap(),goto_table:3 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254 actions=decap(),goto_table:3 table=3, ip actions=set_field:aa:55:aa:55:00:01->eth_dst,output:10 table=3, packet_type=(1,0x800) actions=encap(ethernet),set_field:aa:55:aa:55:00:01->eth_dst,output:10 packet_type=(1,0x894f),in_port=2010 actions=goto_table:2 packet_type=(1,0x894f),in_port=2030 actions=goto_table:2 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254 actions=output:2010 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=255 actions=encap(ethernet),set_field:77:88:99:aa:bb:cc->eth_dst,goto_table:4 table=2, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254 actions=output:2030 table=2, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=255 actions=encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,goto_table:4 table=4, dl_dst=11:22:33:44:55:66,dl_type=0x894f actions=set_field:254->nsh_si,decap(),resubmit(,2) table=4, dl_dst=77:88:99:aa:bb:cc,dl_type=0x894f actions=dec_nsh_ttl,decap(),resubmit(,2) ip,in_port=30 actions=decap(),goto_table:1 n_packets=2, n_bytes=216, packet_type=(1,0x894f),in_port=3010 actions=goto_table:2 packet_type=(1,0x800),in_port=30 actions=goto_table:1 packet_type=(1,0x894f),in_port=3020 actions=goto_table:2 table=1, priority=0,packet_type=(1,0x800) actions=encap(nsh(md_type=1)),set_field:0x1020->nsh_spi,output:3020 table=1, priority=32,packet_type=(1,0x800),nw_dst=192.168.10.10 actions=encap(nsh(md_type=1)),set_field:0x1000->nsh_spi,output:3010 table=2, n_packets=2, n_bytes=216, packet_type=(1,0x894f),nsh_spi=0x3000,nsh_si=255 actions=decap(),goto_table:3 table=2, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254 actions=decap(),goto_table:3 table=3, ip actions=set_field:aa:55:aa:55:00:03->eth_dst,output:30 table=3, n_packets=2, n_bytes=168, packet_type=(1,0x800) actions=encap(ethernet),set_field:aa:55:aa:55:00:03->eth_dst,output:30 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N1 to some other IP destination (192.168.10.20), via the indirect SF path with spi 0x3020 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 461e7d1a95a13a6dd2099cab080045000054500b40004001552fc0a80a0ac0a80a140800531f09a90001e9509a580000000055ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 461e7d1a95a13a6dd2099cab08004500005450934000400154a7c0a80a0ac0a80a140800f41d09a90002ea509a5800000000b3ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637], [0], [ignore]) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789) recirc_id(0),tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(+key)),in_port(4789),packet_type(ns=1,id=0x894f),eth_type(0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789) recirc_id(0),tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(+key)),in_port(4789),packet_type(ns=1,id=0x894f),eth_type(0x894f),nsh(np=1,spi=0x3020,si=254), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x2) recirc_id(0x2),tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(+key)),in_port(4789),packet_type(ns=1,id=0x800),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6 ]) AT_CHECK([ ovs-ofctl -Oopenflow13 dump-flows br-in1 | ofctl_strip | sort | grep actions ovs-ofctl -Oopenflow13 dump-flows br-in2 | ofctl_strip | sort | grep actions ovs-ofctl -Oopenflow13 dump-flows br-in3 | ofctl_strip | sort | grep actions ], [0], [dnl n_packets=4, n_bytes=392, ip,in_port=10 actions=decap(),goto_table:1 packet_type=(1,0x800),in_port=10 actions=goto_table:1 packet_type=(1,0x894f),in_port=1020 actions=goto_table:2 packet_type=(1,0x894f),in_port=1030 actions=goto_table:2 table=1, n_packets=2, n_bytes=196, priority=0,packet_type=(1,0x800) actions=encap(nsh(md_type=1)),set_field:0x3020->nsh_spi,output:1020 table=1, n_packets=2, n_bytes=196, priority=32,packet_type=(1,0x800),nw_dst=192.168.10.30 actions=encap(nsh(md_type=1)),set_field:0x3000->nsh_spi,output:1030 table=2, packet_type=(1,0x894f),nsh_spi=0x1000,nsh_si=255 actions=decap(),goto_table:3 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254 actions=decap(),goto_table:3 table=3, ip actions=set_field:aa:55:aa:55:00:01->eth_dst,output:10 table=3, packet_type=(1,0x800) actions=encap(ethernet),set_field:aa:55:aa:55:00:01->eth_dst,output:10 n_packets=2, n_bytes=216, packet_type=(1,0x894f),in_port=2010 actions=goto_table:2 packet_type=(1,0x894f),in_port=2030 actions=goto_table:2 table=2, n_packets=2, n_bytes=216, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254 actions=output:2030 table=2, n_packets=2, n_bytes=216, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=255 actions=encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,goto_table:4 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=254 actions=output:2010 table=2, packet_type=(1,0x894f),nsh_spi=0x1020,nsh_si=255 actions=encap(ethernet),set_field:77:88:99:aa:bb:cc->eth_dst,goto_table:4 table=4, dl_dst=77:88:99:aa:bb:cc,dl_type=0x894f actions=dec_nsh_ttl,decap(),resubmit(,2) table=4, n_packets=2, n_bytes=216, dl_dst=11:22:33:44:55:66,dl_type=0x894f actions=set_field:254->nsh_si,decap(),resubmit(,2) ip,in_port=30 actions=decap(),goto_table:1 n_packets=2, n_bytes=216, packet_type=(1,0x894f),in_port=3010 actions=goto_table:2 n_packets=2, n_bytes=216, packet_type=(1,0x894f),in_port=3020 actions=goto_table:2 packet_type=(1,0x800),in_port=30 actions=goto_table:1 table=1, priority=0,packet_type=(1,0x800) actions=encap(nsh(md_type=1)),set_field:0x1020->nsh_spi,output:3020 table=1, priority=32,packet_type=(1,0x800),nw_dst=192.168.10.10 actions=encap(nsh(md_type=1)),set_field:0x1000->nsh_spi,output:3010 table=2, n_packets=2, n_bytes=216, packet_type=(1,0x894f),nsh_spi=0x3000,nsh_si=255 actions=decap(),goto_table:3 table=2, n_packets=2, n_bytes=216, packet_type=(1,0x894f),nsh_spi=0x3020,nsh_si=254 actions=decap(),goto_table:3 table=3, ip actions=set_field:aa:55:aa:55:00:03->eth_dst,output:30 table=3, n_packets=4, n_bytes=336, packet_type=(1,0x800) actions=encap(ethernet),set_field:aa:55:aa:55:00:03->eth_dst,output:30 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/odp.at000066400000000000000000001064621514270232600212430ustar00rootroot00000000000000AT_BANNER([datapath parsing and formatting]) AT_SETUP([OVS datapath key parsing and formatting - valid forms]) dnl We could add a test for invalid forms, but that's less important. AT_DATA([odp-base.txt], [dnl eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x81,ttl=128,frag=no) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=first) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=later) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80,dst=8080) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81,dst=6632) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1,code=2) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x71,hlimit=128,frag=no) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=first) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=later) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80,dst=8080) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tclass=0,hlimit=128,frag=no),udp(src=6630,dst=22) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=1,code=2) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e),nd_ext(nd_reserved=0x0,nd_options_type=2) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=1) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=0) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=1) eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=0) ]) (echo '# Valid forms without tun_id or VLAN header.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/' odp-base.txt echo echo '# Valid forms with tunnel header.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,ttl=64,flags(csum|key)),in_port(1),skb_mark(0x1234),/' odp-base.txt echo echo '# Valid forms with VLAN header.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with MPLS header.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/ s/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt echo echo '# Valid forms with MPLS multicast header.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/ s/\(eth([[^)]]*),?\)/\1,eth_type(0x8848),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt echo echo '# Valid forms with tunnel and VLAN headers.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),in_port(1),skb_mark(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with QOS priority, tunnel, and VLAN headers.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),in_port(1),skb_mark(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with conntrack fields.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_mark(0x12345678),ct_label(0x1234567890abcdef1234567890abcdef),/' odp-base.txt echo echo '# Valid forms with IP first fragment.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),/,frag=first),/p' echo echo '# Valid forms with IP later fragment.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p' echo echo '# Valid forms with tunnel and ERSPAN v1 headers.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=1,idx=0x7),flags(df|key)),in_port(1),skb_mark(0),/' odp-base.txt echo echo '# Valid forms with tunnel and ERSPAN v2 headers.' sed 's/^/recirc_id(0),dp_hash(0),skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=2,dir=1,hwid=0x7),flags(df|key)),in_port(1),skb_mark(0),/' odp-base.txt ) > odp-in.txt AT_CAPTURE_FILE([odp-in.txt]) dnl If the BoS bit of the last LSE is 0 then the stack is unterminated dnl Internally a stack of 3 LSEs will be used with the trailing LSEs dnl set to zero. This is reflected when the key is formated sed '/bos=0/{ s/^/ODP_FIT_TOO_LITTLE: / }' < odp-in.txt > odp-out.txt dnl Some fields are always printed for this test, because wildcards aren't dnl specified. We can skip these. sed -i'back' 's/\(skb_mark(0)\),\(ct\)/\1,ct_state(0),ct_zone(0),\2/' odp-out.txt sed -i'back' 's/\(skb_mark([[^)]]*)\),\(eth\)/\1,ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),\2/' odp-out.txt sed -i'back' 's/\(ct_label([[^)]]*)\),\(eth\)/\1,packet_type(ns=0,id=0),\2/' odp-out.txt AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt` ]) AT_CHECK_UNQUOTED([cat odp-in.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CLEANUP AT_SETUP([OVS datapath wildcarded key parsing and formatting - valid forms]) dnl We could add a test for invalid forms, but that's less important. AT_DATA([odp-base.txt], [dnl in_port(1/0xff),eth(src=00:01:02:03:04:05/ff:ff:ff:ff:ff:f0,dst=10:11:12:13:14:15/ff:ff:ff:ff:ff:f0) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff,dst=6632/0xff00) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1/0xf0,code=2) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tclass=0,hlimit=128,frag=no),udp(src=6630/0xff00,dst=22/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=1/0xf0,code=2) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3/::250) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3/::250,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) skb_mark(0x1234/0xfff0),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) ]) (echo '# Valid forms without tun_id or VLAN header.' cat odp-base.txt echo echo '# Valid forms with tunnel header.' sed 's/^/tunnel(tun_id=0x7f10354\/0xff,src=10.10.10.10\/255.255.255.0,dst=20.20.20.20\/255.255.255.0,ttl=64,vxlan(gbp(id=10\/0xff,flags=0xb)),flags(oam|csum|key)),/' odp-base.txt echo echo '# Valid forms with tunnel header (wildcard flag).' sed 's/^/tunnel(tun_id=0x7f10354\/0xff,src=10.10.10.10\/255.255.255.0,dst=20.20.20.20\/255.255.255.0,ttl=64,flags(-df+csum+key)),/' odp-base.txt echo echo '# Valid forms with Geneve header.' sed 's/^/tunnel(tun_id=0x7f10354\/0xff,src=10.10.10.10\/255.255.255.0,dst=20.20.20.20\/255.255.255.0,ttl=64,geneve({class=0,type=0,len=4,0xa\/0xff}{class=0xffff,type=0x1,len=4,0xffffffff}),flags(csum|key)),/' odp-base.txt echo echo '# Valid forms with VLAN header.' sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with MPLS header.' sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100\/0xff,tc=7\/7,ttl=64\/0xff,bos=1\/1)/' odp-base.txt echo echo '# Valid forms with QoS priority.' sed 's/^/skb_priority(0x1234\/0xff),/' odp-base.txt echo echo '# Valid forms with tunnel and VLAN headers.' sed 's/^/tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99/0xff0,pcp=7/0xe),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with QOS priority, tunnel, and VLAN headers.' sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with conntrack fields.' sed 's/\(eth([[^)]]*)\),/\1,ct_state(+trk),ct_zone(0x5\/0xff),ct_mark(0x10305070\/0xf0f0f0f0),ct_label(0x1234567890abcdef1234567890abcdef\/0x102030405060708090a0b0c0d0e0f0),ct_tuple4(src=10.10.10.10,dst=20.20.20.20,proto=17,tp_src=1,tp_dst=2),/' odp-base.txt echo echo '# Valid forms with IP first fragment.' sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt echo echo '# Valid forms with IP later fragment.' sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt echo echo '# Valid forms with tunnel and ERSPAN v1 headers.' sed 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=1/0,idx=0x7/0xf),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt echo echo '# Valid forms with tunnel and ERSPAN v2 headers.' sed 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=2,dir=1,hwid=0x7/0xf),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt ) > odp.txt AT_CAPTURE_FILE([odp.txt]) AT_CHECK_UNQUOTED([ovstest test-odp parse-wc-keys < odp.txt], [0], [`cat odp.txt` ]) AT_CHECK_UNQUOTED([cat odp.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CLEANUP AT_SETUP([OVS datapath wildcarded key filtering.]) dnl We could add a test for invalid forms, but that's less important. AT_DATA([odp-base.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff,dst=6632/0xff00) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1/0xf0,code=2/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) ]) AT_DATA([odp-vlan-base.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=100,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=100,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) ]) AT_DATA([odp-eth-type.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) ]) AT_DATA([odp-vlan.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) ]) AT_DATA([odp-ipv4.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no) ]) AT_DATA([odp-icmp.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no) ]) AT_DATA([odp-arp.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) ]) AT_DATA([odp-tcp.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) ]) AT_DATA([odp-tcp6.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='dl_type=0x1235' < odp-base.txt], [0], [`cat odp-eth-type.txt` ]) AT_CHECK_UNQUOTED([cat odp-eth-type.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='dl_vlan=99' < odp-vlan-base.txt], [0], [`cat odp-vlan.txt` ]) AT_CHECK_UNQUOTED([cat odp-vlan.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='dl_vlan=99,ip' < odp-vlan-base.txt], [0], [`cat odp-vlan.txt` ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='ip,nw_src=35.8.2.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='ip,nw_dst=172.16.0.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` ]) AT_CHECK_UNQUOTED([cat odp-ipv4.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='dl_type=0x0800,nw_src=35.8.2.199,nw_dst=172.16.0.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='icmp,nw_src=35.8.2.199' < odp-base.txt], [0], [`cat odp-icmp.txt` ]) AT_CHECK_UNQUOTED([cat odp-icmp.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='arp,arp_spa=1.2.3.5' < odp-base.txt], [0], [`cat odp-arp.txt` ]) AT_CHECK_UNQUOTED([cat odp-arp.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='tcp,tp_src=90' < odp-base.txt], [0], [`cat odp-tcp.txt` ]) AT_CHECK_UNQUOTED([cat odp-tcp.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CHECK_UNQUOTED([ovstest test-odp parse-filter filter='tcp6,tp_src=90' < odp-base.txt], [0], [`cat odp-tcp6.txt` ]) AT_CHECK_UNQUOTED([cat odp-tcp6.txt | sed 's/^#.*//' | sed 's/$/ actions:drop/' | test-dpparse.py]) AT_CLEANUP AT_SETUP([OVS datapath actions parsing and formatting - valid forms]) AT_DATA([actions.txt], [dnl 1,2,3 userspace(pid=6633,sFlow(vid=9,pcp=7,output=10),actions) userspace(pid=6633,sFlow(vid=9,pcp=7,output=10),actions,tunnel_out_port=10) userspace(pid=9765,slow_path(0)) userspace(pid=9765,slow_path(0),tunnel_out_port=10) userspace(pid=9765,slow_path(cfm)) userspace(pid=9765,slow_path(cfm),tunnel_out_port=10) userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f),actions) userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f),tunnel_out_port=10) userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10)) userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10,ingress)) userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10),tunnel_out_port=10) userspace(pid=6633,flow_sample(probability=123,collector_set_id=1234,obs_domain_id=2345,obs_point_id=3456,output_port=10,egress),tunnel_out_port=10) userspace(pid=6633,ipfix(output_port=10)) userspace(pid=6633,ipfix(output_port=10),tunnel_out_port=10) userspace(pid=6633,controller(reason=1,dont_send=0,continuation=1,recirc_id=4444,rule_cookie=0x5555,controller_id=0,max_len=65535)) userspace(pid=6633,controller(reason=1,dont_send=1,continuation=0,recirc_id=4444,rule_cookie=0x5555,controller_id=0,max_len=65535)) set(in_port(2)) set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15)) set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15/ff:ff:ff:00:00:00)) set(eth_type(0x1234)) set(ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no)) set(ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,ttl=128,frag=no)) set(ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no)) set(ipv4(src=35.8.2.41/255.255.255.0,tos=0x80,ttl=128,frag=no)) set(ipv4(tos=0/0x3)) set(ipv4(tos=0x80/0xfc)) set(ipv4(ttl=128,frag=no)) set(ipv4(frag=no)) set(tcp(src=80,dst=8080)) set(tcp(src=80/0xff00,dst=8080)) set(tcp(src=80)) set(tcp(dst=8080)) set(udp(src=81,dst=6632)) set(udp(src=81/0xff00,dst=6632)) set(udp(src=81)) set(udp(dst=6633)) set(sctp(src=82,dst=6633)) set(sctp(src=82/0xff00,dst=6633)) set(sctp(src=82)) set(sctp(dst=6632)) set(icmp(type=1,code=2)) set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)) set(ipv6(src=::1,dst=::2)) set(ipv6(label=0,proto=10,tclass=0x70,hlimit=128,frag=no)) set(ipv6(label=0,proto=10,hlimit=128)) set(ipv6(label=0/0xfff,proto=10,tclass=0x70/0x70)) set(ipv6(label=0)) set(icmpv6(type=1,code=2)) set(vlan(vid=5)) set(vlan(vid=3,pcp=5)) set(vlan(vid=3,pcp=5,cfi=0)) set(vlan(vid=5/0x3)) set(vlan(vid=5/0x1)),3 push_vlan(vid=12,pcp=0) push_vlan(vid=13,pcp=5,cfi=0) push_vlan(tpid=0x9100,vid=13,pcp=5) push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0) pop_vlan sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2))) set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(df|csum|key))) set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key))) tnl_pop(4) tnl_push(tnl_port(4),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1e241)),out_port(1)) tnl_push(tnl_port(4),header(size=46,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0xa000,proto=0x6558),csum=0x0,key=0x1e241)),out_port(1)) tnl_push(tnl_port(6),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1)) tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(oam,vni=0x1c7)),out_port(1)) tnl_push(tnl_port(6),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x1c7,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(1)) tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x1c7)),out_port(1)) tnl_push(tnl_port(4),header(size=62,type=109,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x2000,proto=0x6558),key=0x1e241)),out_port(1)) tnl_push(tnl_port(4),header(size=66,type=109,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0xa000,proto=0x6558),csum=0x0,key=0x1e241)),out_port(1)) tnl_push(tnl_port(6),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1)) tnl_push(tnl_port(6),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0x0),geneve(oam,vni=0x1c7)),out_port(1)) tnl_push(tnl_port(6),header(size=78,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x1c7,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(1)) tnl_push(tnl_port(6),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x1c7)),out_port(1)) tnl_push(tnl_port(6),header(size=78,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=0,segs(2001:cafe::90))),out_port(1)) tnl_push(tnl_port(6),header(size=110,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=2,segs(2001:cafe::90,2001:cafe::91,2001:cafe::92))),out_port(1)) ct ct(commit) ct(commit,zone=5) ct(commit,mark=0xa0a0a0a0/0xfefefefe) ct(commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff) ct(commit,helper=ftp) ct(commit,helper=tftp) ct(commit,timeout=ovs_tp_1_tcp4) ct(nat) ct(commit,nat(src)) ct(commit,timeout=ovs_tp_1_tcp4,nat(src)) ct(commit,nat(dst)) ct(commit,timeout=ovs_tp_1_tcp4,nat(dst)) ct(commit,nat(src=10.0.0.240,random)) ct(commit,nat(src=10.0.0.240:32768-65535,random)) ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)) ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)) ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)) ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)) ct(commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random)) ct(commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255)) ct(force_commit) ct(force_commit,zone=5) ct(force_commit,mark=0xa0a0a0a0/0xfefefefe) ct(force_commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff) ct(force_commit,helper=ftp) ct(nat) ct(force_commit,nat(src)) ct(force_commit,nat(dst)) ct(force_commit,nat(src=10.0.0.240,random)) ct(force_commit,nat(src=10.0.0.240:32768-65535,random)) ct(force_commit,nat(dst=10.0.0.128-10.0.0.254,hash)) ct(force_commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)) ct(force_commit,nat(src=fe80::20c:29ff:fe88:a18b,random)) ct(force_commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)) ct(force_commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random)) ct(force_commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255)) ct_clear trunc(100) clone(1) clone(clone(push_vlan(vid=12,pcp=0),2),1) set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))) set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=2,dir=1,hwid=0x1),flags(df|key))) check_pkt_len(size=200,gt(4),le(5)) check_pkt_len(size=200,gt(drop),le(5)) check_pkt_len(size=200,gt(ct(nat)),le(drop)) check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) lb_output(1) add_mpls(label=200,tc=7,ttl=64,bos=1,eth_type=0x8847) psample(group=12,cookie=0xf1020304050607080910111213141516) psample(group=12) sample(sample=50.0%,actions(psample(group=12,cookie=0xf1020304))) sample(sample=50.0%,actions(userspace(pid=42,userdata(0102030400000000)),psample(group=12))) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [`cat actions.txt` ]) AT_CHECK_UNQUOTED([cat actions.txt | sed 's/^/actions:/' | test-dpparse.py]) AT_CLEANUP AT_SETUP([OVS datapath actions parsing and formatting - invalid forms]) dnl This caused a hang in older versions. AT_DATA([actions.txt], [dnl encap_nsh@:{@ tnl_push(tnl_port(6),header(size=94,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=2,segs(2001:cafe::90,2001:cafe::91))),out_port(1)) tnl_push(tnl_port(6),header(size=126,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=2,segs(2001:cafe::90,2001:cafe::91,2001:cafe::92,2001:cafe::93))),out_port(1)) psample(group_id=12,cookie=0x0102030405060708090a0b0c0d0e0f0f0f) psample(cookie=0x010203) psample(group=12,cookie=0x010203,group=12) psample(group=abc) psample(group=12,cookie=wrong) psample() ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [dnl odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error ]) AT_CLEANUP AT_SETUP([OVS datapath actions parsing and formatting - userdata overflow]) dnl Userdata should fit in a single netlink message, i.e. should be less than dnl UINT16_MAX - NLA_HDRLEN = 65535 - 4 = 65531 bytes. OVS should not accept dnl larger userdata. OTOH, userdata is part of a nested netlink message, that dnl should not be oversized too. 'pid' takes NLA_HDRLEN + 4 = 8 bytes. dnl Plus NLA_HDRLEN for the nested header. 'actions' flag takes NLA_HDRLEN = 4 dnl and 'tunnel_out_port' takes NLA_HDRLEN + 4 = 8 bytes. dnl So, for the variant with 'actions' maximum length of userdata should be: dnl UINT16_MAX - NLA_HDRLEN - (NLA_HDRLEN + 4) - NLA_HDRLEN - NLA_HDRLEN dnl total max nested header pid actions userdata dnl Result: 65515 bytes for the actual userdata. dnl For the case with 'tunnel_out_port': 65511 dnl Size of userdata will be rounded up to be multiple of 4, so highest dnl acceptable sizes are 65512 and 65508. dnl String with length 65512 * 2 = 131024 is valid, while 131026 is not. data_valid=$( printf '%*s' 131024 | tr ' ' "a") data_invalid=$(printf '%*s' 131026 | tr ' ' "a") echo "userspace(pid=1234567,userdata(${data_valid}),actions)" > actions.txt echo "userspace(pid=1234567,userdata(${data_invalid}),actions)" >> actions.txt dnl String with length 65508 * 2 = 131016 is valid, while 131018 is not. data_valid=$( printf '%*s' 131016 | tr ' ' "a") data_invalid=$(printf '%*s' 131018 | tr ' ' "a") echo "userspace(pid=1234567,userdata(${data_valid}),tunnel_out_port=10)" >> actions.txt echo "userspace(pid=1234567,userdata(${data_invalid}),tunnel_out_port=10)" >> actions.txt AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [dnl `cat actions.txt | head -1` odp_actions_from_string: error `cat actions.txt | head -3 | tail -1` odp_actions_from_string: error ]) AT_CHECK_UNQUOTED([cat actions.txt | sed 's/^/actions:/' | test-dpparse.py]) AT_CLEANUP AT_SETUP([OVS datapath actions parsing and formatting - actions too long]) dnl Actions should fit in a single netlink message. dnl Empty set(encap()) takes 8 bytes to encode. So, 8192 of them is too much, dnl but 8191 still fits. actions=$(printf 'set(encap()),%.0s' $(seq 8190)) echo "${actions}set(encap())" > actions.txt echo "${actions}set(encap()),set(encap())" >> actions.txt AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [dnl `cat actions.txt | head -1` odp_actions_from_string: error ]) AT_CLEANUP AT_SETUP([OVS datapath keys parsing and formatting - keys too long]) dnl Flow keys should fit into a single netlink message. dnl Empty encap() takes 4 bytes. So, 16384 is too many, but 16383 still fits. dnl We're getting 'duplicate attribute' error since it's not a logically valid dnl sequence of keys. 'syntax error' indicates oversized list of keys. keys=$(printf 'encap(),%.0s' $(seq 16382)) echo "${keys}encap()" > keys.txt echo "${keys}encap(),encap()" >> keys.txt AT_CHECK([ovstest test-odp parse-keys < keys.txt | sed 's/encap(),//g'], [0], [dnl odp_flow_key_to_flow: error (duplicate encap attribute in flow key; the flow key in error is: encap()) odp_flow_from_string: error (syntax error at encap()) ]) AT_CLEANUP AT_SETUP([OVS datapath keys parsing and formatting - 33 nested encap ]) AT_DATA([odp-in.txt], [dnl encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap())))))))))))))))))))))))))))))))) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [dnl odp_flow_from_string: error (syntax error at encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap(encap()))))))))))))))))))))))))))))))))) ]) AT_CLEANUP AT_BANNER([datapath actions in userspace]) AT_SETUP([odp-execute - actions implementation]) OVS_VSWITCHD_START() AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Set the scalar first, so we always have the scalar impl as Active. AT_CHECK([ovs-appctl odp-execute/action-impl-set scalar], [0], [dnl Action implementation set to scalar. ]) AT_CHECK([ovs-appctl odp-execute/action-impl-show | grep "scalar"], [], [dnl scalar (available: Yes, active: Yes) ]) AT_CHECK([ovs-appctl odp-execute/action-impl-show | grep "autovalidator"], [], [dnl autovalidator (available: Yes, active: No) ]) dnl Set the autovalidator impl to active. AT_CHECK([ovs-appctl odp-execute/action-impl-set autovalidator], [0], [dnl Action implementation set to autovalidator. ]) AT_CHECK([ovs-appctl odp-execute/action-impl-show | grep "scalar"], [], [dnl scalar (available: Yes, active: No) ]) AT_CHECK([ovs-appctl odp-execute/action-impl-show | grep "autovalidator"], [], [dnl autovalidator (available: Yes, active: Yes) ]) AT_CHECK([ovs-appctl odp-execute/action-impl-set invalid_implementation], [2], [], [dnl Error: unknown action implementation, invalid_implementation, specified! ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP(["/Failed setting action implementation to invalid_implementation/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofp-actions.at000066400000000000000000001221201514270232600226700ustar00rootroot00000000000000AT_BANNER([OpenFlow actions]) AT_SETUP([OpenFlow 1.0 action translation]) AT_KEYWORDS([ofp-actions OF1.0]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0008 fffe 04d2 # actions=CONTROLLER:1234 0000 0008 fffd 04d2 # actions=mod_vlan_vid:9 0001 0008 0009 0000 # actions=mod_vlan_pcp:6 0002 0008 06 000000 # actions=strip_vlan 0003 0008 00000000 # actions=mod_dl_src:00:11:22:33:44:55 0004 0010 001122334455 000000000000 # actions=mod_dl_dst:10:20:30:40:50:60 0005 0010 102030405060 000000000000 # actions=mod_nw_src:1.2.3.4 0006 0008 01020304 # actions=mod_nw_dst:192.168.0.1 0007 0008 c0a80001 # actions=mod_nw_tos:48 0008 0008 30 000000 # actions=mod_tp_src:80 0009 0008 0050 0000 # actions=mod_tp_dst:443 000a 0008 01bb 0000 # actions=enqueue:10:55 000b 0010 000a 000000000000 00000037 # actions=resubmit:5 ffff 0010 00002320 0001 0005 00000000 # actions=set_tunnel:0x12345678 ffff 0010 00002320 0002 0000 12345678 # actions=set_queue:2309737729 ffff 0010 00002320 0004 0000 89abcd01 # actions=pop_queue ffff 0010 00002320 0005 000000000000 # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802 # actions=load:0xf009->NXM_OF_VLAN_TCI[] ffff 0018 00002320 0007 000f 00000802 000000000000f009 # actions=note:11.e9.9a.ad.67.f3 ffff 0010 00002320 0008 11e99aad67f3 # actions=set_tunnel64:0xc426384d49c53d60 ffff 0018 00002320 0009 000000000000 c426384d49c53d60 # actions=set_tunnel64:0x885f3298 ffff 0018 00002320 0009 000000000000 00000000885f3298 # bad OpenFlow10 actions: OFPBIC_UNSUP_INST & ofp_actions|WARN|write_metadata instruction not allowed here ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff # bad OpenFlow10 actions: OFPBIC_UNSUP_INST & ofp_actions|WARN|write_metadata instruction not allowed here ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffff0000ffff0000 # actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[]) ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004 # actions=bundle(eth_src,0,hrw,ofport,members:4,8) ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl 0004 0008 00000000 # actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],members:4,8) ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl 0004 0008 00000000 # actions=resubmit(10,5) ffff 0010 00002320 000e 000a 05 000000 # actions=resubmit(10,5,ct) ffff 0010 00002320 002c 000a 05 000000 # actions=output:NXM_NX_REG1[5..10] ffff 0018 00002320 000f 0145 00010204 ffff 000000000000 # actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl 000c 00000802 0000 00000802 0000 dnl 0030 00000406 0000 00000206 0000 dnl 1010 00000002 0000 dnl 00000000 # actions=exit ffff 0010 00002320 0011 000000000000 # actions=dec_ttl ffff 0010 00002320 0012 000000000000 # actions=fin_timeout(idle_timeout=10,hard_timeout=20) ffff 0010 00002320 0013 000a 0014 0000 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678) ffff 0010 00002320 0014 04d2 162e 02 00 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678,userdata=01.02.03.04.05,pause) ffff 0040 00002320 0025 000000000000 dnl 0000 0008 04d2 0000 dnl 0001 0008 162e 0000 dnl 0002 0005 02 000000 dnl 0003 0009 0102030405 00000000000000 dnl 0004 0004 00000000 # actions=dec_ttl(32768,12345,90,765,1024) ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) ffff 0020 00002320 0026 3039 00005BA0 00008707 0000B26E DDD50000 00000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) ffff 0020 00002320 0029 3039 00005BA0 00008707 0000B26E DDD50200 00000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 # bad OpenFlow10 actions: OFPBAC_BAD_LEN & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 240 exceeds action buffer length 8 & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_LEN): & 00000000 00 00 00 f0 00 00 00 00- 00 00 00 f0 00 00 00 00 # bad OpenFlow10 actions: OFPBAC_BAD_LEN & ofp_actions|WARN|OpenFlow action OFPAT_OUTPUT length 16 not in valid range [[8,8]] & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_LEN): & 00000000 00 00 00 10 ff fe ff ff-00 00 00 00 00 00 00 00 00 00 00 10 ff fe ff ff 00 00 00 00 00 00 00 00 # bad OpenFlow10 actions: OFPBAC_BAD_LEN & ofp_actions|WARN|OpenFlow action NXAST_DEC_TTL_CNT_IDS length 17 is not a multiple of 8 ffff 0011 00002320 0015 0001 00000000 0000000000000000 # bad OpenFlow10 actions: OFPBAC_BAD_OUT_PORT 0000 0008 ffff 0000 # actions=ct() ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0000 # actions=ct(commit) ffff 0018 00002320 0023 0001 00000000 0000 FF 000000 0000 # actions=ct(commit,force) ffff 0018 00002320 0023 0003 00000000 0000 FF 000000 0000 # bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT ffff 0018 00002320 0023 0002 00000000 0000 FF 000000 0000 # actions=ct(table=10) ffff 0018 00002320 0023 0000 00000000 0000 0A 000000 0000 # actions=ct(zone=10) ffff 0018 00002320 0023 0000 00000000 000A FF 000000 0000 # actions=ct(zone=NXM_NX_REG0[0..15]) ffff 0018 00002320 0023 0000 00010004 000F FF 000000 0000 dnl Can't read 8 bits from register into 16-bit zone. # bad OpenFlow10 actions: OFPBAC_BAD_SET_LEN ffff 0018 00002320 0023 0000 00010004 0007 FF 000000 0000 dnl Can't read 32 bits from register into 16-bit zone. # bad OpenFlow10 actions: OFPBAC_BAD_SET_LEN ffff 0018 00002320 0023 0000 00010004 001F FF 000000 0000 # actions=ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])) ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0007 001f 0001d604 000000000000f009 # actions=ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])) ffff 0030 00002320 0023 0003 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0007 001f 0001d604 000000000000f009 # actions=ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])) ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0007 103f 0001d810 0000 0000 0000 0000 dnl ffff 0018 00002320 0007 003f 0001d810 0000 0000 0000 001d # bad OpenFlow10 actions: OFPBAC_BAD_SET_ARGUMENT & ofp_actions|WARN|cannot set CT fields outside of ct action ffff 0018 00002320 0007 001f 0001d604 000000000000f009 # bad OpenFlow10 actions: OFPBAC_BAD_SET_ARGUMENT & meta_flow|WARN|destination field ct_zone is not writable ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0007 000f 0001d504 000000000000f009 # bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT & ofp_actions|WARN|ct action doesn't support nested action ct ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0000 # bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT & ofp_actions|WARN|ct action doesn't support nested modification of reg0 ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0007 001f 00010004 000000000000f009 # actions=ct(alg=ftp) ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0015 # actions=ct(alg=tftp) ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0045 # actions=ct(commit,nat(src)) ffff 0028 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0010 00002320 0024 00 00 0001 0000 # actions=ct(commit,nat(dst)) ffff 0028 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0010 00002320 0024 00 00 0002 0000 # actions=ct(nat) ffff 0028 00002320 0023 0000 00000000 0000 FF 000000 0000 dnl ffff 0010 00002320 0024 00 00 0000 0000 # actions=ct(commit,nat(src=10.0.0.240,random)) ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0024 00 00 0011 0001 0a0000f0 00000000 # actions=ct(commit,nat(src=10.0.0.240:32768-65535,random)) ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0024 00 00 0011 0031 0a0000f0 8000ffff # actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)) ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0018 00002320 0024 00 00 000a 0003 0a000080 0a0000fe # actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)) ffff 0038 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0020 00002320 0024 00 00 0005 0033 0a0000f0 0a0000fe 8000ffff 00000000 # actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)) ffff 0038 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0020 00002320 0024 00 00 0011 0004 fe800000 00000000 020c 29ff fe88 a18b # actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)) ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0030 00002320 0024 00 00 0011 000c fe800000 00000000 020c 29ff fe88 0001 fe800000 00000000 020c 29ff fe88 a18b # actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)) ffff 0050 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0038 00002320 0024 00 00 0011 003c dnl fe800000 00000000 020c 29ff fe88 0001 dnl fe800000 00000000 020c 29ff fe88 a18b dnl 00ff1000 00000000 # actions=ct_clear ffff 0010 00002320 002b 000000000000 # actions=output(port=1,max_len=100) ffff 0010 00002320 0027 0001 00000064 # actions=clone(mod_vlan_vid:5,output:10) ffff 0020 00002320 002a 000000000000 dnl 0001 0008 0005 0000 dnl 0000 0008 000a 0000 # actions=learn(table=2,priority=0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) ffff 0050 00002320 002d 0000 0000 0000 0000000000000000 0000 02 00 0000 0000 00000000 0000 0000 dnl 000c 00000802 0000 00000802 0000 dnl 0030 00000406 0000 00000206 0000 dnl 1010 00000002 0000 dnl 00000000 # actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,limit=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) ffff 0050 00002320 002d 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 00000001 0000 0000 dnl 000c 00000802 0000 00000802 0000 dnl 0030 00000406 0000 00000206 0000 dnl 1010 00000002 0000 dnl 00000000 # actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,limit=1,result_dst=NXM_NX_REG0[8],NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) ffff 0050 00002320 002d 000a 0014 0050 123456789abcdef0 0004 02 00 0002 0004 00000001 0008 0000 dnl 00010004 dnl 000c 00000802 0000 00000802 0000 dnl 0030 00000406 0000 00000206 0000 dnl 1010 00000002 0000 # actions=group:5 ffff 0010 00002320 0028 0000 00000005 # bad OpenFlow10 actions: NXBRC_MUST_BE_ZERO ffff 0018 00002320 0025 0000 0005 0000 1122334455 000005 # bad OpenFlow10 actions: NXBRC_MUST_BE_ZERO ffff 0018 00002320 0025 0000 0005 5000 1122334455 000000 # bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0030 00002320 0024 00 00 0011 000c fe800000 00000000 020c 29ff fe88 a18b fe800000 00000000 020c 29ff fe88 0001 # actions=check_pkt_larger(1500)->NXM_NX_REG0[0] ffff 0018 00002320 0031 05dc 000000010004000000000000 # actions=check_pkt_larger(1000)->NXM_NX_XXREG1[4] ffff 0018 00002320 0031 03e8 00040001e010000000000000 # actions=delete_field:tun_metadata10 ffff 0018 00002320 0032 00 01 64 7c 00 00 00 00 000000000000 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow10 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP AT_SETUP([OpenFlow 1.0 "instruction" translations]) AT_KEYWORDS([ofp-actions OF1.0 instruction]) AT_DATA([test-data], [dnl dnl Try a couple of ordinary actions to make sure they're accepted, dnl but there's no point in retrying all the actions from the previous test. # actions=LOCAL 0000 0008 fffe 04d2 # actions=mod_dl_src:00:11:22:33:44:55 0004 0010 001122334455 000000000000 dnl Now check that write_metadata is accepted. # actions=write_metadata:0xfedcba9876543210 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff # actions=write_metadata:0xfedcba9876543210/0xffff0000ffff0000 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffff0000ffff0000 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-instructions OpenFlow10 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP AT_SETUP([OpenFlow 1.1 action translation]) AT_KEYWORDS([ofp-actions OF1.1]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0010 fffffffe 04d2 000000000000 # actions=CONTROLLER:1234 0000 0010 fffffffd 04d2 000000000000 # actions=set_vlan_vid:9 0001 0008 0009 0000 # actions=set_vlan_pcp:6 0002 0008 06 000000 # actions=mod_dl_src:00:11:22:33:44:55 0003 0010 001122334455 000000000000 # actions=mod_dl_dst:10:20:30:40:50:60 0004 0010 102030405060 000000000000 # actions=mod_nw_src:1.2.3.4 0005 0008 01020304 # actions=mod_nw_dst:192.168.0.1 0006 0008 c0a80001 # actions=mod_nw_tos:48 0007 0008 30 000000 # actions=mod_nw_ecn:2 0008 0008 02 000000 # actions=mod_tp_src:80 0009 0008 0050 0000 # actions=mod_tp_dst:443 000a 0008 01bb 0000 # actions=pop_vlan 0012 0008 00000000 # actions=set_queue:2309737729 0015 0008 89abcd01 dnl 802.1ad isn't supported at the moment dnl # actions=push_vlan:0x88a8 dnl 0011 0008 88a8 0000 # actions=push_vlan:0x8100 0011 0008 8100 0000 # actions=resubmit:5 ffff 0010 00002320 0001 0005 00000000 # actions=set_tunnel:0x12345678 ffff 0010 00002320 0002 0000 12345678 # actions=pop_queue ffff 0010 00002320 0005 000000000000 # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802 # actions=load:0xf009->NXM_OF_VLAN_TCI[] ffff 0018 00002320 0007 000f 00000802 000000000000f009 # actions=note:11.e9.9a.ad.67.f3 ffff 0010 00002320 0008 11e99aad67f3 # actions=set_tunnel64:0xc426384d49c53d60 ffff 0018 00002320 0009 000000000000 c426384d49c53d60 # actions=set_tunnel64:0x885f3298 ffff 0018 00002320 0009 000000000000 00000000885f3298 dnl Write-Metadata is only allowed in contexts that allow instructions. & ofp_actions|WARN|write_metadata instruction not allowed here # bad OpenFlow11 actions: OFPBIC_UNSUP_INST ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff # actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[]) ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004 # actions=bundle(eth_src,0,hrw,ofport,members:4,8) ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl 0004 0008 00000000 # actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],members:4,8) ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl 0004 0008 00000000 # actions=resubmit(10,5) ffff 0010 00002320 000e 000a 05 000000 # actions=resubmit(10,5,ct) ffff 0010 00002320 002c 000a 05 000000 # actions=output:NXM_NX_REG1[5..10] ffff 0018 00002320 000f 0145 00010204 ffff 000000000000 # actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl 000c 00000802 0000 00000802 0000 dnl 0030 00000406 0000 00000206 0000 dnl 1010 00000002 0000 dnl 00000000 # actions=exit ffff 0010 00002320 0011 000000000000 dnl OpenFlow 1.1 OFPAT_DEC_TTL # actions=dec_ttl 0018 0008 00000000 # actions=fin_timeout(idle_timeout=10,hard_timeout=20) ffff 0010 00002320 0013 000a 0014 0000 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678) ffff 0010 00002320 0014 04d2 162e 02 00 # actions=dec_ttl(32768,12345,90,765,1024) ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000 # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) ffff 0018 00002320 001d 3039 00005BA0 00008707 0000B26E # actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ffff 0028 00002320 0033 3039 00005ba0 00000002 000f0000 0001d810 081f0000 0000 000000000000 # bad OpenFlow11 actions: OFPBAC_BAD_OUT_PORT & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_OUT_PORT): & 00000000 00 00 00 10 ff ff ff ff-00 00 00 00 00 00 00 00 0000 0010 ffffffff 0000 000000000000 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow11 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP AT_SETUP([OpenFlow 1.1 instruction translation]) AT_KEYWORDS([OF1.1 instruction ofp-actions]) AT_DATA([test-data], [dnl # actions=LOCAL 0004 0018 00000000 dnl 0000 0010 fffffffe 04d2 000000000000 dnl Apply-Actions non-zero padding # actions=drop # 0: 00 -> (none) # 1: 04 -> (none) # 2: 00 -> (none) # 3: 08 -> (none) # 4: 00 -> (none) # 5: 00 -> (none) # 6: 00 -> (none) # 7: 01 -> (none) 0004 0008 00000001 dnl Check that an empty Apply-Actions instruction gets dropped. # actions=drop # 0: 00 -> (none) # 1: 04 -> (none) # 2: 00 -> (none) # 3: 08 -> (none) # 4: 00 -> (none) # 5: 00 -> (none) # 6: 00 -> (none) # 7: 00 -> (none) 0004 0008 00000000 dnl Duplicate instruction type: # bad OpenFlow11 instructions: OFPBIC_DUP_INST 0004 0008 00000000 0004 0008 00000000 dnl Instructions not multiple of 8 in length. & ofp_actions|WARN|OpenFlow message instructions length 9 is not a multiple of 8 # bad OpenFlow11 instructions: OFPBIC_BAD_LEN 0004 0009 01 00000000 dnl Goto-Table instruction too long. # bad OpenFlow11 instructions: OFPBIC_BAD_LEN 0001 0010 01 000000 0000000000000000 dnl Goto-Table 1 instruction non-zero padding # actions=goto_table:1 # 7: 01 -> 00 0001 0008 01 000001 dnl Goto-Table 1 instruction go back to the previous table. # bad OpenFlow11 instructions: OFPBIC_BAD_TABLE_ID 2,0001 0008 01 000000 dnl Goto-Table 1 # actions=goto_table:1 0001 0008 01 000000 dnl Write-Metadata. # actions=write_metadata:0xfedcba9876543210 0002 0018 00000000 fedcba9876543210 ffffffffffffffff dnl Write-Metadata as Nicira extension action is transformed into instruction. # actions=write_metadata:0xfedcba9876543210 # 1: 04 -> 02 # 3: 28 -> 18 # 8: ff -> fe # 9: ff -> dc # 10: 00 -> ba # 11: 20 -> 98 # 12: 00 -> 76 # 13: 00 -> 54 # 14: 23 -> 32 # 15: 20 -> 10 # 16: 00 -> ff # 17: 16 -> ff # 18: 00 -> ff # 19: 00 -> ff # 20: 00 -> ff # 21: 00 -> ff # 22: 00 -> ff # 23: 00 -> ff # 24: fe -> (none) # 25: dc -> (none) # 26: ba -> (none) # 27: 98 -> (none) # 28: 76 -> (none) # 29: 54 -> (none) # 30: 32 -> (none) # 31: 10 -> (none) # 32: ff -> (none) # 33: ff -> (none) # 34: ff -> (none) # 35: ff -> (none) # 36: ff -> (none) # 37: ff -> (none) # 38: ff -> (none) # 39: ff -> (none) 0004 0028 00000000 ffff 0020 00002320 0016 000000000000 fedcba9876543210 ffffffffffffffff dnl Write-Metadata with mask. # actions=write_metadata:0xfedcba9876543210/0xff00ff00ff00ff00 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 dnl Write-Metadata too short. # bad OpenFlow11 instructions: OFPBIC_BAD_LEN 0002 0010 00000000 fedcba9876543210 dnl Write-Metadata too long. # bad OpenFlow11 instructions: OFPBIC_BAD_LEN 0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000 dnl Write-Metadata duplicated. # bad OpenFlow11 instructions: OFPBIC_DUP_INST 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 0002 0018 00000000 fedcba9876543210 ff00ff00ff00ff00 dnl Write-Metadata in wrong position (OpenFlow 1.1+ disregards the order dnl and OVS reorders it to the canonical order) # actions=write_metadata:0xfedcba9876543210,goto_table:1 # 1: 01 -> 02 # 3: 08 -> 18 # 4: 01 -> 00 # 8: 00 -> fe # 9: 02 -> dc # 10: 00 -> ba # 11: 18 -> 98 # 12: 00 -> 76 # 13: 00 -> 54 # 14: 00 -> 32 # 15: 00 -> 10 # 16: fe -> ff # 17: dc -> ff # 18: ba -> ff # 19: 98 -> ff # 20: 76 -> ff # 21: 54 -> ff # 22: 32 -> ff # 23: 10 -> ff # 24: ff -> 00 # 25: ff -> 01 # 26: ff -> 00 # 27: ff -> 08 # 28: ff -> 01 # 29: ff -> 00 # 30: ff -> 00 # 31: ff -> 00 0001 0008 01 000000 0002 0018 00000000 fedcba9876543210 ffffffffffffffff dnl empty Write-Actions non-zero padding # actions=write_actions(drop) # 0: 00 -> (none) # 1: 03 -> (none) # 2: 00 -> (none) # 3: 08 -> (none) # 4: 00 -> (none) # 5: 00 -> (none) # 6: 00 -> (none) # 7: 01 -> (none) 0003 0008 00000001 dnl Check that an empty Write-Actions instruction gets dropped. # actions=write_actions(drop) # 0: 00 -> (none) # 1: 03 -> (none) # 2: 00 -> (none) # 3: 08 -> (none) # 4: 00 -> (none) # 5: 00 -> (none) # 6: 00 -> (none) # 7: 00 -> (none) 0003 0008 00000000 dnl Clear-Actions too-long # bad OpenFlow11 instructions: OFPBIC_BAD_LEN 0005 0010 00000000 0000000000000000 dnl Clear-Actions non-zero padding # actions=clear_actions # 7: 01 -> 00 0005 0008 00000001 dnl Clear-Actions non-zero padding # actions=clear_actions # 4: 01 -> 00 0005 0008 01 000000 dnl Clear-Actions # actions=clear_actions 0005 0008 00000000 dnl Experimenter actions not supported yet. # bad OpenFlow11 instructions: OFPBIC_BAD_EXPERIMENTER ffff 0008 01 000000 dnl Bad instruction number (0 not assigned). # bad OpenFlow11 instructions: OFPBIC_UNKNOWN_INST 0000 0008 01 000000 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-instructions OpenFlow11 < input.txt], [0], [expout], [experr]) AT_CLEANUP dnl Our primary goal here is to verify OpenFlow 1.2-specific changes, dnl so the list of tests is short. AT_SETUP([OpenFlow 1.2 action translation]) AT_KEYWORDS([ofp-actions OF1.2]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0010 fffffffe 04d2 000000000000 # bad OpenFlow12 actions: OFPBAC_BAD_SET_MASK & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_SET_MASK): & 00000000 00 19 00 18 80 00 09 0c-00 00 00 00 12 34 00 00 & 00000010 00 00 ff ff 00 00 00 00- 0019 0018 8000090c 000000001234 00000000ffff 00000000 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow12 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP dnl Our primary goal here is to verify OpenFlow 1.3-specific changes, dnl so the list of tests is short. AT_SETUP([OpenFlow 1.3 action translation]) AT_KEYWORDS([ofp-actions OF1.3]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0010 fffffffe 04d2 000000000000 dnl Check the Nicira extension form of "move". # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802 dnl Check the ONF extension form of "copy_field". # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] ffff 0020 4f4e4600 0c80 0000 0010 0000 0000 0000 00000002 00000802 00000000 dnl Check OpenFlow v1.3.4 Conformance Test: 430.500. # bad OpenFlow13 actions: OFPBAC_BAD_SET_TYPE & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_SET_TYPE): & 00000000 00 19 00 08 80 00 fe 00-00 00 00 10 00 00 00 01 & 00000010 00 00 00 00 00 00 00 00- 0019 0008 8000fe00 000000100000 000100000000 00000000 dnl Check OpenFlow v1.3.4 Conformance Test: 430.510. # bad OpenFlow13 actions: OFPBAC_BAD_SET_LEN & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_SET_LEN): & 00000000 00 19 00 10 80 00 08 07-00 01 02 03 04 05 00 00 & 00000010 00 00 00 10 00 00 00 01- 0019 0010 80000807 000102030405 000000000010 00000001 dnl Check NSH encap (experimenter extension). # actions=encap(nsh(md_type=1)) ffff 0018 00002320 002e 0000 0001894f 0004 01 05 01 000000 dnl NSH encap with non-zero padding. # actions=encap(nsh(md_type=1)) # 21: 12 -> 00 # 22: 34 -> 00 # 23: 56 -> 00 ffff 0018 00002320 002e 0000 0001894f 0004 01 05 01 123456 dnl Check mpls encap # actions=encap(mpls) ffff 0010 00002320 002e 0000 00018847 dnl Check mpls encap # actions=encap(mpls_mc) ffff 0010 00002320 002e 0000 00018848 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow13 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP dnl Our primary goal here is to verify that OpenFlow 1.5-specific changes, dnl so the list of tests is short. AT_SETUP([OpenFlow 1.5 action translation]) AT_KEYWORDS([ofp-actions OF1.5]) AT_DATA([test-data], [dnl # actions=LOCAL 0000 0010 fffffffe 04d2 000000000000 # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] 001c 0018 0010 0000 0000 0000 00000002 00000802 00000000 # actions=set_field:00:00:00:00:12:34/00:00:00:00:ff:ff->eth_src 0019 0018 8000090c 000000001234 00000000ffff 00000000 # actions=meter:5 001d 0008 00000005 ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow15 < input.txt], [0], [expout], [experr]) AT_CHECK([cat expout | grep 'actions=' | test-ofparse.py]) AT_CLEANUP AT_SETUP([ofp-actions - inconsistent MPLS actions]) OVS_VSWITCHD_START dnl OK: Use fin_timeout action on TCP flow AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=fin_timeout(idle_timeout=1)']) AT_CHECK([echo 'tcp actions=fin_timeout(idle_timeout=1)' | test-ofparse.py]) dnl Bad: Use fin_timeout action on TCP flow that has been converted to MPLS AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=push_mpls:0x8847,fin_timeout(idle_timeout=1)'], [1], [], [dnl ovs-ofctl: none of the usable flow formats (OpenFlow10,NXM) is among the allowed flow formats (OpenFlow11) ]) AT_CHECK([echo 'tcp actions=push_mpls:0x8847,fin_timeout(idle_timeout=1)' | test-ofparse.py]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([reg_load <-> set_field translation corner case]) AT_KEYWORDS([ofp-actions]) OVS_VSWITCHD_START dnl In OpenFlow 1.3, set_field always sets all the bits in the field, dnl but when we translate to NXAST_LOAD we need to only set the bits that dnl actually exist (e.g. mpls_label only has 20 bits) otherwise OVS rejects dnl the "load" action as invalid. Check that we do this correctly. AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 mpls,actions=set_field:10-\>mpls_label]) AT_CHECK([ovs-ofctl -O OpenFlow10 dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: mpls actions=load:0xa->OXM_OF_MPLS_LABEL[[]] ]) AT_CHECK([echo 'mpls actions=set_field:10->mpls_label' | test-ofparse.py]) AT_CHECK([echo 'mpls actions=load:0xa->OXM_OF_MPLS_LABEL[[]]'| test-ofparse.py]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([enqueue action for OF1.1+]) AT_KEYWORDS([ofp-actions]) OVS_VSWITCHD_START dnl OpenFlow 1.0 has an "enqueue" action. For OpenFlow 1.1+, we translate dnl it to a series of actions that accomplish the same thing. AT_CHECK([ovs-ofctl -O OpenFlow10 add-flow br0 'actions=enqueue(123,456)']) AT_CHECK([echo 'actions=enqueue(123,456)' | test-ofparse.py]) AT_CHECK([ovs-ofctl -O OpenFlow10 dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: actions=enqueue:123:456 ]) AT_CHECK([echo 'actions=enqueue:123:456' | test-ofparse.py]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.3): reset_counts actions=set_queue:456,output:123,pop_queue ]) AT_CHECK([echo 'actions=set_queue:456,output:123,pop_queue' | test-ofparse.py]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mod_nw_ttl action for OF1.0]) AT_KEYWORDS([ofp-actions]) OVS_VSWITCHD_START dnl OpenFlow 1.1+ have a mod_nw_ttl action. For OpenFlow 1.0, we translate dnl it to an Open vSwitch extension. AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 'ip,actions=mod_nw_ttl:123']) AT_CHECK([ovs-ofctl -O OpenFlow10 dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: ip actions=load:0x7b->NXM_NX_IP_TTL[[]] ]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.1): ip actions=mod_nw_ttl:123 ]) AT_CHECK([echo 'ip,actions=mod_nw_ttl:123' | test-ofparse.py]) AT_CHECK([echo 'ip actions=load:0x7b->NXM_NX_IP_TTL[[]]' | test-ofparse.py]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mod_nw_ecn action translation]) AT_KEYWORDS([ofp-actions]) OVS_VSWITCHD_START dnl OpenFlow 1.1, but no other version, has a "mod_nw_ecn" action. dnl Check that we translate it properly for OF1.0 and OF1.2. dnl (OF1.3+ should be the same as OF1.2.) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 'ip,actions=mod_nw_ecn:2']) AT_CHECK([echo 'ip,actions=mod_nw_ecn:2' | test-ofparse.py]) AT_CHECK([ovs-ofctl -O OpenFlow10 dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: ip actions=load:0x2->NXM_NX_IP_ECN[[]] ]) AT_CHECK([echo 'ip actions=load:0x2->NXM_NX_IP_ECN[[]]' | test-ofparse.py]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.1): ip actions=mod_nw_ecn:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.2): ip actions=set_field:2->nw_ecn ]) AT_CHECK([echo 'ip actions=set_field:2->nw_ecn' | test-ofparse.py]) dnl Check that OF1.2+ set_field to set ECN is translated into the OF1.1 dnl mod_nw_ecn action. dnl dnl We don't do anything equivalent for OF1.0 reg_load because we prefer dnl that anything that comes in as reg_load gets translated back to reg_load dnl on output. Perhaps this is somewhat inconsistent but it's what OVS dnl has done for multiple versions. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip,actions=set_field:2->ip_ecn']) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.1): ip actions=mod_nw_ecn:2 ]) dnl Check that OF1.2+ set_field to set ECN is translated for earlier OF dnl versions. AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow10 add-flow br0 'ip,actions=set_field:2->ip_ecn']) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 'ip,actions=set_field:2->ip_ecn']) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([action parsing errors]) bad_action () { AT_CHECK_UNQUOTED([ovs-ofctl parse-flow "actions=$1"], [1], [], [ovs-ofctl: $2 ]) } # output bad_action 'output(port=xyzzy,max_len=5)' \ "output to unknown truncate port: xyzzy" bad_action 'output(port=all,max_len=64)' \ "output to unsupported truncate port: all" bad_action 'output(port=local,max_len=64,foo=bar)' \ "invalid key 'foo' in output_trunc argument" bad_action 'output(port=local,max_len=5)' \ "max_len 5 is less than the minimum value 14" # controller bad_action 'controller(reason=asdf)' 'unknown reason "asdf"' bad_action 'controller(foo=bar)' 'unknown key "foo" parsing controller action' bad_action 'controller(userdata=123x456)' \ 'bad hex digit in `controller'\'' action `userdata'\' # enqueue bad_action 'enqueue:123' \ '"enqueue" syntax is "enqueue:PORT:QUEUE" or "enqueue(PORT,QUEUE)"' bad_action 'enqueue:asdf:123' 'asdf: enqueue to unknown port' # bundle bad_action 'bundle:123' '123: not enough arguments to bundle action' bad_action 'bundle(symmetric_l4,60,hrw,ofport,ports:1,2,3,4,5)' \ "symmetric_l4,60,hrw,ofport,ports:1,2,3,4,5: missing member delimiter, expected \`members', got \`ports'" bad_action 'bundle(symmetric_l4,60,hrw,ofport,members:xyzzy,2,3,4,5)' \ 'xyzzy: bad port number' bad_action 'bundle(asymmetric_l4,60,hrw,ofport,members:1,2,3,4,5)' \ "asymmetric_l4,60,hrw,ofport,members:1,2,3,4,5: unknown fields \`asymmetric_l4'" bad_action 'bundle(symmetric_l4,60,hrt,ofport,members:1,2,3,4,5)' \ "symmetric_l4,60,hrt,ofport,members:1,2,3,4,5: unknown algorithm \`hrt'" bad_action 'bundle(symmetric_l4,60,hrw,odpport,members:1,2,3,4,5)' \ "symmetric_l4,60,hrw,odpport,members:1,2,3,4,5: unknown member_type \`odpport'" bad_action 'bundle_load(symmetric_l4,60,hrw,ofport,actset_output,members:1,2,3,4,5)' \ "symmetric_l4,60,hrw,ofport,actset_output,members:1,2,3,4,5: experimenter OXM field 'actset_output' not supported" # mod_vlan_vid bad_action 'mod_vlan_vid:6000' '6000: not a valid VLAN VID' # mod_vlan_pcp bad_action 'mod_vlan_pcp:8' '8: not a valid VLAN PCP' # push_vlan bad_action 'push_vlan(0x1234)' '0x1234: not a valid VLAN ethertype' # mod_nw_tos bad_action 'mod_nw_tos(1)' '1: not a valid TOS' # mod_nw_ecn bad_action 'mod_nw_ecn(5)' '5: not a valid ECN' # set_field bad_action 'set_field:1' "1: missing \`->'" bad_action 'set_field:1->' "1->: missing field name following \`->'" bad_action 'set_field:1->x' 'x is not a valid OXM field name' bad_action 'set_field:1->eth_type' 'eth_type is read-only' bad_action 'set_field:1->eth_src' '1: invalid Ethernet address' bad_action 'set_field:0xffff->ip_dscp' '0xffff: value too large for 1-byte field ip_dscp' bad_action 'set_field:0xff->ip_dscp' '0xff is not a valid value for field ip_dscp' # reg_load bad_action 'load:xyzzy->eth_src' 'xyzzy->eth_src: cannot parse integer value' bad_action 'load:0xff->eth_src[[1..5]]' '0xff->eth_src[[1..5]]: value 00:00:00:00:00:ff does not fit into 5 bits' # push/pop bad_action 'push(eth_dst[[]]x)' 'x: trailing garbage following push or pop' # dec_ttl bad_action 'dec_ttl(,)' 'dec_ttl_cnt_ids: expected at least one controller id.' # set_mpls_label bad_action 'set_mpls_label' 'set_mpls_label: expected label.' # set_mpls_label oversized bad_action 'set_mpls_label(0x100000)' '0x100000: not a valid MPLS label' # set_mpls_tc bad_action 'set_mpls_tc' 'set_mpls_tc: expected tc.' # set_mpls_tc oversized bad_action 'set_mpls_tc(8)' '8: not a valid MPLS TC' # set_mpls_ttl bad_action 'set_mpls_ttl' 'set_mpls_ttl: expected ttl.' # set_mpls_ttl oversized bad_action 'set_mpls_ttl(256)' 'invalid MPLS TTL "256"' # fin_timeout bad_action 'fin_timeout(foo=bar)' "invalid key 'foo' in 'fin_timeout' argument" # encap bad_action 'encap(,)' 'Missing encap hdr: ,' bad_action 'encap(x(y))' 'Encap hdr not supported: y' bad_action 'encap(nsh(type=1))' 'Invalid property: type' bad_action 'encap(nsh(md_type))' 'Value missing for encap property' bad_action 'encap(nsh(md_type=3))' 'invalid md_type' bad_action 'encap(nsh(tlv(,,)))' 'Invalid NSH TLV header: ,,' # decap bad_action 'decap(packet_type(x))' 'Missing packet_type attribute ns' bad_action 'decap(packet_type(ns=99))' 'Unsupported ns value: 99' bad_action 'decap(packet_type(ns=0))' 'Missing packet_type attribute type' bad_action 'decap(foo=bar)' 'Invalid decap argument: foo' # resubmit bad_action 'resubmit(asdf)' 'asdf: resubmit to unknown port' bad_action 'resubmit(,asdf)' 'asdf: resubmit to unknown table' bad_action 'resubmit(1,2,xyzzy)' 'xyzzy: unknown parameter' bad_action 'resubmit(in_port,255)' 'at least one "in_port" or "table" must be specified on resubmit' # learn bad_action 'learn(load:123->actset_output)' \ "123->actset_output: experimenter OXM field 'actset_output' not supported" bad_action 'learn(load:1234->eth_dst[[0..5]])' \ '1234->eth_dst[[0..5]]: value does not fit into 6 bits' bad_action 'learn(actset_output=0x1000)' \ "actset_output=0x1000: experimenter OXM field 'actset_output' not supported" bad_action 'learn(eth_type[[5]]=xyzzy)' \ "eth_type[[5]]=xyzzy: eth_type[[5]] value xyzzy cannot be parsed as a subfield (xyzzy: unknown field \`xyzzy') or an immediate value (eth_type[[5]]=xyzzy: cannot parse integer value)" bad_action 'learn(eth_type[[0]]=eth_type[[1..2]])' \ 'eth_type[[0]]=eth_type[[1..2]]: bit widths of eth_type[[0]] (2) and eth_type[[1..2]] (1) differ' bad_action 'learn(load:->)' "load: missing source before \`->' in \`->'" bad_action 'learn(load:x)' "load: missing \`->' in \`x'" bad_action 'learn(load:1x->foo)' "load: garbage before \`->' in \`1x->foo'" bad_action 'learn(foo)' 'foo: unknown keyword foo' bad_action 'learn(table=foo)' 'unknown table "foo"' bad_action 'learn(table=255)' "table=255: table id 255 not valid for \`learn' action" bad_action 'learn(result_dst=tcp_flags)' 'tcp_flags is read-only' bad_action 'learn(result_dst=eth_dst)' "result_dst in 'learn' action must be a single bit" # conjunction bad_action 'conjunction(1, 1/1)' 'conjunction must have at least 2 clauses' bad_action 'conjunction(1, 1/65)' 'conjunction must have at most 64 clauses' bad_action 'conjunction(1, 0/2)' 'clause index must be positive' bad_action 'conjunction(1, 3/2)' \ 'clause index must be less than or equal to number of clauses' # multipath bad_action 'multipath(1,2,3,4)' \ '1,2,3,4: not enough arguments to multipath action' bad_action 'multipath(xyzzy,50,modulo_n,1,0,NXM_NX_REG0[[]])' \ "xyzzy,50,modulo_n,1,0,NXM_NX_REG0[[]]: unknown fields \`xyzzy'" bad_action 'multipath(eth_src,50,fubar,1,0,NXM_NX_REG0[[]])' \ "eth_src,50,fubar,1,0,NXM_NX_REG0[[]]: unknown algorithm \`fubar'" bad_action 'multipath(eth_src,50,modulo_n,0,0,NXM_NX_REG0[[]])' \ "eth_src,50,modulo_n,0,0,NXM_NX_REG0[[]]: n_links 0 is not in valid range 1 to 65536" bad_action 'multipath(eth_src,50,modulo_n,1024,0,actset_output)' \ "eth_src,50,modulo_n,1024,0,actset_output: experimenter OXM field 'actset_output' not supported" bad_action 'multipath(eth_src,50,modulo_n,1024,0,NXM_NX_REG0[[0..7]])' \ "eth_src,50,modulo_n,1024,0,NXM_NX_REG0[[0..7]]: 8-bit destination field has 256 possible values, less than specified n_links 1024" # note bad_action 'note:x' "bad hex digit in \`note' argument" # unroll_xlate bad_action 'unroll_xlate' "UNROLL is an internal action that shouldn't be used via OpenFlow" # sample bad_action 'sample(probability=0)' 'invalid probability value "0"' bad_action 'sample(sampling_port=asdf)' 'asdf: unknown port' bad_action 'sample(probability=12345,obs_domain_id=NXM_NX_CT_LABEL[[5..40]])' \ 'size of obs_domain_id field (36) exceeds maximum (32)' bad_action 'sample(probability=12345,obs_point_id=NXM_NX_CT_LABEL[[0..32]])' \ 'size of obs_point_id field (33) exceeds maximum (32)' bad_action 'sample(foo=bar)' 'invalid key "foo" in "sample" argument' bad_action 'sample' 'non-zero "probability" must be specified on sample' # ct bad_action 'ct(table=asdf)' 'unknown table asdf' bad_action 'ct(table=255)' 'invalid table 0xff' bad_action 'ct(foo=bar)' 'invalid argument to "ct" action: `foo'\' bad_action 'ct(force)' '"force" flag requires "commit" flag.' # nat bad_action 'nat(src=1.2.3.4x)' 'garbage (x) after nat range "1.2.3.4x" (pos: 7)' bad_action 'nat(src=1.2.3.4-0.1.2.3)' 'invalid nat range "1.2.3.4-0.1.2.3"' bad_action 'nat(foo=bar)' 'invalid key "foo" in "nat" argument' bad_action 'nat(src=1.2.3.4,dst=2.3.4.5)' 'May only specify one of "src" or "dst".' bad_action 'nat(persistent)' 'Flags allowed only with "src" or "dst".' bad_action 'nat(src=1.2.3.4,hash,random)' 'Both "hash" and "random" are not allowed.' # check_pkt_larger bad_action 'check_pkt_larger(1500)->reg0' \ 'Only 1-bit destination field is allowed' # goto_table bad_action 'goto_table:asdf' 'unknown table "asdf"' # nested actions bad_action 'set_field:1234->ct_mark' \ "cannot set CT fields outside of ct action" bad_action 'nat' 'Cannot have NAT action outside of "ct" action' bad_action 'ct(commit,exec(push_vlan(0x8100)))' \ "ct action doesn't support nested action push_vlan" bad_action 'ct(commit,exec(set_field:12:34:56:78:9a:bc->eth_dst))' \ "ct action doesn't support nested modification of eth_dst" bad_action 'conjunction(1, 2/3),ct_clear' \ '"conjunction" actions may be used along with "note" but not any other kind of action (such as the "ct_clear" action used here)' # instructions bad_action 'goto_table:5,goto_table:5' \ 'duplicate goto_table instruction not allowed, for OpenFlow 1.1+ compatibility' bad_action 'goto_table:5,clone()' \ 'invalid instruction ordering: apply_actions must appear before goto_table, for OpenFlow 1.1+ compatibility' AT_CHECK([ovs-ofctl parse-group 'group_id=1,type=select,bucket=actions=clear_actions'], [1], [], [ovs-ofctl: clear_actions instruction not allowed here ]) # ofpacts_parse__() bad_action 'apply_actions' 'apply_actions is the default instruction' bad_action 'xyzzy' 'unknown action xyzzy' bad_action 'drop,3' '"drop" must not be accompanied by any other action or instruction' # Too many actions writes=$(printf 'write_actions(%.0s' $(seq 100)) bad_action "${writes}" 'Action nested too deeply' outputs=$(printf '1,%.0s' $(seq 4096)) bad_action "${outputs}" 'input too big' AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofp-errors.at000066400000000000000000000225431514270232600225540ustar00rootroot00000000000000AT_BANNER([ofp-errors tests]) AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.0]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 010100170000000000000001657874726120646174610a], [0], [dnl OFPT_ERROR (xid=0x0): OFPHFC_EPERM extra data\012 ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.1]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 020100170000000000000001657874726120646174610a], [0], [dnl OFPT_ERROR (OF1.1) (xid=0x0): OFPHFC_EPERM extra data\012 ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 0101001400000000000100060105ccddeeff0011], [0], [dnl OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN OFPT_FEATURES_REQUEST (xid=0xeeff0011): (***truncated to 8 bytes from 52445***) 00000000 01 05 cc dd ee ff 00 11- |........ | ]) AT_CLEANUP AT_SETUP([OFPT_ERROR prints type of truncated inner messages]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print "0101004c092529d500010006 \ 01 06 00 e0 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 02 00 00 00 00 00 00 87 00 00 0f ff \ ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01"], [0], [dnl OFPT_ERROR (xid=0x92529d5): OFPBRC_BAD_LEN OFPT_FEATURES_REPLY (xid=0x1): (***truncated to 64 bytes from 224***) 00000000 01 06 00 e0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....| 00000010 00 00 01 00 02 00 00 00-00 00 00 87 00 00 0f ff |................| 00000020 ff fe 50 54 00 00 00 01-62 72 30 00 00 00 00 00 |..PT....br0.....| 00000030 00 00 00 00 00 00 00 00-00 00 00 01 00 00 00 01 |................| ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.0]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl OFPT_ERROR (xid=0x55555555): OFPBMC_BAD_PREREQ OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.1]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print '0201001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.1) (xid=0x55555555): OFPBMC_BAD_PREREQ OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CLEANUP dnl Error type 3, code 1 is OFPFMFC_OVERLAP in OF1.0 dnl and OFPBIC_UNSUP_INST in OF1.1, so check that value in both versions. AT_SETUP([OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 0101001400000000000300010106ccddeeff0011], [0], [dnl OFPT_ERROR (xid=0x0): OFPFMFC_OVERLAP OFPT_FEATURES_REPLY (xid=0xeeff0011): (***truncated to 8 bytes from 52445***) 00000000 01 06 cc dd ee ff 00 11- |........ | ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 0201001400000000000300010206ccddeeff0011], [0], [dnl OFPT_ERROR (OF1.1) (xid=0x0): OFPBIC_UNSUP_INST OFPT_FEATURES_REPLY (OF1.1) (xid=0xeeff0011): (***truncated to 8 bytes from 52445***) 00000000 02 06 cc dd ee ff 00 11- |........ | ]) AT_CLEANUP AT_SETUP([OFPT_ERROR with type OFPBIC_DUP_INST - OF1.4]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print 0501001400000000000300090206ccddeeff0011], [0], [dnl OFPT_ERROR (OF1.4) (xid=0x0): OFPBIC_DUP_INST OFPT_FEATURES_REPLY (OF1.1) (xid=0xeeff0011): (***truncated to 8 bytes from 52445***) 00000000 02 06 cc dd ee ff 00 11- |........ | ]) AT_CLEANUP dnl OF1.1 had OFPBIC_UNSUP_EXP_INST as 3,5. dnl OF1.2 broke it into OFPBIC_BAD_EXPERIMENTER as 3,5 dnl and OFPBIC_BAD_EXT_TYPE as 3,6. dnl Thus, for OF1.1 we translate both of the latter error codes into 3,5. AT_SETUP([encoding OFPBIC_* experimenter errors]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXPERIMENTER], [0], [dnl OpenFlow 1.1: vendor 0, type 3, code 5 OpenFlow 1.2: vendor 0, type 3, code 5 OpenFlow 1.3: vendor 0, type 3, code 5 OpenFlow 1.4: vendor 0, type 3, code 5 OpenFlow 1.5: vendor 0, type 3, code 5 ]) AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXP_TYPE], [0], [dnl OpenFlow 1.1: vendor 0, type 3, code 5 OpenFlow 1.2: vendor 0, type 3, code 6 OpenFlow 1.3: vendor 0, type 3, code 6 OpenFlow 1.4: vendor 0, type 3, code 6 OpenFlow 1.5: vendor 0, type 3, code 6 ]) AT_CLEANUP dnl The "bad role" error was a Nicira extension in OpenFlow 1.0 and 1.1. dnl It was adopted as an official error code in OpenFlow 1.2. AT_SETUP([encoding errors extension that became official]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK( [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0100000812345678], [0], [dnl 00000000 01 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 01 02 01 01 00 00 08-12 34 56 78 ]) AT_CHECK( [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0200000812345678], [0], [dnl 00000000 02 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 01 02 01 02 00 00 08-12 34 56 78 ]) AT_CHECK( [ovs-ofctl encode-error-reply OFPRRFC_BAD_ROLE 0300000812345678], [0], [dnl 00000000 03 01 00 14 12 34 56 78-00 0b 00 02 03 00 00 08 00000010 12 34 56 78 ]) AT_CLEANUP AT_SETUP([decoding OFPBIC_* experimenter errors]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print '0201001455555555 00030005 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.1) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030005 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030006 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CLEANUP AT_SETUP([decoding experimenter errors]) AT_KEYWORDS([ofp-print ofp-errors]) AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010203 0102000811111111'], [0], [dnl OFPT_ERROR (xid=0x55555555): NXBRC_MUST_BE_ZERO OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CHECK([ovs-ofctl ofp-print '0201001c55555555 b0c20000 0000232000010203 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.1) (xid=0x55555555): NXBRC_MUST_BE_ZERO OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CHECK([ovs-ofctl ofp-print '0301001855555555 ffff0004 00002320 0102000811111111'], [0], [dnl OFPT_ERROR (OF1.2) (xid=0x55555555): NXBRC_MUST_BE_ZERO OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload ]) AT_CHECK([ovs-ofctl ofp-print '0301001812345678 ffff0a28 4f4e4600 0300000812345678'], [0], [dnl OFPT_ERROR (OF1.2) (xid=0x12345678): OFPBIC_DUP_INST OFPT_HELLO (OF1.2) (xid=0x12345678): version bitmap: 0x01, 0x02, 0x03 ]) AT_CHECK([ovs-ofctl ofp-print '0401001812345678 ffff0a28 4f4e4600 0400000812345678'], [0], [dnl OFPT_ERROR (OF1.3) (xid=0x12345678): OFPBIC_DUP_INST OFPT_HELLO (OF1.3) (xid=0x12345678): version bitmap: 0x01, 0x02, 0x03, 0x04 ]) AT_CHECK([ovs-ofctl ofp-print '0501001412345678 00030009 0500000812345678'], [0], [dnl OFPT_ERROR (OF1.4) (xid=0x12345678): OFPBIC_DUP_INST OFPT_HELLO (OF1.4) (xid=0x12345678): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05 ]) AT_CLEANUP AT_SETUP([encoding experimenter errors]) AT_KEYWORDS([ofp-print ofp-errors]) # Demonstrate that a Nicira extension error gets encoded correctly # using the Nicira error extension format in OF1.0 and OF1.1, and # correctly using the standard experimenter format in OF1.2. AT_CHECK( [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 0100000812345678], [0], [dnl 00000000 01 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 01 02 03 01 00 00 08-12 34 56 78 ]) AT_CHECK( [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 0200000812345678], [0], [dnl 00000000 02 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 01 02 03 02 00 00 08-12 34 56 78 ]) AT_CHECK( [ovs-ofctl encode-error-reply NXBRC_MUST_BE_ZERO 0300000812345678], [0], [dnl 00000000 03 01 00 18 12 34 56 78-ff ff 00 04 00 00 23 20 00000010 03 00 00 08 12 34 56 78 ]) # Check that OFPERR_OFPBIC_DUP_INST is: # - not encodable in OF1.0 (OF1.0 doesn't have instructions, after all). # - encoded as a Nicira extension in OF1.1. # - encoded as an ONF extension in OF1.2 and OF1.3. # - encoded in the standard form in OF1.4. AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' encode-error-reply OFPBIC_DUP_INST 0100000812345678], [0], [dnl 00000000 01 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 01 02 09 01 00 00 08-12 34 56 78 ], [ofp_errors|ERR|cannot encode OFPBIC_DUP_INST for OpenFlow 1.0 ]) AT_CHECK([ovs-ofctl encode-error-reply OFPBIC_DUP_INST 0200000812345678], [0], [00000000 02 01 00 1c 12 34 56 78-b0 c2 00 00 00 00 23 20 00000010 00 03 01 00 02 00 00 08-12 34 56 78 ]) AT_CHECK([ovs-ofctl encode-error-reply OFPBIC_DUP_INST 0300000812345678], [0], [00000000 03 01 00 18 12 34 56 78-ff ff 0a 28 4f 4e 46 00 00000010 03 00 00 08 12 34 56 78 ]) AT_CHECK([ovs-ofctl encode-error-reply OFPBIC_DUP_INST 0400000812345678], [0], [00000000 04 01 00 18 12 34 56 78-ff ff 0a 28 4f 4e 46 00 00000010 04 00 00 08 12 34 56 78 ]) AT_CHECK([ovs-ofctl encode-error-reply OFPBIC_DUP_INST 0500000812345678], [0], [00000000 05 01 00 14 12 34 56 78-00 03 00 09 05 00 00 08 00000010 12 34 56 78 ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofp-print.at000066400000000000000000005116701514270232600224000ustar00rootroot00000000000000AT_BANNER([ofp-print]) AT_SETUP([empty]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print ''], [0], [OpenFlow message is empty ]) AT_CLEANUP AT_SETUP([too short]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print aabb], [0], [dnl OpenFlow packet too short (only 2 bytes): 00000000 aa bb |.. | ]) AT_CLEANUP AT_SETUP([wrong OpenFlow version]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print 00bb0008eeff0011], [0], [dnl ***decode error: OFPBRC_BAD_TYPE*** 00000000 00 bb 00 08 ee ff 00 11- |........ | ], [ofp_msgs|WARN|unknown OpenFlow message (version 0, type 187) ]) AT_CLEANUP AT_SETUP([truncated message]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0110ccddeeff0011], [0], [dnl (***truncated to 8 bytes from 52445***) 00000000 01 10 cc dd ee ff 00 11- |........ | ]) AT_CLEANUP AT_SETUP([message only uses part of buffer]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 01100009eeff00112233], [0], [dnl (***only uses 9 bytes out of 10***) 00000000 01 10 00 09 ee ff 00 11-22 33 |........"3 | ]) # " AT_CLEANUP AT_SETUP([OFPT_HELLO - ordinary]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0100000800000000], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x01 ]) AT_CLEANUP AT_SETUP([OFPT_HELLO with extra data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0100001300000000657874726120646174610a], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x01 unknown data in hello: 00000000 01 00 00 13 00 00 00 00-65 78 74 72 61 20 64 61 |........extra da| 00000010 74 61 0a |ta. | ]) AT_CLEANUP AT_SETUP([OFPT_HELLO with version bitmap]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 f0"], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x04, 0x05, 0x06, 0x07 ]) AT_CLEANUP AT_SETUP([OFPT_HELLO with version bitmap and extra data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 00 00 1b 00 00 00 00 ff ff 00 06 01 02 00 00 \ 00 01 00 08 00 00 00 f0 61 62 63"], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x04, 0x05, 0x06, 0x07 unknown data in hello: 00000000 01 00 00 1b 00 00 00 00-ff ff 00 06 01 02 00 00 |................| 00000010 00 01 00 08 00 00 00 f0-61 62 63 |........abc | ]) AT_CLEANUP AT_SETUP([OFPT_HELLO with higher than supported version]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "0f 00 00 08 00 00 00 00"], [0], [dnl OFPT_HELLO (OF 0x0f) (xid=0x0): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ]) AT_CHECK([ovs-ofctl ofp-print "40 00 00 08 00 00 00 00"], [0], [dnl OFPT_HELLO (OF 0x40) (xid=0x0): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f ]) AT_CHECK([ovs-ofctl ofp-print "3f 00 00 18 00 00 00 00 00 01 00 0c aa aa aa aa aa aa aa aa 00 00 00 00"], [0], [dnl OFPT_HELLO (OF 0x3f) (xid=0x0): version bitmap: 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f ]) AT_CLEANUP AT_SETUP([OFPT_HELLO with contradictory version bitmaps]) AT_KEYWORDS([ofp-print]) dnl Bitmap claims support for no versions at all. AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 00"], [0], [OFPT_HELLO (xid=0x0): version bitmap: 0x01 unknown data in hello: 00000000 01 00 00 10 00 00 00 00-00 01 00 08 00 00 00 00 |................| ], [dnl ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f) ]) dnl Bitmap claims support for only versions above 0x1f. AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "3f 00 00 18 00 00 00 00 00 01 00 0c 00 00 00 00 aa aa aa aa 00 00 00 00"], [0], [OFPT_HELLO (OF 0x3f) (xid=0x0): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f unknown data in hello: 00000000 3f 00 00 18 00 00 00 00-00 01 00 0c 00 00 00 00 |?...............| 00000010 aa aa aa aa 00 00 00 00- |........ | ], [dnl ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f) ]) dnl Bitmap claims support for nonexistent version 0x00. AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 f1"], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x04, 0x05, 0x06, 0x07 ], [dnl ofp_util|WARN|peer claims to support invalid OpenFlow version 0x00 ]) dnl Bitmap claims support for only nonexistent version 0x00. AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 01"], [0], [dnl OFPT_HELLO (xid=0x0): version bitmap: 0x01 unknown data in hello: 00000000 01 00 00 10 00 00 00 00-00 01 00 08 00 00 00 01 |................| ], [dnl ofp_util|WARN|peer claims to support invalid OpenFlow version 0x00 ofp_util|WARN|peer does not support any OpenFlow version (between 0x01 and 0x1f) ]) AT_CLEANUP dnl OFPT_ERROR tests are in ofp-errors.at. AT_SETUP([OFPT_ECHO_REQUEST, empty payload]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 02 00 08 00 00 00 01'], [0], [dnl OFPT_ECHO_REQUEST (xid=0x1): 0 bytes of payload ]) AT_CLEANUP AT_SETUP([OFPT_ECHO_REQUEST, 5-byte payload]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '0102000d00000001 25 53 54 1a 9d'], [0], [dnl OFPT_ECHO_REQUEST (xid=0x1): 5 bytes of payload 00000000 25 53 54 1a 9d |%ST.. | ]) AT_CLEANUP AT_SETUP([OFPT_ECHO_REPLY, empty payload]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 03 00 08 00 00 00 01'], [0], [dnl OFPT_ECHO_REPLY (xid=0x1): 0 bytes of payload ]) AT_CLEANUP AT_SETUP([OFPT_ECHO_REPLY, 5-byte payload]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '0103000d0000000ba330efaf9e'], [0], [dnl OFPT_ECHO_REPLY (xid=0xb): 5 bytes of payload 00000000 a3 30 ef af 9e |.0... | ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REQUEST]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '0105000800000001'], [0], [dnl OFPT_FEATURES_REQUEST (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 06 00 e0 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 02 00 00 00 00 00 00 87 00 00 0f ff \ ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 03 50 54 00 00 00 01 65 74 68 30 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ 00 02 50 54 00 00 00 03 65 74 68 32 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ 00 01 50 54 00 00 00 02 65 74 68 31 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ "], [0], [dnl OFPT_FEATURES_REPLY (xid=0x1): dpid:0000505400000001 n_tables:2, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(eth1): addr:50:54:00:00:00:02 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max 2(eth2): addr:50:54:00:00:00:03 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max LOCAL(br0): addr:50:54:00:00:00:01 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 06 00 dc 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 02 00 00 00 00 00 00 87 00 00 0f ff \ ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 03 50 54 00 00 00 01 65 74 68 30 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ 00 02 50 54 00 00 00 03 65 74 68 32 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ 00 01 50 54 00 00 00 02 65 74 68 31 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 08 00 00 02 8f 00 00 02 8f \ "], [0], [dnl ***decode error: OFPBRC_BAD_LEN*** 00000000 01 06 00 dc 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....| 00000010 00 00 01 00 02 00 00 00-00 00 00 87 00 00 0f ff |................| 00000020 ff fe 50 54 00 00 00 01-62 72 30 00 00 00 00 00 |..PT....br0.....| 00000030 00 00 00 00 00 00 00 00-00 00 00 01 00 00 00 01 |................| 00000040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................| 00000050 00 03 50 54 00 00 00 01-65 74 68 30 00 00 00 00 |..PT....eth0....| 00000060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................| 00000070 00 00 02 08 00 00 02 8f-00 00 02 8f 00 00 00 00 |................| 00000080 00 02 50 54 00 00 00 03-65 74 68 32 00 00 00 00 |..PT....eth2....| 00000090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................| 000000a0 00 00 02 08 00 00 02 8f-00 00 02 8f 00 00 00 00 |................| 000000b0 00 01 50 54 00 00 00 02-65 74 68 31 00 00 00 00 |..PT....eth1....| 000000c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................| 000000d0 00 00 02 08 00 00 02 8f-00 00 02 8f |............ | ], [stderr]) AT_CHECK([sed 's/.*|//' stderr], [0], [dnl received OFPT_FEATURES_REPLY with incorrect length 220 (must be exactly 32 bytes or longer by an integer multiple of 48 bytes) ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 02 00 00 00 00 00 00 87 00 00 00 00 \ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \ 62 72 30 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \ 00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \ 00 00 28 0f 00 00 00 00 00 01 86 a0 00 01 86 a0 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.1) (xid=0x1): dpid:0000505400000001 n_tables:2, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS ARP_MATCH_IP 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max LOCAL(br0): addr:50:54:00:00:00:01 config: PORT_DOWN state: LINK_DOWN speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 06 00 90 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 02 00 00 00 00 00 00 87 00 00 00 00 \ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \ 62 72 30 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \ 00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \ "], [0], [dnl ***decode error: OFPBRC_BAD_LEN*** 00000000 02 06 00 90 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....| 00000010 00 00 01 00 02 00 00 00-00 00 00 87 00 00 00 00 |................| 00000020 ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......| 00000030 62 72 30 00 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............| 00000040 00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................| 00000050 00 00 00 00 00 00 00 00-00 01 86 a0 00 01 86 a0 |................| 00000060 00 00 00 03 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......| 00000070 65 74 68 30 00 00 00 00-00 00 00 00 00 00 00 00 |eth0............| 00000080 00 00 00 00 00 00 00 00-00 00 20 08 00 00 28 0f |.......... ...@{:@.| ], [stderr]) AT_CHECK([sed 's/.*|//' stderr], [0], [dnl received OFPT_FEATURES_REPLY with incorrect length 144 (must be exactly 32 bytes or longer by an integer multiple of 64 bytes) ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \ 62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \ 00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \ 00 00 28 0f 00 00 00 00 00 01 86 a0 00 01 86 a0 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.2) (xid=0x1): dpid:0000505400000001 n_tables:255, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max LOCAL(br0): addr:50:54:00:00:00:01 config: PORT_DOWN state: LINK_DOWN speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \ 62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \ 00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.2) (xid=0x1): (***truncated to 144 bytes from 160***) 00000000 03 06 00 a0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....| 00000010 00 00 01 00 ff 00 00 00-00 00 01 77 00 00 00 00 |...........w....| 00000020 ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......| 00000030 62 72 30 0a 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............| 00000040 00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................| 00000050 00 00 00 00 00 00 00 00-00 01 86 a0 00 01 86 a0 |................| 00000060 00 00 00 03 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......| 00000070 65 74 68 30 00 00 00 00-00 00 00 00 00 00 00 00 |eth0............| 00000080 00 00 00 00 00 00 00 00-00 00 20 08 00 00 28 0f |.......... ...@{:@.| ], [stderr]) AT_CHECK([sed 's/.*|//' stderr], [0], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 00 00 00 00 00 01 77 00 00 00 00 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.3) (xid=0x1): dpid:0000505400000001 n_tables:255, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 00 00 00 00 00 07 6f 00 00 00 00 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.4) (xid=0x1): dpid:0000505400000001 n_tables:255, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS IP_REASM QUEUE_STATS PORT_BLOCKED BUNDLES FLOW_MONITORING ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - OF1.5]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 06 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 00 00 00 00 00 07 6f 00 00 00 00 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.5) (xid=0x1): dpid:0000505400000001 n_tables:255, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS IP_REASM QUEUE_STATS PORT_BLOCKED BUNDLES FLOW_MONITORING ]) AT_CLEANUP AT_SETUP([OFPT_FEATURES_REPLY - with auxiliary_id - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 06 00 20 00 00 00 01 00 00 50 54 00 00 00 01 \ 00 00 01 00 ff 01 00 00 00 00 01 77 00 00 00 00 \ "], [0], [dnl OFPT_FEATURES_REPLY (OF1.3) (xid=0x1): dpid:0000505400000001 n_tables:255, n_buffers:256, auxiliary_id:1 capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED ]) AT_CLEANUP AT_SETUP([OFPT_GET_CONFIG_REQUEST]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '0107000800000001'], [0], [dnl OFPT_GET_CONFIG_REQUEST (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_GET_CONFIG_REPLY, most common form]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 08 00 0c 00 00 00 03 00 00 00 00'], [0], [dnl OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0 ]) AT_CLEANUP AT_SETUP([OFPT_GET_CONFIG_REPLY, frags and miss_send_len]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 08 00 0c 00 00 00 03 00 02 00 ff'], [0], [dnl OFPT_GET_CONFIG_REPLY (xid=0x3): frags=reassemble miss_send_len=255 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0a 00 4e 00 00 00 00 00 00 01 11 00 3c 00 03 \ 00 00 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \ 45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \ 50 02 02 00 26 e8 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=10031,tp_dst=0,tcp_flags=syn tcp_csum:26e8 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.0, with hex output of packet data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0a 00 4e 00 00 00 00 00 00 01 11 00 3c 00 03 \ 00 00 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \ 45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \ 50 10 02 00 26 e8 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=10031,tp_dst=0,tcp_flags=ack tcp_csum:26e8 00000000 50 54 00 00 00 06 50 54-00 00 00 05 08 00 45 00 00000010 00 28 bd 12 00 00 40 06-3c 6a c0 a8 00 01 c0 a8 00000020 00 02 27 2f 00 00 78 50-cc 5b 57 af 42 1e 50 10 00000030 02 00 26 e8 00 00 00 00-00 00 00 00 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 0a 00 54 00 00 00 00 00 00 01 11 00 00 00 03 \ 00 00 00 03 00 3c 00 00 \ 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \ 45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \ 50 02 02 00 26 e8 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (OF1.1) (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=10031,tp_dst=0,tcp_flags=syn tcp_csum:26e8 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 0a 00 4c 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 00 01 00 0c 80 00 00 04 ff ff ff fe 00 00 00 00 \ 00 00 ff ff ff ff ff ff 00 23 20 83 c1 5f 80 35 \ 00 01 08 00 06 04 00 01 00 23 20 83 c1 5f 00 00 \ 00 00 00 23 20 83 c1 5f 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=1,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.2, with hex output of packet data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 0a 00 4c 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 00 01 00 0c 80 00 00 04 ff ff ff fe 00 00 00 00 \ 00 00 ff ff ff ff ff ff 00 23 20 83 c1 5f 80 35 \ 00 01 08 00 06 04 00 03 00 23 20 83 c1 5f 00 00 \ 00 00 00 23 20 83 c1 5f 00 00 00 00 \ " 3], [0], [dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f 00000000 ff ff ff ff ff ff 00 23-20 83 c1 5f 80 35 00 01 00000010 08 00 06 04 00 03 00 23-20 83 c1 5f 00 00 00 00 00000020 00 23 20 83 c1 5f 00 00-00 00 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 0a 00 54 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 01 02 03 04 05 06 07 08 00 01 00 0c 80 00 00 04 \ ff ff ff fe 00 00 00 00 00 00 ff ff ff ff ff ff \ 00 23 20 83 c1 5f 80 35 00 01 08 00 06 04 00 03 \ 00 23 20 83 c1 5f 00 00 00 00 00 23 20 83 c1 5f \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x102030405060708 total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 0a 00 54 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 01 02 03 04 05 06 07 08 00 01 00 0c 80 00 00 04 \ ff ff ff fe 00 00 00 00 00 00 ff ff ff ff ff ff \ 00 23 20 83 c1 5f 80 35 00 01 08 00 06 04 00 03 \ 00 23 20 83 c1 5f 00 00 00 00 00 23 20 83 c1 5f \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x102030405060708 total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.5]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0a 00 54 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 01 02 03 04 05 06 07 08 00 01 00 0c 80 00 00 04 \ ff ff ff fe 00 00 00 00 00 00 ff ff ff ff ff ff \ 00 23 20 83 c1 5f 80 35 00 01 08 00 06 04 00 03 \ 00 23 20 83 c1 5f 00 00 00 00 00 23 20 83 c1 5f \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_IN (OF1.5) (xid=0x0): cookie=0x102030405060708 total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_IN - OF1.3, with hex output of packet data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 0a 00 54 00 00 00 00 ff ff ff 00 00 2a 00 00 \ 01 02 03 04 05 06 07 08 00 01 00 0c 80 00 00 04 \ ff ff ff fe 00 00 00 00 00 00 ff ff ff ff ff ff \ 00 23 20 83 c1 5f 80 35 00 01 08 00 06 04 00 03 \ 00 23 20 83 c1 5f 00 00 00 00 00 23 20 83 c1 5f \ 00 00 00 00 \ " 3], [0], [dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x102030405060708 total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00 rarp,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f 00000000 ff ff ff ff ff ff 00 23-20 83 c1 5f 80 35 00 01 00000010 08 00 06 04 00 03 00 23-20 83 c1 5f 00 00 00 00 00000020 00 23 20 83 c1 5f 00 00-00 00 ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_REMOVED - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0b 00 58 00 00 00 00 00 00 00 00 00 03 50 54 \ 00 00 00 05 50 54 00 00 00 06 ff ff 00 00 08 06 \ 00 02 00 00 c0 a8 00 01 c0 a8 00 02 00 00 00 00 \ 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 05 \ 30 e0 35 00 00 05 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c \ "], [0], [dnl OFPT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 reason=idle duration5.820s idle5 pkts1 bytes60 ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_REMOVED - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 0b 00 40 00 00 00 00 fe dc ba 98 76 54 32 10 \ 80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \ 00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \ 00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.010s idle60 hard120 pkts1234567 bytes1869134438 ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_REMOVED - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 0b 00 40 00 00 00 00 fe dc ba 98 76 54 32 10 \ 80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \ 00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \ 00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.010s idle60 hard120 pkts1234567 bytes1869134438 ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_REMOVED - OF1.5]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0b 00 80 00 00 00 02 01 00 00 00 11 00 22 00 \ 00 00 00 00 00 00 00 01 00 01 00 2d 80 00 00 04 \ 00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \ 0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 34 80 02 00 08 \ 00 00 00 98 29 e6 ed c0 80 02 02 08 00 00 00 98 \ 00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \ 80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \ "], [0], [dnl OFPT_FLOW_REMOVED (OF1.5) (xid=0x2): priority=0,ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 reason=idle table_id=1 cookie:0x1 duration152.703s idle4352 hard8704 pkts2 bytes128 ]) AT_CLEANUP AT_SETUP([OFPT_PORT_STATUS - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0c 00 40 00 00 00 00 02 00 00 00 00 00 00 00 \ 00 03 50 54 00 00 00 01 65 74 68 30 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 \ 00 00 02 08 00 00 02 8f 00 00 02 8f 00 00 00 00 \ "], [0], [dnl OFPT_PORT_STATUS (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01 config: PORT_DOWN state: LINK_DOWN current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_PORT_STATUS - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 0c 00 50 00 00 00 00 02 00 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \ 00 00 28 0f 00 00 00 00 00 01 86 a0 00 01 86 a0 \ "], [0], [dnl OFPT_PORT_STATUS (OF1.1) (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_PORT_STATUS - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 0c 00 58 00 00 00 00 02 00 00 00 00 00 00 00 \ 00 00 00 03 00 48 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 \ 00 00 20 08 00 00 28 0f 00 00 28 0f 00 00 00 00 \ 00 01 86 a0 00 01 86 a0 \ "], [0], [dnl OFPT_PORT_STATUS (OF1.4) (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0d 00 54 00 00 00 00 00 00 01 14 00 01 00 08 \ 00 00 00 08 00 03 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 buffer=0x00000114 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.0, with packet]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0d 00 54 00 00 00 00 ff ff ff ff 00 01 00 08 \ 00 00 00 08 00 03 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.0, with hex output of packet data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0d 00 54 00 00 00 00 ff ff ff ff 00 01 00 08 \ 00 00 00 08 00 03 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 \ " 3], [0], [dnl OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75 00000000 50 54 00 00 00 05 50 54-00 00 00 06 08 00 45 00 00000010 00 28 00 00 40 00 40 06-b9 7c c0 a8 00 02 c0 a8 00000020 00 01 00 00 2b 60 00 00-00 00 6a 4f 2b 58 50 14 00000030 00 00 6d 75 00 00 00 00-00 00 00 00 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 0d 00 28 88 58 df c5 ff ff ff 00 ff ff ff fe \ 00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD buffer=0xffffff00 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.1, with packet]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 0d 00 64 88 58 df c5 ff ff ff ff ff ff ff fe \ 00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD data_len=60 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75 ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.5]) AT_KEYWORDS([ofp-print packet-out]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0d 00 30 11 22 33 44 ff ff ff 00 00 10 00 00 \ 00 01 00 10 80 00 00 04 00 00 00 01 00 00 00 00 \ 00 00 00 10 ff ff ff fb 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.5) (xid=0x11223344): in_port=1 actions=FLOOD buffer=0xffffff00 ]) dnl missing in_port AT_CHECK([ovs-ofctl ofp-print "\ 06 0d 00 40 11 22 33 44 ff ff ff 00 00 10 00 00 \ 00 01 00 20 80 00 04 08 00 00 00 00 00 00 00 03 \ 80 00 4C 08 00 00 00 00 00 00 00 05 00 00 00 00 \ 00 00 00 10 ff ff ff fb 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.5) (xid=0x11223344): ***decode error: OFPBRC_BAD_PORT*** 00000000 06 0d 00 40 11 22 33 44-ff ff ff 00 00 10 00 00 |...@."3D........| 00000010 00 01 00 20 80 00 04 08-00 00 00 00 00 00 00 03 |... ............| 00000020 80 00 4c 08 00 00 00 00-00 00 00 05 00 00 00 00 |..L.............| 00000030 00 00 00 10 ff ff ff fb-05 dc 00 00 00 00 00 00 |................| ]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0d 00 48 11 22 33 44 ff ff ff 00 00 10 00 00 \ 00 01 00 28 80 00 00 04 00 00 00 01 80 00 04 08 \ 00 00 00 00 00 00 00 03 80 00 4C 08 00 00 00 00 \ 00 00 00 05 00 00 00 00 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.5) (xid=0x11223344): tun_id=0x5,metadata=0x3,in_port=1 actions=FLOOD buffer=0xffffff00 ]) dnl include non pipeline field AT_CHECK([ovs-ofctl ofp-print "\ 06 0d 00 38 11 22 33 44 ff ff ff 00 00 10 00 00 \ 00 01 00 18 80 00 00 04 00 00 00 01 80 00 16 04 \ 11 22 33 44 00 00 00 00 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPT_PACKET_OUT (OF1.5) (xid=0x11223344): ***decode error: OFPBRC_PIPELINE_FIELDS_ONLY*** 00000000 06 0d 00 38 11 22 33 44-ff ff ff 00 00 10 00 00 |...8."3D........| dnl " 00000010 00 01 00 18 80 00 00 04-00 00 00 01 80 00 16 04 |................| 00000020 11 22 33 44 00 00 00 00-00 00 00 10 ff ff ff fb |."3D............| 00000030 05 dc 00 00 00 00 00 00- |........ | ]) AT_CLEANUP AT_SETUP([OFPT_PACKET_OUT - OF1.5, with packet]) AT_KEYWORDS([ofp-print packet-out]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0d 00 74 11 22 33 44 ff ff ff ff 00 10 00 00 \ 00 01 00 18 80 00 00 04 00 00 00 01 80 00 04 08 \ 00 00 00 00 00 00 00 03 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 "], [0], [dnl OFPT_PACKET_OUT (OF1.5) (xid=0x11223344): metadata=0x3,in_port=1 actions=FLOOD data_len=60 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75 ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.0 - low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \ 00 00 00 06 50 54 00 00 00 05 ff ff 00 00 08 06 \ 00 02 00 00 c0 a8 00 02 c0 a8 00 01 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \ 00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 idle:5 buf:0x10e out_port:0 actions=output:3 ], [dnl ofp_match|INFO|normalization changed ofp_match, details: ofp_match|INFO| pre: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 ofp_match|INFO|post: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.1 - low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 020e 0090 01020304 \ da1aa3e035d87158 ffffffffffffffff \ 02 01 003c 0078 9c40 ffffffff ffffffff ffffffff 0003 \ 0000 \ \ 0000 0058 00000000 000003f7 \ 000000000000ffffffffffff 000000000000ffffffffffff \ 0000 00 00 0806 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 \ 00000000 00 000000 0000000000000000ffffffffffffffff \ \ 0001 0008 03 000000 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.1) (xid=0x1020304): MOD table:2 priority=40000,arp,arp_spa=192.168.128.0/24 cookie:0xda1aa3e035d87158/0xffffffffffffffff idle:60 hard:120 send_flow_rem check_overlap actions=goto_table:3 ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 ], [dnl ]) AT_CLEANUP # The flow is formatted with ofp10_match_to_string() for the # high-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.0 - high verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \ 00 00 00 06 50 54 00 00 00 05 ff ff 00 00 08 06 \ 00 02 00 00 c0 a8 00 02 c0 a8 00 01 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \ 00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \ " 3], [0], [dnl OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e out_port:0 actions=output:3 ], [dnl ofp_match|INFO|normalization changed ofp_match, details: ofp_match|INFO| pre: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 ofp_match|INFO|post: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 ], [dnl ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([OFPT_FLOW_MOD - OF1.3 - flags - low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 04 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 1f 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.3) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 send_flow_rem check_overlap reset_counts no_packet_counts no_byte_counts actions=output:3 ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field ip_src]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 58 52 33 45 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 0a 80 00 0a 02 08 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 16 04 \ c0 a8 03 5c 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x52334502): ADD priority=255,ip actions=set_field:192.168.3.92->ip_src ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field ip_dst]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 58 52 33 45 07 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 0a 80 00 0a 02 08 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 18 04 \ c0 a8 4a 7a 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,ip actions=set_field:192.168.74.122->ip_dst ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field sctp_src]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 58 52 33 45 07 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 22 02 \ 0d 06 00 00 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,sctp actions=set_field:3334->sctp_src ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field sctp_dst]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 58 52 33 45 07 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 24 02 \ 11 5d 00 00 00 00 00 00 \ " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,sctp actions=set_field:4445->sctp_dst ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - experimenter OXM]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 48 52 33 45 07 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 14 ff ff 01 0c 00 00 23 20 01 23 45 67 \ 0f ff ff ff 00 00 00 00 " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,dp_hash=0x1234567/0xfffffff actions=drop ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field nd_target, nd_sll]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 0e 00 78 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 14 80 00 0a 02 86 dd 80 00 14 01 3a 80 \ 00 3a 01 87 00 00 00 00 00 04 00 30 00 00 00 00 \ 00 19 00 18 80 00 3e 10 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 19 00 10 80 00 40 06 \ aa aa aa aa aa aa 00 00 " 2], [0], [dnl OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD priority=255,icmp6,icmp_type=135 actions=set_field:::1->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll ], [dnl ]) AT_CLEANUP dnl This triggered a buggy "instructions out of order" message earlier. AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 0e 00 40 cf fe 6b 86 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 e8 \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 04 00 00 00 00 00 06 00 08 00 00 00 01"], [0], [dnl OFPT_FLOW_MOD (OF1.3) (xid=0xcffe6b86): ADD priority=1000 actions=meter:1 ]) AT_CLEANUP AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field ip_src]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 13 00 68 52 33 45 04 00 01 00 00 00 00 00 00 \ 00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 0a 80 00 0a 02 08 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 16 04 \ c0 a8 03 5c 00 00 00 00 \ " 2], [0], [dnl OFPST_FLOW reply (OF1.2) (xid=0x52334504): cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,ip actions=set_field:192.168.3.92->ip_src ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field ip_dst]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 13 00 68 52 33 45 09 00 01 00 00 00 00 00 00 \ 00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 0a 80 00 0a 02 08 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 18 04 \ c0 a8 4a 7a 00 00 00 00 \ " 2], [0], [dnl OFPST_FLOW reply (OF1.2) (xid=0x52334509): cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,ip actions=set_field:192.168.74.122->ip_dst ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field sctp_src]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 13 00 68 52 33 45 04 00 01 00 00 00 00 00 00 \ 00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 22 02 \ 0d 06 00 00 00 00 00 00 \ " 2], [0], [dnl OFPST_FLOW reply (OF1.2) (xid=0x52334504): cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,sctp actions=set_field:3334->sctp_src ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field sctp_dst]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 03 13 00 68 52 33 45 09 00 01 00 00 00 00 00 00 \ 00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \ 00 04 00 18 00 00 00 00 00 19 00 10 80 00 24 02 \ 11 5d 00 00 00 00 00 00 \ " 2], [0], [dnl OFPST_FLOW reply (OF1.2) (xid=0x52334509): cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,sctp actions=set_field:4445->sctp_dst ], [dnl ]) AT_CLEANUP AT_SETUP([OFPT_PORT_MOD - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 0f 00 20 00 00 00 03 00 03 50 54 00 00 00 01 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_PORT_MOD (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: UNCHANGED ]) AT_CLEANUP AT_SETUP([OFPT_PORT_MOD - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \ 50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_PORT_MOD (OF1.1) (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: UNCHANGED ]) AT_CLEANUP AT_SETUP([OFPT_PORT_MOD - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \ 50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_PORT_MOD (OF1.2) (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: UNCHANGED ]) AT_CLEANUP AT_SETUP([OFPT_PORT_MOD - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \ 50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_PORT_MOD (OF1.3) (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: UNCHANGED ]) AT_CLEANUP AT_SETUP([OFPT_PORT_MOD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \ 50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 08 00 00 00 01 " 3], [0], [dnl OFPT_PORT_MOD (OF1.4) (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: 10MB-HD ]) AT_CLEANUP AT_SETUP([OFPT_TABLE_MOD - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 11 00 10 00 00 00 02 02 00 00 00 00 00 00 02 \ " 3], [0], [dnl OFPT_TABLE_MOD (OF1.1) (xid=0x2): table_id=2, flow_miss_config=drop ]) AT_CLEANUP AT_SETUP([OFPT_TABLE_MOD - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 11 00 10 00 00 00 02 02 00 00 00 00 00 00 01 \ " 3], [0], [dnl OFPT_TABLE_MOD (OF1.2) (xid=0x2): table_id=2, flow_miss_config=continue ]) AT_CLEANUP AT_SETUP([OFPT_TABLE_MOD - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_TABLE_MOD (OF1.3) (xid=0x2): table_id=2 ]) AT_CLEANUP AT_SETUP([OFPT_TABLE_MOD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 11 00 10 00 00 00 02 02 00 00 00 00 00 00 00 \ " 3], [0], [dnl OFPT_TABLE_MOD (OF1.4) (xid=0x2): table_id=2, eviction=off, vacancy=off ]) AT_CLEANUP AT_SETUP([OFPST_DESC request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl OFPST_DESC request (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPST_DESC reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 04 2c 00 00 00 01 00 00 00 00 4e 69 63 69 \ 72 61 2c 20 49 6e 63 2e 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 4f 70 65 6e \ 20 76 53 77 69 74 63 68 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 31 2e 31 2e \ 30 70 72 65 32 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 4e 6f 6e 65 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 4e 6f 6e 65 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_DESC reply (xid=0x1): Manufacturer: Nicira, Inc. Hardware: Open vSwitch Software: 1.1.0pre2 Serial Num: None DP Description: None ]) AT_CLEANUP AT_SETUP([OFPST_FLOW request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 38 00 00 00 04 00 01 00 00 00 38 20 ff \ ff fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 ff 00 ff ff \ "], [0], [dnl OFPST_FLOW request (xid=0x4): ]) AT_CLEANUP AT_SETUP([OFPST_FLOW request - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 03 12 00 38 00 00 00 02 00 01 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_FLOW request (OF1.2) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_FLOW request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 04 12 00 38 00 00 00 02 00 01 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_FLOW request (OF1.3) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_FLOW request - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 06 12 00 38 00 00 00 04 00 01 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_FLOW request (OF1.5) (xid=0x4): ]) AT_CLEANUP AT_SETUP([OFPST_FLOW reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 01 e4 00 00 00 04 00 01 00 00 00 60 00 00 \ 00 00 00 00 00 03 50 54 00 00 00 05 50 54 00 00 \ 00 06 ff ff 00 00 08 06 00 02 00 00 c0 a8 00 01 \ c0 a8 00 02 00 00 00 00 00 00 00 04 0b eb c2 00 \ ff ff 00 05 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 \ 00 00 00 3c 00 00 00 08 00 01 00 00 00 60 00 00 \ 00 00 00 00 00 01 50 54 00 00 00 06 50 54 00 00 \ 00 05 ff ff 00 00 08 00 00 01 00 00 c0 a8 00 02 \ c0 a8 00 01 00 00 00 00 00 00 00 08 35 a4 e9 00 \ ff ff 00 05 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 0d 00 00 00 00 \ 00 00 04 fa 00 00 00 08 00 03 00 00 00 60 00 00 \ 00 00 00 00 00 01 50 54 00 00 00 06 50 54 00 00 \ 00 05 ff ff 00 00 08 06 00 01 00 00 c0 a8 00 02 \ c0 a8 00 01 00 00 00 00 00 00 00 04 10 b0 76 00 \ ff ff 00 05 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 \ 00 00 00 3c 00 00 00 08 00 03 00 00 00 60 00 00 \ 00 00 00 01 00 03 50 54 00 00 00 05 50 54 00 00 \ 00 06 ff ff 00 00 08 00 00 01 00 00 c0 a8 00 01 \ c0 a8 00 02 00 08 00 00 00 00 00 09 05 b8 d8 00 \ 80 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 0d 00 00 00 00 \ 00 00 04 fa 00 00 00 08 00 01 00 00 \ 00 58 02 00 00 3f ff ff 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_FLOW reply (xid=0x4): cookie=0x0, duration=4.200s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1 cookie=0x0, duration=8.900s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3 cookie=0x0, duration=4.280s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=1,nw_tos=0,tp_src=0,tp_dst=0 actions=output:3 cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1 cookie=0x0, duration=0s, table=2, n_packets=0, n_bytes=0, actions=drop ]) AT_CLEANUP AT_SETUP([OFPST_FLOW reply - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 03 13 01 78 00 00 00 02 00 01 00 00 00 00 00 00 \ 00 78 00 00 00 00 00 03 01 5e f3 c0 80 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 62 \ 00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \ ca da ad d6 0d 37 80 00 0a 02 08 00 80 00 10 01 \ 00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \ 05 dc 00 00 00 00 00 00 00 78 00 00 00 00 00 04 \ 20 7c 0a 40 80 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 \ 00 00 00 00 00 00 00 8c 00 01 00 2d 80 00 00 04 \ 00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \ 0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \ 00 00 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 02 05 dc 00 00 00 00 00 00 \ 00 78 00 00 00 00 00 04 20 a9 d1 00 80 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 2a \ 00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \ 52 54 00 97 00 69 80 00 0a 02 08 00 80 00 10 01 \ 00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \ 05 dc 00 00 00 00 00 00 \ "], [0], [dnl OFPST_FLOW reply (OF1.2) (xid=0x2): cookie=0x0, duration=3.023s, table=0, n_packets=1, n_bytes=98, ip,metadata=0,in_port=2,dl_dst=ca:da:ad:d6:0d:37,nw_tos=0 actions=output:2 cookie=0x0, duration=4.545s, table=0, n_packets=2, n_bytes=140, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2 cookie=0x0, duration=4.548s, table=0, n_packets=1, n_bytes=42, ip,metadata=0,in_port=2,dl_dst=52:54:00:97:00:69,nw_tos=0 actions=output:2 ]) AT_CLEANUP AT_SETUP([OFPST_FLOW reply - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 06 13 01 00 00 00 00 04 00 01 00 00 00 00 00 00 \ 00 78 00 00 00 00 80 00 00 00 00 00 00 05 00 00 \ 00 00 00 00 00 00 00 00 00 01 00 0c 80 00 00 04 \ 00 00 00 02 00 00 00 00 00 00 00 34 80 02 00 08 \ 00 00 00 c4 0b 06 e0 40 80 02 02 08 00 00 00 c4 \ 00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 02 \ 80 02 0a 08 00 00 00 00 00 00 00 80 00 00 00 00 \ 00 04 00 18 00 00 00 00 00 00 00 10 ff ff ff fa \ 00 00 00 00 00 00 00 00 00 78 00 00 00 00 0f a0 \ 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 0c 80 00 00 04 00 00 00 03 00 00 00 00 \ 00 00 00 34 80 02 00 08 00 00 00 b3 25 40 be 40 \ 80 02 02 08 00 00 00 b3 00 00 00 00 80 02 08 08 \ 00 00 00 00 00 00 00 02 80 02 0a 08 00 00 00 00 \ 00 00 00 80 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 ff ff ff fa 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_FLOW reply (OF1.5) (xid=0x4): cookie=0x0, duration=196.185s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=196, in_port=2 actions=NORMAL cookie=0x0, duration=179.625s, table=0, n_packets=2, n_bytes=128, send_flow_rem reset_counts idle_age=179, priority=4000,in_port=3 actions=NORMAL ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 38 00 00 00 04 00 02 00 00 00 38 20 ff \ ff fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 ff 00 ff ff \ "], [0], [dnl OFPST_AGGREGATE request (xid=0x4): ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE request - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 03 12 00 38 00 00 00 02 00 02 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE request (OF1.2) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 04 12 00 38 00 00 00 02 00 02 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE request (OF1.3) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE request - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 06 12 00 60 00 00 00 04 00 02 00 00 00 00 00 00 \ ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 01 00 04 00 00 00 00 00 00 00 24 80 02 06 04 \ 00 00 00 00 80 02 08 08 00 00 00 00 00 00 00 00 \ 80 02 0a 08 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE request (OF1.5) (xid=0x4): ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 24 00 00 00 04 00 02 00 00 00 00 00 00 \ 00 00 01 82 00 00 00 00 00 00 93 78 00 00 00 04 \ 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE reply (xid=0x4): packet_count=386 byte_count=37752 flow_count=4 ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE reply - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 03 13 00 28 00 00 00 02 00 02 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 79 00 00 00 00 00 00 4b 4f \ 00 00 00 03 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE reply (OF1.2) (xid=0x2): packet_count=121 byte_count=19279 flow_count=3 ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 28 00 00 00 02 00 02 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 79 00 00 00 00 00 00 4b 4f \ 00 00 00 03 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE reply (OF1.3) (xid=0x2): packet_count=121 byte_count=19279 flow_count=3 ]) AT_CLEANUP AT_SETUP([OFPST_AGGREGATE reply - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 06 13 00 38 00 00 00 04 00 02 00 00 00 00 00 00 \ 00 00 00 24 80 02 06 04 00 00 00 03 80 02 08 08 \ 00 00 00 00 00 00 00 79 80 02 0a 08 00 00 00 00 \ 00 00 4b 4f 00 00 00 00 \ "], [0], [dnl OFPST_AGGREGATE reply (OF1.5) (xid=0x4): packet_count=121 byte_count=19279 flow_count=3 ]) AT_CLEANUP AT_SETUP([OFPST_TABLE request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl OFPST_TABLE request (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPST_TABLE request - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "02120010000000020003000000000000"], [0], [dnl OFPST_TABLE request (OF1.1) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_TABLE request - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "03120010000000020003000000000000"], [0], [dnl OFPST_TABLE request (OF1.2) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_TABLE request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "04120010000000020003000000000000"], [0], [dnl OFPST_TABLE request (OF1.3) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_TABLE reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 4c 00 00 00 01 00 03 00 00 00 00 00 00 \ 63 6c 61 73 73 69 66 69 65 72 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 3f ff ff 00 10 00 00 00 00 00 0b 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_TABLE reply (xid=0x1): table 0 ("classifier"): active=11, lookup=0, matched=0 max_entries=1048576 matching: exact match or wildcard: in_port eth_{src,dst,type} vlan_{vid,pcp} ip_{src,dst} nw_{proto,tos} tcp_{src,dst} ]) AT_CLEANUP AT_SETUP([OFPST_TABLE reply - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) (echo 'OFPST_TABLE reply (OF1.2) (xid=0x2): table 0 ("classifier"): active=1, lookup=74614, matched=106024 config=controller max_entries=1000000 instructions (table miss and others): instructions: write_metadata goto_table Write-Actions and Apply-Actions features: supported on Set-Field: metadata in_port_oxm eth_{src,dst,type} vlan_{vid,pcp} mpls_{label,tc} ip_{src,dst} ipv6_{src,dst,label} nw_proto ip_dscp nw_ecn arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll} matching: exact match or wildcard: metadata in_port_oxm eth_{src,dst,type} vlan_{vid,pcp} mpls_{label,tc} ip_{src,dst} ipv6_{src,dst,label} nw_proto ip_dscp nw_ecn arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll} table 1 ("table1"): active=0, lookup=0, matched=0 (same features) ' for i in `seq 2 253`; do printf ' table %d ("table%d"): ditto\n' $i $i done echo ' table 254 ("table254"): active=2, lookup=0, matched=0 (same features)') > expout (pad32="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" pad7="00 00 00 00 00 00 00 " mid="00 00 00 0f ff ff ff ff \ 00 00 00 0f ff ff ff ff 00 00 00 00 00 00 00 00 \ 00 00 00 0f ff ff ff ff 00 00 00 0f ff ff ff ff \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 07 00 00 00 00 00 0f 42 40 " tail="00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" printf "03 13 7f 90 00 00 00 02 00 03 00 00 00 00 00 00 " x=0 printf "%02x $pad7" $x printf "%s$pad32" "classifier" | od -A n -t x1 -v -N 32 | tr '\n' ' ' printf "$mid 00 00 00 01 " printf "00 00 00 00 00 01 23 76 00 00 00 00 00 01 9e 28 " x=1 while test $x -lt 254; do printf "%02x $pad7" $x printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' ' printf "$mid 00 00 00 00 $tail " x=`expr $x + 1` done x=254 printf "%02x $pad7" $x printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' ' printf "$mid 00 00 00 02 $tail") > in AT_CHECK([ovs-ofctl ofp-print - < in], [0], [expout]) AT_CLEANUP AT_SETUP([OFPST_TABLE reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 40 00 00 00 01 00 03 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 02 00 \ 00 00 00 00 00 00 01 00 01 00 00 00 00 00 00 0c \ 00 00 00 00 00 00 02 01 00 00 00 00 00 00 01 01 \ "], [0], [dnl OFPST_TABLE reply (OF1.3) (xid=0x1): table 0: active=11, lookup=512, matched=256 table 1: active=12, lookup=513, matched=257 ]) AT_CLEANUP AT_SETUP([OFPST_PORT request - 1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 14 00 00 00 01 00 04 00 00 ff ff 00 00 \ 00 00 00 00 \ "], [0], [dnl OFPST_PORT request (xid=0x1): port_no=ANY ]) AT_CLEANUP AT_SETUP([OFPST_PORT request - 1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 02 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \ ff ff ff ff 00 00 00 00 \ "], [0], [dnl OFPST_PORT request (OF1.1) (xid=0x2): port_no=ANY ]) AT_CLEANUP AT_SETUP([OFPST_PORT request - 1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 03 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \ ff ff ff ff 00 00 00 00 \ "], [0], [dnl OFPST_PORT request (OF1.2) (xid=0x2): port_no=ANY ]) AT_CLEANUP AT_SETUP([OFPST_PORT request - 1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 04 12 00 18 00 00 00 02 00 04 00 00 00 00 00 00 \ ff ff ff ff 00 00 00 00 \ "], [0], [dnl OFPST_PORT request (OF1.3) (xid=0x2): port_no=ANY ]) AT_CLEANUP AT_SETUP([OFPST_PORT reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 01 ac 00 00 00 01 00 04 00 00 00 03 00 00 \ 00 00 00 00 00 00 00 00 00 00 4d 20 00 00 00 00 \ 00 00 14 32 00 00 00 00 00 0f 60 4e 00 00 00 00 \ 00 05 71 bc 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 ff fe 00 00 00 00 00 00 00 00 00 00 \ 00 00 02 ac 00 00 00 00 00 00 01 f5 00 00 00 00 \ 00 01 0c 8c 00 00 00 00 00 00 db 1c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 \ 00 00 00 00 00 00 00 00 00 00 06 be 00 00 00 00 \ 00 00 05 84 00 00 00 00 00 02 34 b4 00 00 00 00 \ 00 02 23 d4 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 \ 00 00 14 12 00 00 00 00 00 00 14 66 00 00 00 00 \ 00 04 a2 54 00 00 00 00 00 05 8a 1e 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_PORT reply (xid=0x1): 4 ports port 3: rx pkts=19744, bytes=1007694, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=5170, bytes=356796, drop=0, errs=0, coll=0 port LOCAL: rx pkts=684, bytes=68748, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=501, bytes=56092, drop=0, errs=0, coll=0 port 2: rx pkts=1726, bytes=144564, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=1412, bytes=140244, drop=0, errs=0, coll=0 port 1: rx pkts=5138, bytes=303700, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=5222, bytes=363038, drop=0, errs=0, coll=0 ]) AT_CLEANUP AT_SETUP([OFPST_PORT reply - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 03 13 01 48 00 00 00 02 00 04 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 00 00 00 00 00 00 01 95 56 \ 00 00 00 00 00 00 00 88 00 00 00 00 02 5d 08 98 \ 00 00 00 00 00 00 2c f8 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 ff ff ff fe 00 00 00 00 \ 00 00 00 00 00 00 00 44 00 00 00 00 00 00 9d 2c \ 00 00 00 00 00 00 16 7c 00 00 00 00 01 1e 36 44 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 44 \ 00 00 00 00 00 00 9d 2c 00 00 00 00 00 00 16 7c \ 00 00 00 00 01 1e 36 44 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_PORT reply (OF1.2) (xid=0x2): 3 ports port 2: rx pkts=103766, bytes=39651480, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=136, bytes=11512, drop=0, errs=0, coll=0 port LOCAL: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0 port 1: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0 ]) AT_CLEANUP AT_SETUP([OFPST_PORT reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 01 60 00 00 00 02 00 04 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 00 00 00 00 00 00 01 95 56 \ 00 00 00 00 00 00 00 88 00 00 00 00 02 5d 08 98 \ 00 00 00 00 00 00 2c f8 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 01 00 0f 42 40 \ ff ff ff fe 00 00 00 00 \ 00 00 00 00 00 00 00 44 00 00 00 00 00 00 9d 2c \ 00 00 00 00 00 00 16 7c 00 00 00 00 01 1e 36 44 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 44 \ 00 00 00 00 00 00 9d 2c 00 00 00 00 00 00 16 7c \ 00 00 00 00 01 1e 36 44 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 07 54 d4 c0 \ "], [0], [dnl OFPST_PORT reply (OF1.3) (xid=0x2): 3 ports port 2: rx pkts=103766, bytes=39651480, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=136, bytes=11512, drop=0, errs=0, coll=0 duration=1.001s port LOCAL: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0 port 1: rx pkts=68, bytes=5756, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=40236, bytes=18757188, drop=0, errs=0, coll=0 duration=0.123s ]) AT_CLEANUP AT_SETUP([OFPST_PORT reply - OF1.4]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 88 00 00 00 02 00 04 00 00 00 00 00 00 \ 00 78 00 00 00 00 00 02 00 00 00 01 00 0f 42 40 \ 00 00 00 00 00 01 95 56 00 00 00 00 00 00 00 88 \ 00 00 00 00 02 5d 08 98 00 00 00 00 00 00 2c f8 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 28 00 00 00 00 00 00 00 00 00 00 00 fc \ 00 00 00 00 00 00 00 fd 00 00 00 00 00 00 00 fe \ 00 00 00 00 00 00 00 ff \ "], [0], [dnl OFPST_PORT reply (OF1.4) (xid=0x2): 1 ports port 2: rx pkts=103766, bytes=39651480, drop=0, errs=0, frame=252, over=253, crc=254 tx pkts=136, bytes=11512, drop=0, errs=0, coll=255 duration=1.001s ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 14 00 00 00 01 00 05 00 00 ff fc 00 00 \ ff ff ff ff \ "], [0], [dnl OFPST_QUEUE request (xid=0x1): port=ANY queue=ALL ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE request - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 02 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPST_QUEUE request (OF1.1) (xid=0x2): port=ANY queue=ALL ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE request - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 03 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPST_QUEUE request (OF1.2) (xid=0x2): port=ANY queue=ALL ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 04 12 00 18 00 00 00 02 00 05 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPST_QUEUE request (OF1.3) (xid=0x2): port=ANY queue=ALL ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 cc 00 00 00 01 00 05 00 00 00 03 00 00 \ 00 00 00 01 00 00 00 00 00 00 01 2e 00 00 00 00 \ 00 00 00 01 00 00 00 00 00 00 00 00 00 03 00 00 \ 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 \ 00 00 00 01 00 00 00 00 00 00 08 34 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 02 00 00 \ 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 \ 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 \ 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_QUEUE reply (xid=0x1): 6 queues port 3 queue 1: bytes=302, pkts=1, errors=0, duration=? port 3 queue 2: bytes=0, pkts=0, errors=0, duration=? port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=? port 2 queue 2: bytes=0, pkts=0, errors=0, duration=? port 1 queue 1: bytes=0, pkts=0, errors=0, duration=? port 1 queue 2: bytes=0, pkts=0, errors=0, duration=? ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE reply - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 02 13 00 d0 00 00 00 01 00 05 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 01 00 00 00 00 00 00 01 2e \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 01 00 00 00 00 00 00 08 34 \ 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_QUEUE reply (OF1.1) (xid=0x1): 6 queues port 3 queue 1: bytes=302, pkts=1, errors=0, duration=? port 3 queue 2: bytes=0, pkts=0, errors=0, duration=? port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=? port 2 queue 2: bytes=0, pkts=0, errors=0, duration=? port 1 queue 1: bytes=0, pkts=0, errors=0, duration=? port 1 queue 2: bytes=0, pkts=0, errors=0, duration=? ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE reply - OF1.2]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 03 13 00 d0 00 00 00 01 00 05 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 01 00 00 00 00 00 00 01 2e \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 01 00 00 00 00 00 00 08 34 \ 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_QUEUE reply (OF1.2) (xid=0x1): 6 queues port 3 queue 1: bytes=302, pkts=1, errors=0, duration=? port 3 queue 2: bytes=0, pkts=0, errors=0, duration=? port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=? port 2 queue 2: bytes=0, pkts=0, errors=0, duration=? port 1 queue 1: bytes=0, pkts=0, errors=0, duration=? port 1 queue 2: bytes=0, pkts=0, errors=0, duration=? ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 01 00 00 00 00 01 00 05 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 01 00 00 00 00 00 00 01 2e \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 \ 00 00 00 03 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 \ 00 00 00 02 00 00 00 01 00 00 00 00 00 00 08 34 \ 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 \ 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 \ 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPST_QUEUE reply (OF1.3) (xid=0x1): 6 queues port 3 queue 1: bytes=302, pkts=1, errors=0, duration=100.500s port 3 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=100.500s port 2 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s port 1 queue 1: bytes=0, pkts=0, errors=0, duration=100.500s port 1 queue 2: bytes=0, pkts=0, errors=0, duration=? ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE reply - OF1.4]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 05 13 01 30 00 00 00 01 00 05 00 00 00 00 00 00 \ 00 30 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 01 00 00 00 00 00 00 01 2e \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 00 30 00 00 00 00 00 00 \ 00 00 00 03 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 00 30 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 01 00 00 00 00 00 00 08 34 \ 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 00 30 00 00 00 00 00 00 \ 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 00 30 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 64 1d cd 65 00 00 30 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPST_QUEUE reply (OF1.4) (xid=0x1): 6 queues port 3 queue 1: bytes=302, pkts=1, errors=0, duration=100.500s port 3 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=100.500s port 2 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s port 1 queue 1: bytes=0, pkts=0, errors=0, duration=100.500s port 1 queue 2: bytes=0, pkts=0, errors=0, duration=? ]) AT_CLEANUP AT_SETUP([NXST_GROUP request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 20 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 07 00 00 00 00 \ ff ff ff ff 00 00 00 00 \ "], [0], [NXST_GROUP request (xid=0x4): group_id=ANY ]) AT_CLEANUP AT_SETUP([OFPST_GROUP request - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 02 12 00 18 00 00 00 02 00 06 00 00 00 00 00 00 \ ff ff ff ff 00 00 00 00 \ "], [0], [OFPST_GROUP request (OF1.1) (xid=0x2): group_id=ANY ]) AT_CLEANUP AT_SETUP([NXST_GROUP reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 b8 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 07 00 00 00 00 \ 00 58 00 00 87 65 43 21 00 00 00 04 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 12 1d cd 65 00 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 66 66 00 00 00 00 00 33 33 33 \ 00 48 00 00 00 00 00 05 00 00 00 02 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 10 1d cd 65 00 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ "], [0], [dnl NXST_GROUP reply (xid=0x4): group_id=2271560481,duration=18.500s,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443 group_id=5,duration=16.500s,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962 ]) AT_CLEANUP AT_SETUP([OFPST_GROUP reply - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 02 13 00 a0 00 00 00 02 00 06 00 00 00 00 00 00 \ 00 50 00 00 87 65 43 21 00 00 00 04 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 66 66 00 00 00 00 00 33 33 33 \ 00 40 00 00 00 00 00 05 00 00 00 02 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ "], [0], [dnl OFPST_GROUP reply (OF1.1) (xid=0x2): group_id=2271560481,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443 group_id=5,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962 ]) AT_CLEANUP AT_SETUP([OFPST_GROUP reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 b0 00 00 00 02 00 06 00 00 00 00 00 00 \ 00 58 00 00 87 65 43 21 00 00 00 04 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 12 1d cd 65 00 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 66 66 00 00 00 00 00 33 33 33 \ 00 48 00 00 00 00 00 05 00 00 00 02 00 00 00 00 \ 00 00 00 00 00 00 88 88 00 00 00 00 00 77 77 77 \ 00 00 00 10 1d cd 65 00 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \ "], [0], [dnl OFPST_GROUP reply (OF1.3) (xid=0x2): group_id=2271560481,duration=18.500s,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443 group_id=5,duration=16.500s,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962 ]) AT_CLEANUP AT_SETUP([NXST_GROUP_DESC request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 20 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 08 00 00 00 00 \ 00 00 00 01 00 00 00 00 "], [0], [NXST_GROUP_DESC request (xid=0x4): group_id=1 ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_DESC request - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 02 12 00 10 00 00 00 02 00 07 00 00 00 00 00 00 \ "], [0], [OFPST_GROUP_DESC request (OF1.1) (xid=0x2): group_id=ALL ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_DESC request - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 06 12 00 18 00 00 00 02 00 07 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 00 "], [0], [OFPST_GROUP_DESC request (OF1.5) (xid=0x2): group_id=1 ]) AT_CLEANUP AT_SETUP([NXST_GROUP_DESC reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 c8 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 08 00 00 00 00 \ 00 b0 01 00 00 00 20 00 00 60 00 00 00 00 00 00 \ 00 20 00 08 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 00 00 08 00 64 00 00 \ 00 01 00 08 00 00 00 01 \ 00 20 00 08 00 00 00 01 00 00 00 08 00 02 00 00 \ 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 02 \ 00 20 00 08 00 00 00 02 00 00 00 08 00 03 00 00 \ 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 03 \ ff ff 00 3b 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 \ 80 00 18 04 ff ff ff 00 80 00 1a 02 ff ff 80 00 \ 14 01 ff 00 00 00 00 00 \ "], [0], [dnl NXST_GROUP_DESC reply (xid=0x4): group_id=8192,type=select,selection_method=hash,fields(ip_dst=255.255.255.0,nw_proto,tcp_src),bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_DESC reply - OF1.1]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 02 13 00 78 00 00 00 02 00 07 00 00 00 00 00 00 \ 00 68 01 00 00 00 20 00 \ 00 20 00 64 00 00 00 01 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 02 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 03 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_GROUP_DESC reply (OF1.1) (xid=0x2): group_id=8192,type=select,bucket=weight:100,watch_port:1,actions=output:1,bucket=weight:200,watch_port:2,actions=output:2,bucket=weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_DESC reply - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 06 13 00 d8 00 00 00 02 00 07 00 00 00 00 00 00 \ 00 c8 01 00 00 00 20 00 00 78 00 00 00 00 00 00 \ 00 28 00 10 00 00 00 00 00 00 00 10 00 00 00 01 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 64 00 00 \ 00 01 00 08 00 00 00 01 \ 00 28 00 10 00 00 00 01 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 02 \ 00 28 00 10 00 00 00 02 00 00 00 10 00 00 00 03 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 03 \ ff ff 00 3b 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 \ 80 00 18 04 ff ff ff 00 80 00 1a 02 ff ff 80 00 \ 14 01 ff 00 00 00 00 00 \ "], [0], [dnl OFPST_GROUP_DESC reply (OF1.5) (xid=0x2): group_id=8192,type=select,selection_method=hash,fields(ip_dst=255.255.255.0,nw_proto,tcp_src),bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([NXST_GROUP_FEATURES request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 18 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 09 00 00 00 00 \ "], [0], [NXST_GROUP_FEATURES request (xid=0x4): ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_FEATURES request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 03 12 00 10 00 00 00 02 00 08 00 00 00 00 00 00 \ "], [0], [OFPST_GROUP_FEATURES request (OF1.2) (xid=0x2): ]) AT_CLEANUP AT_SETUP([NXST_GROUP_FEATURES reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 09 00 00 00 00 \ 00 00 00 0f 00 00 00 0f \ 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 \ 00 00 00 01 00 00 00 07 00 00 00 0f 00 00 00 1f \ "], [0], [dnl NXST_GROUP_FEATURES reply (xid=0x4): Group table: Types: 0xf Capabilities: 0xf all group: max_groups=0x1 actions: output select group: max_groups=0x2 actions: output set_vlan_vid set_vlan_pcp indirect group: max_groups=0x3 actions: output set_vlan_vid set_vlan_pcp strip_vlan fast failover group: max_groups=0x4 actions: output set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src ]) AT_CLEANUP AT_SETUP([OFPST_GROUP_FEATURES reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 03 13 00 38 00 00 00 02 00 08 00 00 00 00 00 00 \ 00 00 00 0f 00 00 00 0f \ 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 \ 00 00 00 01 00 02 00 01 00 06 00 01 00 0e 00 01 \ "], [0], [dnl OFPST_GROUP_FEATURES reply (OF1.2) (xid=0x2): Group table: Types: 0xf Capabilities: 0xf all group: max_groups=0x1 actions: output select group: max_groups=0x2 actions: output push_vlan indirect group: max_groups=0x3 actions: output strip_vlan push_vlan fast failover group: max_groups=0x4 actions: output strip_vlan push_vlan push_mpls ]) AT_CLEANUP AT_SETUP([OFPST_PORT_DESC request - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "0110000c00000001000d0000"], [0], [dnl OFPST_PORT_DESC request (xid=0x1): port=ANY ]) AT_CLEANUP AT_SETUP([OFPST_PORT_DESC request - OF1.5]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 06 12 00 18 00 00 00 02 00 0d 00 00 00 00 00 00 \ 00 00 00 05 00 00 00 00"], [0], [dnl OFPST_PORT_DESC request (OF1.5) (xid=0x2): port=5 ]) AT_CLEANUP AT_SETUP([OFPST_PORT_DESC reply - OF1.0]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 3c 00 00 00 00 00 0d 00 00 00 03 50 54 \ 00 00 00 01 65 74 68 30 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 01 00 00 02 08 \ 00 00 02 8f 00 00 02 8f 00 00 00 00 \ "], [0], [dnl OFPST_PORT_DESC reply (xid=0x0): 3(eth0): addr:50:54:00:00:00:01 config: PORT_DOWN state: LINK_DOWN current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPST_PORT_DESC reply - OF1.4]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 58 00 00 00 02 00 0d 00 00 00 00 00 00 \ 00 00 00 03 00 48 00 00 50 54 00 00 00 01 00 00 \ 65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 \ 00 00 20 08 00 00 28 0f 00 00 28 0f 00 00 00 00 \ 00 01 86 a0 00 01 86 a0 \ "], [0], [dnl OFPST_PORT_DESC reply (OF1.4) (xid=0x2): 3(eth0): addr:50:54:00:00:00:01 config: 0 state: 0 current: 100MB-FD AUTO_NEG advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG speed: 100 Mbps now, 100 Mbps max ]) AT_CLEANUP AT_SETUP([OFPT_METER_MOD request - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 1d 00 20 00 00 00 02 00 00 00 0d 00 00 00 05 \ 00 01 00 10 00 00 04 00 00 00 00 80 00 00 00 00 \ "], [0], [dnl OFPT_METER_MOD (OF1.3) (xid=0x2): ADD meter=5 kbps burst stats bands= type=drop rate=1024 burst_size=128 ]) AT_CLEANUP AT_SETUP([OFPT_METER_MOD request - bad band - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 1d 00 20 85 01 d7 38 00 00 00 00 00 00 00 01 00 05 00 10 00 00 00 02 00 00 00 02 00 00 00 00 "], [0], [dnl OFPT_METER_MOD (OF1.3) (xid=0x8501d738): ***decode error: OFPMMFC_BAD_BAND*** 00000000 04 1d 00 20 85 01 d7 38-00 00 00 00 00 00 00 01 |... ...8........| 00000010 00 05 00 10 00 00 00 02-00 00 00 02 00 00 00 00 |................| ]) AT_CLEANUP AT_SETUP([OFPT_METER_MOD request - bad command - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 1d 00 10 28 a6 26 52 00 08 00 00 00 00 00 01 "], [0], [dnl OFPT_METER_MOD (OF1.3) (xid=0x28a62652): ***decode error: OFPMMFC_BAD_COMMAND*** 00000000 04 1d 00 10 28 a6 26 52-00 08 00 00 00 00 00 01 |....@{:@.&R........| ]) AT_CLEANUP AT_SETUP([OFPT_METER_MOD request - bad flags - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 1d 00 20 82 b3 a1 a4 00 00 00 03 00 00 00 01 \ 00 01 00 10 00 00 00 02 00 00 00 02 00 00 00 00 \ "], [0], [dnl OFPT_METER_MOD (OF1.3) (xid=0x82b3a1a4): ***decode error: OFPMMFC_BAD_FLAGS*** 00000000 04 1d 00 20 82 b3 a1 a4-00 00 00 03 00 00 00 01 |... ............| 00000010 00 01 00 10 00 00 00 02-00 00 00 02 00 00 00 00 |................| ]) AT_CLEANUP AT_SETUP([OFPST_METER request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "041200180000000200090000000000000000000100000000"], [0], [dnl OFPST_METER request (OF1.3) (xid=0x2): meter=1 ]) AT_CLEANUP AT_SETUP([OFPST_METER_CONFIG request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "0412001800000002000a0000000000000000000100000000"], [0], [dnl OFPST_METER_CONFIG request (OF1.3) (xid=0x2): meter=1 ]) AT_CLEANUP AT_SETUP([OFPST_METER_FEATURES request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "0412001000000002000b000000000000"], [0], [dnl OFPST_METER_FEATURES request (OF1.3) (xid=0x2): ]) AT_CLEANUP AT_SETUP([OFPST_METER_FEATURES reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 20 00 00 00 02 00 0b 00 00 00 00 00 00 \ 00 01 00 00 00 00 00 06 00 00 00 0F 10 02 00 00 \ "], [0], [dnl OFPST_METER_FEATURES reply (OF1.3) (xid=0x2): max_meter:65536 max_bands:16 max_color:2 band_types: drop dscp_remark capabilities: kbps pktps burst stats ]) AT_CLEANUP AT_SETUP([OFPST_METER_CONFIG reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 50 00 00 00 02 00 0a 00 00 00 00 00 00 \ 00 28 00 05 00 00 00 01 \ 00 01 00 10 00 01 00 00 00 00 05 00 00 00 00 00 \ 00 02 00 10 00 10 00 00 00 00 f0 00 00 00 00 00 \ 00 18 00 09 00 00 00 02 \ 00 01 00 10 00 02 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPST_METER_CONFIG reply (OF1.3) (xid=0x2): meter=1 kbps burst bands= type=drop rate=65536 burst_size=1280 type=dscp_remark rate=1048576 burst_size=61440 prec_level=0 meter=2 kbps stats bands= type=drop rate=131072 ]) AT_CLEANUP AT_SETUP([OFPST_METER reply - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 90 00 00 00 02 00 09 00 00 00 00 00 00 \ 00 00 00 01 00 48 00 00 00 00 00 00 00 00 00 05 \ 00 00 00 00 00 00 10 00 00 00 00 00 00 02 30 00 \ 00 00 01 8a 0a 6e 23 44 \ 00 00 00 00 00 00 00 7e 00 00 00 00 00 00 34 33 \ 00 00 00 00 00 00 00 e7 00 00 00 00 00 00 94 2e \ 00 00 00 02 00 38 00 00 00 00 00 00 00 00 00 02 \ 00 00 00 00 00 00 02 00 00 00 00 00 00 00 30 00 \ 00 00 01 87 0a 23 6e 44 \ 00 00 00 00 00 00 00 2a 00 00 00 00 00 00 04 33 \ "], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:5 packet_in_count:4096 byte_in_count:143360 duration:394.174990148s bands: 0: packet_count:126 byte_count:13363 1: packet_count:231 byte_count:37934 meter:2 flow_count:2 packet_in_count:512 byte_in_count:12288 duration:391.170094148s bands: 0: packet_count:42 byte_count:1075 ]) AT_CLEANUP AT_SETUP([OFPST_TABLE_FEATURES request - OF1.3]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 04 13 09 40 00 00 00 d5 00 0c 00 01 00 00 00 00 \ 09 30 00 00 00 00 00 00 74 61 62 6c 65 30 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff \ ff ff ff ff ff ff ff ff 00 00 00 03 00 0f 42 40 \ 00 00 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \ 00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \ 00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \ 00 01 00 2c 00 01 00 08 00 00 00 00 00 02 00 08 \ 00 00 00 00 00 03 00 08 00 00 00 00 00 04 00 08 \ 00 00 00 00 00 05 00 08 00 00 00 00 00 00 00 00 \ 00 02 01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c \ 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c \ 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c \ 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c \ 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c \ 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c \ 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c \ 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c \ 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c \ 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c \ 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac \ ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc \ bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc \ cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc \ dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec \ ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc \ fd 00 00 00 00 00 00 00 00 03 01 01 01 02 03 04 \ 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 \ 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 \ 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 \ 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 \ 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 \ 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 \ 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 \ 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 \ 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 \ 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 \ a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 \ b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 \ c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 \ d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 \ e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 \ f5 f6 f7 f8 f9 fa fb fc fd 00 00 00 00 00 00 00 \ 00 04 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \ 00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \ 00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \ 00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \ 00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \ 00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \ 00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \ 00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \ 00 00 00 00 00 00 00 00 00 05 00 84 00 00 00 08 \ 00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \ 00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \ 00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \ 00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \ 00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \ 00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \ 00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \ 00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \ 00 06 00 84 00 00 00 08 00 00 00 00 00 0b 00 08 \ 00 00 00 00 00 0c 00 08 00 00 00 00 00 0f 00 08 \ 00 00 00 00 00 10 00 08 00 00 00 00 00 11 00 08 \ 00 00 00 00 00 12 00 08 00 00 00 00 00 13 00 08 \ 00 00 00 00 00 14 00 08 00 00 00 00 00 15 00 08 \ 00 00 00 00 00 16 00 08 00 00 00 00 00 17 00 08 \ 00 00 00 00 00 18 00 08 00 00 00 00 00 19 00 08 \ 00 00 00 00 00 1a 00 08 00 00 00 00 00 1b 00 08 \ 00 00 00 00 00 00 00 00 00 07 00 84 00 00 00 08 \ 00 00 00 00 00 0b 00 08 00 00 00 00 00 0c 00 08 \ 00 00 00 00 00 0f 00 08 00 00 00 00 00 10 00 08 \ 00 00 00 00 00 11 00 08 00 00 00 00 00 12 00 08 \ 00 00 00 00 00 13 00 08 00 00 00 00 00 14 00 08 \ 00 00 00 00 00 15 00 08 00 00 00 00 00 16 00 08 \ 00 00 00 00 00 17 00 08 00 00 00 00 00 18 00 08 \ 00 00 00 00 00 19 00 08 00 00 00 00 00 1a 00 08 \ 00 00 00 00 00 1b 00 08 00 00 00 00 00 00 00 00 \ 00 08 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \ 80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \ 00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \ 00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \ 80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \ 80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \ 80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \ 80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \ 80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \ 80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \ 80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \ 80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \ 80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \ 80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \ 00 0a 00 dc 80 00 4c 08 00 01 3e 04 00 01 40 04 \ 80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \ 00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \ 00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \ 80 00 08 06 80 00 06 06 80 00 0a 02 00 00 08 02 \ 80 00 0c 02 80 00 0e 01 80 00 44 04 80 00 46 01 \ 80 00 48 01 80 00 16 04 80 00 18 04 80 00 34 10 \ 80 00 36 10 80 00 38 04 80 00 14 01 00 00 0a 01 \ 80 00 10 01 80 00 12 01 00 01 3a 01 00 01 34 01 \ 80 00 2a 02 80 00 2c 04 80 00 2e 04 80 00 30 06 \ 80 00 32 06 80 00 1a 02 80 00 1c 02 00 01 44 02 \ 80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \ 80 00 26 01 80 00 28 01 80 00 3a 01 80 00 3c 01 \ 80 00 3e 10 80 00 40 06 80 00 42 06 00 00 00 00 \ 00 0c 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \ 80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \ 00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \ 00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \ 80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \ 80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \ 80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \ 80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \ 80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \ 80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \ 80 00 22 02 80 00 24 02 00 0d 00 a8 80 00 4c 08 \ 00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \ 80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \ 00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \ 00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \ 00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \ 80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \ 80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \ 00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \ 80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \ 80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \ 00 0e 00 a8 80 00 4c 08 00 01 3e 04 00 01 40 04 \ 80 00 04 08 00 00 00 02 80 00 00 04 00 01 42 04 \ 00 01 00 04 00 01 02 04 00 01 04 04 00 01 06 04 \ 00 01 08 04 00 01 0a 04 00 01 0c 04 00 01 0e 04 \ 80 00 08 06 80 00 06 06 00 00 08 02 80 00 0c 02 \ 80 00 0e 01 80 00 44 04 80 00 46 01 80 00 16 04 \ 80 00 18 04 80 00 34 10 80 00 36 10 00 00 0a 01 \ 80 00 10 01 80 00 12 01 00 01 3a 01 80 00 2a 02 \ 80 00 2c 04 80 00 2e 04 80 00 30 06 80 00 32 06 \ 80 00 1a 02 80 00 1c 02 80 00 1e 02 80 00 20 02 \ 80 00 22 02 80 00 24 02 00 0f 00 a8 80 00 4c 08 \ 00 01 3e 04 00 01 40 04 80 00 04 08 00 00 00 02 \ 80 00 00 04 00 01 42 04 00 01 00 04 00 01 02 04 \ 00 01 04 04 00 01 06 04 00 01 08 04 00 01 0a 04 \ 00 01 0c 04 00 01 0e 04 80 00 08 06 80 00 06 06 \ 00 00 08 02 80 00 0c 02 80 00 0e 01 80 00 44 04 \ 80 00 46 01 80 00 16 04 80 00 18 04 80 00 34 10 \ 80 00 36 10 00 00 0a 01 80 00 10 01 80 00 12 01 \ 00 01 3a 01 80 00 2a 02 80 00 2c 04 80 00 2e 04 \ 80 00 30 06 80 00 32 06 80 00 1a 02 80 00 1c 02 \ 80 00 1e 02 80 00 20 02 80 00 22 02 80 00 24 02 \ "], [0], [OFPST_TABLE_FEATURES reply (OF1.3) (xid=0xd5): flags=[[more]] table 0 ("table0"): metadata: match=0xffffffffffffffff write=0xffffffffffffffff max_entries=1000000 instructions (table miss and others): next tables: 1-253 instructions: apply_actions clear_actions write_actions write_metadata goto_table Write-Actions and Apply-Actions features: actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue supported on Set-Field: tun_{id,src,dst} metadata in_{port,port_oxm} pkt_mark reg0...reg7 eth_{src,dst} vlan_{tci,vid,pcp} mpls_{label,tc} ip_{src,dst} ipv6_{src,dst} nw_tos ip_dscp nw_{ecn,ttl} arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} matching: exact match or wildcard: tun_{id,src,dst} metadata in_{port,port_oxm} pkt_mark reg0...reg7 eth_{src,dst,type} vlan_{tci,vid,pcp} mpls_{label,tc,bos} ip_{src,dst} ipv6_{src,dst,label} nw_{proto,tos} ip_dscp nw_{ecn,ttl} ip_frag arp_{op,spa,tpa,sha,tha} tcp_{src,dst,flags} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll} ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REQUEST (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REQUEST - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '02 14 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REQUEST (OF1.1) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REQUEST - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '03 14 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REQUEST (OF1.2) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REQUEST - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '04 14 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REQUEST (OF1.3) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REPLY - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '01 13 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REPLY (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REPLY - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '02 15 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REPLY (OF1.1) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REPLY - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '03 15 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REPLY (OF1.2) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_BARRIER_REPLY - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print '04 15 00 08 00 00 00 01'], [0], [dnl OFPT_BARRIER_REPLY (OF1.3) (xid=0x1): ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "01 14 00 0c 00 00 00 01 00 01 00 00"], [0], [dnl OFPT_QUEUE_GET_CONFIG_REQUEST (xid=0x1): port=1 ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 16 00 10 00 00 00 01 00 00 00 01 00 00 00 00"], [0], [dnl OFPT_QUEUE_GET_CONFIG_REQUEST (OF1.2) (xid=0x1): port=1 ]) AT_CLEANUP AT_SETUP([OFPST_QUEUE_DESC request - OF1.4]) AT_KEYWORDS([ofp-print OFPT_QUEUE_GET_CONFIG_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 05 12 00 18 00 00 00 01 00 0f 00 00 00 00 00 00 \ 00 00 00 01 00 00 00 02"], [0], [OFPST_QUEUE_DESC request (OF1.4) (xid=0x1): port=1 queue=2 ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "01 15 00 40 00 00 00 01 \ 00 01 00 00 00 00 00 00 \ 00 00 55 55 00 28 00 00 \ 00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \ 00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \ 00 00 44 44 00 08 00 00 \ "], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (xid=0x1): port=1 queue 17476: queue 21845: min_rate:50.0% max_rate:75.0% ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "02 17 00 40 00 00 00 01 \ 00 00 00 01 00 00 00 00 \ 00 00 55 55 00 28 00 00 \ 00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \ 00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \ 00 00 44 44 00 08 00 00 \ "], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.1) (xid=0x1): port=1 queue 17476: queue 21845: min_rate:50.0% max_rate:75.0% ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "03 17 00 50 00 00 00 01 \ 00 00 00 01 00 00 00 00 \ 00 00 55 55 00 00 00 01 00 30 00 00 00 00 00 00 \ 00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \ 00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \ 00 00 44 44 00 08 00 01 00 10 00 00 00 00 00 00 \ "], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.2) (xid=0x1): port=1 queue 17476: queue 21845: min_rate:50.0% max_rate:75.0% ]) AT_CLEANUP AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "04 17 00 50 00 00 00 01 \ 00 00 00 01 00 00 00 00 \ 00 00 55 55 00 00 00 01 00 30 00 00 00 00 00 00 \ 00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \ 00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \ 00 00 44 44 00 08 00 01 00 10 00 00 00 00 00 00 \ "], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.3) (xid=0x1): port=1 queue 17476: queue 21845: min_rate:50.0% max_rate:75.0% ]) AT_CLEANUP # OF1.4 renamed OFPT_QUEUE_GET_CONFIG_REPLY to OFPST_QUEUE_DESC. AT_SETUP([OFPST_QUEUE_DESC reply - OF1.4]) AT_KEYWORDS([ofp-print OFPT_QUEUE_GET_CONFIG_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 48 00 00 00 01 00 0f 00 00 00 00 00 00 \ 00 00 00 01 00 00 55 55 00 20 00 00 00 00 00 00 \ 00 01 00 08 01 f4 00 00 \ 00 02 00 08 02 ee 00 00 \ 00 00 00 02 00 00 44 44 00 18 00 00 00 00 00 00 \ 00 02 00 08 00 64 00 00 \ "], [0], [dnl OFPST_QUEUE_DESC reply (OF1.4) (xid=0x1): port=1 queue 21845: min_rate:50.0% max_rate:75.0% port=2 queue 17476: max_rate:10.0% ]) AT_CLEANUP AT_SETUP([OFPT_SET_ASYNC - OF1.3]) AT_KEYWORDS([ofp-print]) dnl This message has bit 12 set for the PACKET_IN messages (primary and dnl secondary). dnl Those aren't supported bits so they get silently ignored on decoding. dnl That seems reasonable because OF1.3 doesn't define any error codes for dnl OFPT_SET_ASYNC. AT_CHECK([ovs-ofctl ofp-print "\ 04 1c 00 20 00 00 00 00 00 00 10 05 00 00 10 07 \ 00 00 00 03 00 00 00 07 00 00 00 00 00 00 00 03 \ "], [0], [dnl OFPT_SET_ASYNC (OF1.3) (xid=0x0): primary: PACKET_IN: no_match invalid_ttl PORT_STATUS: add delete FLOW_REMOVED: (off) ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) secondary: PACKET_IN: no_match action invalid_ttl PORT_STATUS: add delete modify FLOW_REMOVED: idle hard ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) ]) AT_CLEANUP AT_SETUP([OFPT_ROLE_REQUEST - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 18 00 18 00 00 00 02 00 00 00 02 00 00 00 00 \ 00 00 00 00 00 00 00 03 \ "], [0], [dnl OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=primary generation_id=3 ]) AT_CLEANUP AT_SETUP([OFPT_ROLE_REQUEST - nochange - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 18 00 18 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=nochange ]) AT_CLEANUP AT_SETUP([NXT_ROLE_REQUEST]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 14 00 00 00 02 00 00 23 20 00 00 00 0a \ 00 00 00 01 \ "], [0], [dnl NXT_ROLE_REQUEST (xid=0x2): role=primary ]) AT_CLEANUP AT_SETUP([OFPT_ROLE_REPLY - OF1.2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 03 19 00 18 00 00 00 02 00 00 00 03 00 00 00 00 \ 12 34 56 78 ab cd ef 90 \ "], [0], [dnl OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=secondary generation_id=1311768467750121360 ]) AT_CLEANUP AT_SETUP([NXT_ROLE_REPLY]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 14 00 00 00 02 00 00 23 20 00 00 00 0b \ 00 00 00 02 \ "], [0], [dnl NXT_ROLE_REPLY (xid=0x2): role=secondary ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, experimenter - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 20 00 00 00 0a 4f 4e 46 00 00 00 07 77 \ 00 00 00 02 02 00 00 00 ff ff ff ff ff ff ff ff \ "], [0], [dnl ONFT_ROLE_STATUS (OF1.3) (xid=0xa): role=primary reason=experimenter_data_changed ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, config - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 20 00 00 00 0a 4f 4e 46 00 00 00 07 77 \ 00 00 00 02 01 00 00 00 ff ff ff ff ff ff ff ff \ "], [0], [dnl ONFT_ROLE_STATUS (OF1.3) (xid=0xa): role=primary reason=configuration_changed ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, config,generation - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 20 00 00 00 0a 4f 4e 46 00 00 00 07 77 \ 00 00 00 02 01 00 00 00 00 00 00 00 00 00 00 10 \ "], [0], [dnl ONFT_ROLE_STATUS (OF1.3) (xid=0xa): role=primary generation_id=16 reason=configuration_changed ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, experimenter - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1e 00 18 00 00 00 0a \ 00 00 00 02 02 00 00 00 ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=primary reason=experimenter_data_changed ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, config - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1e 00 18 00 00 00 0a \ 00 00 00 02 01 00 00 00 ff ff ff ff ff ff ff ff \ "], [0], [dnl OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=primary reason=configuration_changed ]) AT_CLEANUP AT_SETUP([OFP_ROLE_STATUS - primary, config,generation - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1e 00 18 00 00 00 0a \ 00 00 00 02 01 00 00 00 00 00 00 00 00 00 00 10 \ "], [0], [dnl OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=primary generation_id=16 reason=configuration_changed ]) AT_CLEANUP AT_SETUP([OFP_REQUESTFORWARD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 20 00 18 00 00 00 02 \ 05 0f 00 10 02 00 00 00 \ 00 00 00 00 00 00 00 01 \ "], [0], [dnl OFPT_REQUESTFORWARD (OF1.4) (xid=0x2): reason=group_mod ADD group_id=1,type=all ]) AT_CLEANUP AT_SETUP([OFP_REQUESTFORWARD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 20 00 18 00 00 00 02 \ 05 0f 00 10 02 00 00 00 \ 00 01 01 00 00 00 00 01 \ "], [0], [dnl OFPT_REQUESTFORWARD (OF1.4) (xid=0x2): reason=group_mod MOD group_id=1,type=select ]) AT_CLEANUP AT_SETUP([OFP_REQUESTFORWARD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 20 00 18 00 00 00 02 \ 05 1d 00 10 02 00 00 00 \ 00 00 00 00 00 00 00 01 \ "], [0], [dnl OFPT_REQUESTFORWARD (OF1.4) (xid=0x2): reason=meter_mod ADD meter=1 bands= ]) AT_CLEANUP AT_SETUP([OFP_REQUESTFORWARD - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 20 00 18 00 00 00 02 \ 05 1d 00 10 02 00 00 00 \ 00 01 01 00 00 00 00 01 \ "], [0], [dnl OFPT_REQUESTFORWARD (OF1.4) (xid=0x2): reason=meter_mod MOD meter=1 flags:0x100 bands= ]) AT_CLEANUP AT_SETUP([NXT_REQUESTFORWARD - inner NXT_GROUP_MOD]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ dnl OF version 1.0; type=extension: 01 04 \ dnl size in bytes: 00 b8 \ dnl xid: 00 00 00 02 \ dnl Nicira vendor number: 00 00 23 20 \ dnl subtype (message id number = 132 in this case) 00 00 00 84 \ dnl inner msg copied and pasted from NXT_GROUP_MOD test above: 01 04 00 a8 00 00 00 02 00 00 23 20 00 00 00 1f 00 00 01 00 87 65 43 21 \ 00 60 00 00 ff ff ff ff 00 20 00 08 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 00 20 00 08 00 00 00 01 \ 00 00 00 08 00 02 00 00 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 02 \ 00 20 00 08 00 00 00 02 00 00 00 08 00 03 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 03 ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 \ "], [0], [dnl NXT_REQUESTFORWARD (xid=0x2): reason=group_mod ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([NXT_REQUESTFORWARD - inner OFPT_GROUP_MOD - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ dnl OF Version 1.1; type=extension: 02 04 \ dnl size in bytes: 00 80 \ dnl xid: 00 00 00 02 \ dnl Nicira vendor number: 00 00 23 20 \ dnl subtype (message id number = 132 in this case) 00 00 00 84 \ dnl inner msg copied and pasted from OFPT_GROUP_MOD OF1.1 test above: 02 0f 00 70 11 22 33 44 00 00 01 00 87 65 43 21 \ 00 20 00 64 00 00 00 01 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 02 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 03 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ "], [0], [dnl NXT_REQUESTFORWARD (OF1.1) (xid=0x2): reason=group_mod ADD group_id=2271560481,type=select,bucket=weight:100,watch_port:1,actions=output:1,bucket=weight:200,watch_port:2,actions=output:2,bucket=weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([ONFT_REQUESTFORWARD - inner OFPT_METER_MOD - OF1.3]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ dnl OF Version 1.3; type=extension: 04 04 \ dnl size in bytes: 00 30 \ dnl xid: 00 00 00 02 \ dnl ONF vendor number: 4F 4E 46 00 \ dnl subtype (message id number = 2350 in this case) 00 00 09 2e \ dnl inner msg copied and pasted from the valid OFPT_METER_MOD OF1.3 test: 04 1d 00 20 00 00 00 02 00 00 00 0d 00 00 00 05 \ 00 01 00 10 00 00 04 00 00 00 00 80 00 00 00 00 \ "], [0], [dnl ONFT_REQUESTFORWARD (OF1.3) (xid=0x2): reason=meter_mod ADD meter=5 kbps burst stats bands= type=drop rate=1024 burst_size=128 ]) AT_CLEANUP AT_SETUP([NXT_SET_PACKET_IN]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 14 00 00 00 02 00 00 23 20 00 00 00 10 \ 00 00 00 01 \ "], [0], [dnl NXT_SET_PACKET_IN_FORMAT (xid=0x2): format=nxt_packet_in ]) AT_CLEANUP AT_SETUP([NXT_PACKET_IN]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 ba 00 00 00 00 00 00 23 20 00 00 00 11 \ ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \ 00 4e 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \ 20 08 00 00 00 00 00 00 00 06 00 01 00 04 00 00 \ 00 01 00 01 02 04 00 00 00 02 00 01 04 04 00 00 \ 00 03 00 01 06 04 00 00 00 04 00 01 08 04 00 00 \ 00 05 80 00 05 10 5a 5a 5a 5a 5a 5a 5a 5a ff ff \ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \ 80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \ 00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \ 00 55 00 56 00 00 00 00 00 00 00 00 50 02 00 00 \ 31 6d 00 00 00 00 00 00 00 00 \ "], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,metadata=0x5a5a5a5a5a5a5a5a,in_port=1 (via action) data_len=64 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86,tcp_flags=syn tcp_csum:316d ]) AT_CLEANUP AT_SETUP([NXT_PACKET_IN, with hex output of packet data]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 ba 00 00 00 00 00 00 23 20 00 00 00 11 \ ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \ 00 4e 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \ 20 08 00 00 00 00 00 00 00 06 00 01 00 04 00 00 \ 00 01 00 01 02 04 00 00 00 02 00 01 04 04 00 00 \ 00 03 00 01 06 04 00 00 00 04 00 01 08 04 00 00 \ 00 05 80 00 05 10 5a 5a 5a 5a 5a 5a 5a 5a ff ff \ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \ 80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \ 00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \ 00 55 00 56 00 00 00 00 00 00 00 00 50 01 00 00 \ 31 6d 00 00 00 00 00 00 00 00 \ " 3], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,metadata=0x5a5a5a5a5a5a5a5a,in_port=1 (via action) data_len=64 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:316d 00000000 82 82 82 82 82 82 80 81-81 81 81 81 81 00 00 50 00000010 08 00 45 00 00 28 00 00-00 00 00 06 32 05 53 53 00000020 53 53 54 54 54 54 00 55-00 56 00 00 00 00 00 00 00000030 00 00 50 01 00 00 31 6d-00 00 00 00 00 00 00 00 ]) AT_CLEANUP AT_SETUP([NX_PACKET_IN2]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print " 01 04 0098 00000000 00002320 0000001e 0000 0034 82 82 82 82 82 82 80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 00 55 00 56 00 00 00 00 00 00 00000000 0001 0008 00000040 0002 0008 00000114 0003 0005 07 000000 0004 0010 00000000 fedcba9876543210 0005 0005 01 000000 0006 0010 80000408 5a5a5a5a5a5a5a5a 0007 0009 0102030405 00000000000000 " ], [0], [dnl NXT_PACKET_IN2 (xid=0x0): table_id=7 cookie=0xfedcba9876543210 total_len=64 metadata=0x5a5a5a5a5a5a5a5a (via action) data_len=48 buffer=0x00000114 userdata=01.02.03.04.05 ip,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no ]) AT_CLEANUP AT_SETUP([NXT_SET_ASYNC_CONFIG]) AT_KEYWORDS([ofp-print]) dnl This message has bit 12 set for the PACKET_IN messages (primary and secondary). dnl Those aren't supported bits so they get silently ignored on decoding. AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 28 00 00 00 00 00 00 23 20 00 00 00 13 \ 00 00 10 05 00 00 10 07 00 00 00 03 00 00 00 07 \ 00 00 00 00 00 00 00 03 \ "], [0], [dnl NXT_SET_ASYNC_CONFIG (xid=0x0): primary: PACKET_IN: no_match invalid_ttl PORT_STATUS: add delete FLOW_REMOVED: (off) ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) secondary: PACKET_IN: no_match action invalid_ttl PORT_STATUS: add delete modify FLOW_REMOVED: idle hard ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) ]) AT_CLEANUP AT_SETUP([OFPT_SET_ASYNC_CONFIG]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1c 00 38 00 00 00 02 00 00 00 08 00 00 00 05 \ 00 01 00 08 00 00 00 02 00 02 00 08 00 00 00 02 \ 00 03 00 08 00 00 00 05 00 04 00 08 00 00 00 1c \ 00 05 00 08 00 00 00 05 \ "], [0], [dnl OFPT_SET_ASYNC (OF1.4) (xid=0x2): primary: PACKET_IN: action PORT_STATUS: add modify FLOW_REMOVED: idle delete ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) secondary: PACKET_IN: no_match invalid_ttl PORT_STATUS: delete FLOW_REMOVED: delete group_delete meter_delete ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) ]) AT_CLEANUP AT_SETUP([OFPT_SET_ASYNC_CONFIG - invalid mask - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1c 00 38 00 00 00 02 00 00 00 08 00 00 00 40 \ 00 01 00 08 00 00 00 02 00 02 00 08 00 00 00 02 \ 00 03 00 08 00 00 00 05 00 04 00 08 00 00 00 1c \ 00 05 00 08 00 00 00 05 \ "], [0], [dnl OFPT_SET_ASYNC (OF1.4) (xid=0x2): ***decode error: OFPACFC_INVALID*** 00000000 05 1c 00 38 00 00 00 02-00 00 00 08 00 00 00 40 |...8...........@| 00000010 00 01 00 08 00 00 00 02-00 02 00 08 00 00 00 02 |................| 00000020 00 03 00 08 00 00 00 05-00 04 00 08 00 00 00 1c |................| 00000030 00 05 00 08 00 00 00 05- |........ | ], [stderr]) AT_CHECK([sed 's/.*|//' stderr], [0], [bad value 0x40 for PACKET_IN (allowed mask 0x3f) ]) AT_CLEANUP AT_SETUP([OFPT_SET_ASYNC_CONFIG - unsupported configuration - OF1.4]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 05 1c 00 38 00 00 00 02 00 00 00 08 00 00 00 05 \ 00 11 00 08 00 00 00 02 00 02 00 08 00 00 00 02 \ 00 03 00 08 00 00 00 05 00 04 00 08 00 00 00 1c \ 00 05 00 08 00 00 00 05\ "], [0], [dnl OFPT_SET_ASYNC (OF1.4) (xid=0x2): ***decode error: OFPACFC_UNSUPPORTED*** 00000000 05 1c 00 38 00 00 00 02-00 00 00 08 00 00 00 05 |...8............| 00000010 00 11 00 08 00 00 00 02-00 02 00 08 00 00 00 02 |................| 00000020 00 03 00 08 00 00 00 05-00 04 00 08 00 00 00 1c |................| 00000030 00 05 00 08 00 00 00 05- |........ | ], [stderr]) AT_CHECK([sed 's/.*|//' stderr], [0], [unknown async config property type 17 ]) AT_CLEANUP AT_SETUP([NXT_SET_CONTROLLER_ID]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 14 \ 00 00 00 00 00 00 00 7b \ "], [0], [dnl NXT_SET_CONTROLLER_ID (xid=0x3): id=123 ]) AT_CLEANUP AT_SETUP([FLOW_MONITOR_CANCEL]) AT_KEYWORDS([ofp-print]) dnl OpenFlow 1.0-1.2 AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \ 01 02 30 40 \ "], [0], [dnl NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640 ]) dnl OpenFlow 1.3 AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 14 00 00 00 06 4f 4e 46 00 00 00 07 4e \ 01 02 30 40 \ "], [0], [dnl ONFT_FLOW_MONITOR_CANCEL (OF1.3) (xid=0x6): id=16920640 ]) dnl OpenFlow 1.4+ AT_CHECK([ovs-ofctl ofp-print "\ 05 12 00 28 00 00 00 04 00 10 00 00 00 00 00 00 \ 01 02 30 40 00 00 00 00 00 00 00 00 00 00 00 02 \ 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_FLOW_MONITOR request (OF1.4) (xid=0x4): id=16920640 command=delete ]) AT_CLEANUP AT_SETUP([FLOW_MONITOR_PAUSED]) AT_KEYWORDS([ofp-print]) dnl OpenFlow 1.0-1.2 AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \ "], [0], [dnl NXT_FLOW_MONITOR_PAUSED (xid=0x3): ]) dnl OpenFlow 1.3 AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 4F \ "], [0], [dnl ONFT_FLOW_MONITOR_PAUSED (OF1.3) (xid=0x3): ]) dnl OpenFlow 1.4+ AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 \ 00 08 00 05 00 00 00 00 \ "], [0], [dnl OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=PAUSED ]) AT_CLEANUP AT_SETUP([FLOW_MONITOR_RESUMED]) AT_KEYWORDS([ofp-print]) dnl OpenFlow 1.0-1.2 AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \ "], [0], [dnl NXT_FLOW_MONITOR_RESUMED (xid=0x3): ]) dnl OpenFlow 1.3 AT_CHECK([ovs-ofctl ofp-print "\ 04 04 00 10 00 00 00 03 4f 4e 46 00 00 00 07 50 \ "], [0], [dnl ONFT_FLOW_MONITOR_RESUMED (OF1.3) (xid=0x3): ]) dnl OpenFlow 1.4+ AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 18 00 00 00 00 00 10 00 00 00 00 00 00 00 08 00 06 00 00 00 00 "], [0], [dnl OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=RESUMED ]) AT_CLEANUP AT_SETUP([NXT_SET_FLOW_FORMAT]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 14 00 00 00 02 00 00 23 20 00 00 00 0c \ 00 00 00 02 \ "], [0], [dnl NXT_SET_FLOW_FORMAT (xid=0x2): format=nxm ]) AT_CLEANUP # The flow is formatted with cls_rule_format() for the low-verbosity case. AT_SETUP([NXT_FLOW_MOD, low verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \ ff ff ff ff 00 10 00 00 00 14 00 00 00 00 00 00 \ 00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \ 00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \ 00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \ " 2], [0], [dnl NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 out_port:16 actions=load:0x5->NXM_NX_REG0[[]] ]) AT_CLEANUP # The flow is formatted with ofp10_match_to_string() for the # low-verbosity case. AT_SETUP([NXT_FLOW_MOD, high verbosity]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \ ff ff ff ff 01 00 00 00 00 14 00 00 00 00 00 00 \ 00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \ 00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \ 00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \ " 3], [0], [dnl NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007b) out_port:256 actions=load:0x5->NXM_NX_REG0[[]] ]) AT_CLEANUP AT_SETUP([NXT_GROUP_MOD add - OF1.0]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 a8 00 00 00 02 00 00 23 20 00 00 00 1f \ 00 00 01 00 87 65 43 21 \ 00 60 00 00 ff ff ff ff \ \ 00 20 00 08 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 \ \ 00 20 00 08 00 00 00 01 00 00 00 08 00 02 00 00 \ 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 02 \ 00 20 00 08 00 00 00 02 00 00 00 08 00 03 00 00 \ 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 03 \ ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 07 \ "], [0], [dnl NXT_GROUP_MOD (xid=0x2): ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPT_GROUP_MOD - OF1.1]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 02 0f 00 70 11 22 33 44 00 00 01 00 87 65 43 21 \ 00 20 00 64 00 00 00 01 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 02 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 20 00 c8 00 00 00 03 ff ff ff ff 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_GROUP_MOD (OF1.1) (xid=0x11223344): ADD group_id=2271560481,type=select,bucket=weight:100,watch_port:1,actions=output:1,bucket=weight:200,watch_port:2,actions=output:2,bucket=weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPT_GROUP_MOD add - OF1.5]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0f 00 b8 11 22 33 44 00 00 01 00 87 65 43 21 \ 00 78 00 00 ff ff ff ff 00 28 00 10 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 \ 00 28 00 10 00 00 00 01 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 02 00 28 00 10 00 00 00 02 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 03 \ ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 07 \ "], [0], [dnl OFPT_GROUP_MOD (OF1.5) (xid=0x11223344): ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPT_GROUP_MOD insert bucket - OF1.5]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 06 0f 00 90 11 22 33 44 00 03 01 00 87 65 43 21 \ 00 78 00 00 ff ff ff fd 00 28 00 10 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 \ 00 28 00 10 00 00 00 01 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 02 00 28 00 10 00 00 00 02 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 03 \ "], [0], [dnl OFPT_GROUP_MOD (OF1.5) (xid=0x11223344): INSERT_BUCKET command_bucket_id:first,group_id=2271560481,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([NXT_FLOW_REMOVED]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 78 00 00 00 00 00 00 23 20 00 00 00 0e \ 00 00 00 00 00 00 00 00 ff ff 00 02 00 00 00 06 \ 01 6e 36 00 00 05 00 3c 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 03 00 00 \ 02 06 50 54 00 00 00 06 00 00 04 06 50 54 00 00 \ 00 05 00 00 06 02 08 06 00 00 08 02 00 00 00 00 \ 1e 02 00 02 00 00 20 04 c0 a8 00 01 00 00 22 04 \ c0 a8 00 02 00 00 00 00 \ "], [0], [dnl NXT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2 reason=idle table_id=1 duration6.024s idle5 pkts1 bytes60 ]) AT_CLEANUP AT_SETUP([NXT_FLOW_MOD_TABLE_ID]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 18 01 02 03 04 00 00 23 20 00 00 00 0f \ 01 00 00 00 00 00 00 00 \ "], [0], [dnl NXT_FLOW_MOD_TABLE_ID (xid=0x1020304): enable ]) AT_CLEANUP AT_SETUP([NXT_RESUME]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 0038 01020304 00002320 0000001c \ 0000 0012 ffffffffffff 102030405060 1234 000000000000 \ 0006 000a 00000002 fffd 000000000000 "], [0], [dnl NXT_RESUME (xid=0x1020304): total_len=14 in_port=CONTROLLER (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=10:20:30:40:50:60,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x1234 ]) AT_CLEANUP AT_SETUP([NXST_FLOW request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 20 00 00 00 04 ff ff 00 00 00 00 23 20 \ 00 00 00 00 00 00 00 00 ff ff 00 00 ff 00 00 00 \ "], [0], [dnl NXST_FLOW request (xid=0x4): ]) AT_CLEANUP AT_SETUP([NXST_FLOW reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 08 18 00 00 00 04 ff ff 00 00 00 00 23 20 \ 00 00 00 00 00 00 00 00 00 88 00 00 00 00 00 01 \ 02 dc 6c 00 ff ff 00 05 00 00 00 4c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 03 00 00 \ 02 06 50 54 00 00 00 06 00 00 04 06 50 54 00 00 \ 00 05 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 01 00 00 10 04 c0 \ a8 00 02 00 00 0c 01 06 00 00 12 02 09 e7 00 00 \ 14 02 00 00 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 88 00 00 00 00 00 03 32 11 62 00 ff ff 00 05 \ 00 00 00 4c 00 03 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 03 00 00 02 06 50 54 00 00 00 06 \ 00 00 04 06 50 54 00 00 00 05 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 01 00 00 10 04 c0 a8 00 02 00 00 0c 01 06 \ 00 00 12 02 09 e4 00 00 14 02 00 00 00 00 00 00 \ 00 00 00 08 00 01 00 00 00 88 00 00 00 00 00 02 \ 33 f9 aa 00 ff ff 00 05 00 00 00 4c 00 05 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 01 00 00 \ 02 06 50 54 00 00 00 05 00 00 04 06 50 54 00 00 \ 00 06 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 02 00 00 10 04 c0 \ a8 00 01 00 00 0c 01 06 00 00 12 02 00 00 00 00 \ 14 02 09 e5 00 00 00 00 00 00 00 08 00 03 00 00 \ 00 88 00 00 00 00 00 04 2d 0f a5 00 ff ff 00 05 \ 00 00 00 4c 00 01 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 03 00 00 02 06 50 54 00 00 00 06 \ 00 00 04 06 50 54 00 00 00 05 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 01 00 00 10 04 c0 a8 00 02 00 00 0c 01 06 \ 00 00 12 02 09 e3 00 00 14 02 00 00 00 00 00 00 \ 00 00 00 08 00 01 00 00 00 88 00 00 00 00 00 02 \ 34 73 bc 00 ff ff 00 05 00 0a 00 4c 00 03 00 03 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 03 00 00 \ 02 06 50 54 00 00 00 06 00 00 04 06 50 54 00 00 \ 00 05 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 01 00 00 10 04 c0 \ a8 00 02 00 00 0c 01 06 00 00 12 02 09 e5 00 00 \ 14 02 00 00 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 88 00 00 00 00 00 05 28 0d e8 00 ff ff 00 05 \ 00 00 00 4c 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 03 00 00 02 06 50 54 00 00 00 06 \ 00 00 04 06 50 54 00 00 00 05 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 01 00 00 10 04 c0 a8 00 02 00 00 0c 01 06 \ 00 00 12 02 09 e2 00 00 14 02 00 00 00 00 00 00 \ 00 00 00 08 00 01 00 00 00 88 00 00 00 00 00 01 \ 02 62 5a 00 ff ff 00 05 00 00 00 4c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 01 00 00 \ 02 06 50 54 00 00 00 05 00 00 04 06 50 54 00 00 \ 00 06 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 02 00 00 10 04 c0 \ a8 00 01 00 00 0c 01 06 00 00 12 02 00 00 00 00 \ 14 02 09 e7 00 00 00 00 00 00 00 08 00 03 00 00 \ 00 88 00 00 00 00 00 01 38 be 5e 00 ff ff 00 05 \ 00 00 00 4c 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 01 00 00 02 06 50 54 00 00 00 05 \ 00 00 04 06 50 54 00 00 00 06 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 02 00 00 10 04 c0 a8 00 01 00 00 0c 01 06 \ 00 00 12 02 00 00 00 00 14 02 09 e6 00 00 00 00 \ 00 00 00 08 00 03 00 00 00 88 00 00 00 00 00 04 \ 27 d0 df 00 ff ff 00 05 00 00 00 4c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 01 00 00 \ 02 06 50 54 00 00 00 05 00 00 04 06 50 54 00 00 \ 00 06 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 02 00 00 10 04 c0 \ a8 00 01 00 00 0c 01 06 00 00 12 02 00 00 00 00 \ 14 02 09 e3 00 00 00 00 00 00 00 08 00 03 00 00 \ 00 88 00 00 00 00 00 03 2c d2 9c 00 ff ff 00 05 \ 00 00 00 4c 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 01 00 00 02 06 50 54 00 00 00 05 \ 00 00 04 06 50 54 00 00 00 06 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 02 00 00 10 04 c0 a8 00 01 00 00 0c 01 06 \ 00 00 12 02 00 00 00 00 14 02 09 e4 00 00 00 00 \ 00 00 00 08 00 03 00 00 00 88 00 00 00 00 00 00 \ 0a 40 83 00 ff ff 00 05 00 00 00 4c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 03 00 00 \ 02 06 50 54 00 00 00 06 00 00 04 06 50 54 00 00 \ 00 05 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 01 00 00 10 04 c0 \ a8 00 02 00 00 0c 01 06 00 00 12 02 09 e8 00 00 \ 14 02 00 00 00 00 00 00 00 00 00 08 00 01 00 00 \ 00 88 00 00 00 00 00 05 25 31 7c 00 ff ff 00 05 \ 00 00 00 4c 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 01 00 00 02 06 50 54 00 00 00 05 \ 00 00 04 06 50 54 00 00 00 06 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 02 00 00 10 04 c0 a8 00 01 00 00 0c 01 06 \ 00 00 12 02 00 00 00 00 14 02 09 e2 00 00 00 00 \ 00 00 00 08 00 03 00 00 00 88 00 00 00 00 00 00 \ 04 c4 b4 00 ff ff 00 05 00 00 00 4c 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 3c 00 00 00 02 00 01 00 00 \ 02 06 50 54 00 00 00 05 00 00 04 06 50 54 00 00 \ 00 06 00 00 06 02 08 00 00 00 08 02 00 00 00 00 \ 0a 01 00 00 00 0e 04 c0 a8 00 02 00 00 10 04 c0 \ a8 00 01 00 00 0c 01 06 00 00 12 02 00 00 00 00 \ 14 02 09 e8 00 00 00 00 00 00 00 08 00 03 00 00 \ 00 88 00 00 00 00 00 01 39 38 70 00 ff ff 00 05 \ 00 00 00 4c 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 3c \ 00 00 00 02 00 03 00 00 02 06 50 54 00 00 00 06 \ 00 00 04 06 50 54 00 00 00 05 00 00 06 02 08 00 \ 00 00 08 02 00 00 00 00 0a 01 00 00 00 0e 04 c0 \ a8 00 01 00 00 10 04 c0 a8 00 02 00 00 0c 01 06 \ 00 00 12 02 09 e6 00 00 14 02 00 00 00 00 00 00 \ 00 00 00 08 00 01 00 00 00 60 00 00 00 00 00 e4 \ 2e 7d db 00 80 00 00 00 00 00 00 14 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 01 20 08 00 00 00 00 \ 00 00 01 c8 00 01 00 04 00 00 00 7b 00 00 00 00 \ ff ff 00 18 00 00 23 20 00 07 00 1f 00 01 00 04 \ 00 00 00 00 00 00 00 05 \ 00 30 01 00 00 00 0e 10 00 07 a1 20 80 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 64 00 00 00 00 00 00 19 00 \ "], [0], [[NXST_FLOW reply (xid=0x4): cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2535,tp_dst=0 actions=output:1 cookie=0x0, duration=3.840s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2532,tp_dst=0 actions=output:1 cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=4, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2533 actions=output:3 cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=0, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2531,tp_dst=0 actions=output:1 cookie=0x0, duration=2.880s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, hard_timeout=10, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2533,tp_dst=0 actions=output:1 cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2530,tp_dst=0 actions=output:1 cookie=0x0, duration=1.040s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2535 actions=output:3 cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2534 actions=output:3 cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2531 actions=output:3 cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2532 actions=output:3 cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2536,tp_dst=0 actions=output:1 cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2530 actions=output:3 cookie=0x0, duration=0.080s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2536 actions=output:3 cookie=0x0, duration=1.960s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2534,tp_dst=0 actions=output:1 cookie=0x0, duration=228.780s, table=0, n_packets=0, n_bytes=0, reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[] cookie=0x0, duration=3600.0005s, table=1, n_packets=100, n_bytes=6400, actions=drop ]]) AT_CLEANUP AT_SETUP([NXST_AGGREGATE request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 20 00 00 00 04 ff ff 00 00 00 00 23 20 \ 00 00 00 01 00 00 00 00 ff ff 00 00 ff 00 00 00 \ "], [0], [dnl NXST_AGGREGATE request (xid=0x4): ]) AT_CLEANUP AT_SETUP([NXST_AGGREGATE reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 30 00 00 00 04 ff ff 00 00 00 00 23 20 \ 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 07 \ 00 00 00 00 00 00 01 a4 00 00 00 07 00 00 00 00 \ "], [0], [dnl NXST_AGGREGATE reply (xid=0x4): packet_count=7 byte_count=420 flow_count=7 ]) AT_CLEANUP AT_SETUP([FLOW_MONITOR request]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) dnl OpenFlow 1.0-1.2 AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \ 00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \ 00 00 20 00 00 04 ff ff 00 06 02 00 00 00 00 00 00 00 00 02 00 01 00 00 \ "], [0], [dnl NXST_FLOW_MONITOR request (xid=0x4): id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1 id=8192 flags=delete table=2 in_port=1 ]) dnl OpenFlow 1.3 AT_CHECK([ovs-ofctl ofp-print "\ 04 12 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 \ 4f 4e 46 00 00 00 07 4e \ 00 00 10 00 00 3f 00 04 ff ff ff fe 01 00 00 00 00 01 00 04 00 00 00 00 \ 00 00 20 00 00 04 ff ff 00 00 00 02 01 00 00 00 00 01 00 04 00 00 00 00 \ "], [0], [dnl ONFST_FLOW_MONITOR request (OF1.3) (xid=0x6): id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1 id=8192 flags=delete out_port=2 table=1 ]) dnl OpenFlow 1.4+ AT_CHECK([ovs-ofctl ofp-print "\ 05 12 00 58 00 00 00 06 00 10 00 00 00 00 00 00 \ 00 00 10 00 ff ff ff fe ff ff ff ff 00 5f 00 00 00 01 00 04 00 00 00 00 \ 00 00 20 00 00 00 00 01 00 00 00 40 00 5f 01 01 00 01 00 04 00 00 00 00 \ 00 00 40 00 00 00 00 02 00 00 00 40 00 5f 02 02 00 01 00 04 00 00 00 00 \ "], [0], [dnl OFPST_FLOW_MONITOR request (OF1.4) (xid=0x6): id=4096 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=0 id=8192 flags=initial,add,delete,modify,actions,own out_port=1 out_group=64 table=1 id=16384 command=delete ]) AT_CLEANUP AT_SETUP([FLOW_MONITOR reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) dnl OpenFlow 1.0-1.2 AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \ 00 20 00 01 00 05 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \ 00 00 00 02 00 01 00 00 \ 00 08 00 03 00 01 86 a0 \ "], [0], [dnl NXST_FLOW_MONITOR reply (xid=0x4): event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16 cookie=0x123456789abcdef0 in_port=1 event=ABBREV xid=0x186a0 ]) dnl OpenFlow 1.3 AT_CHECK([ovs-ofctl ofp-print "\ 04 13 00 48 00 00 00 06 ff ff 00 00 00 00 00 00 4f 4e 46 00 00 00 07 4e \ 00 28 00 01 00 05 80 00 00 00 00 00 00 0c 00 00 \ 12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \ 00 00 00 01 00 00 00 00 \ 00 08 00 03 00 01 86 a0 \ "], [0], [dnl ONFST_FLOW_MONITOR reply (OF1.3) (xid=0x6): event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1 event=ABBREV xid=0x186a0 ]) dnl OpenFlow 1.4+ AT_CHECK([ovs-ofctl ofp-print "\ 05 13 00 40 00 00 00 00 00 10 00 00 00 00 00 00 \ 00 28 00 02 00 05 00 00 00 00 80 00 00 00 00 00 \ 12 34 56 78 9a bc de f0 00 01 00 0c 80 00 00 04 \ 00 00 00 01 00 00 00 00 \ 00 08 00 04 00 01 86 a0 \ "], [0], [dnl OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=DELETED reason=eviction table=0 cookie=0x123456789abcdef0 in_port=1 event=ABBREV xid=0x186a0 ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - atomic OPEN_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 00 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - ordered OPEN_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 00 00 02 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - atomic ordered OPEN_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 00 00 03 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - OPEN_REPLY]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 01 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=OPEN_REPLY flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - CLOSE_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 02 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - CLOSE_REPLY]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 03 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=CLOSE_REPLY flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - COMMIT_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 04 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - COMMIT_REPLY]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 05 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=COMMIT_REPLY flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - DISCARD_REQUEST]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 06 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=DISCARD_REQUEST flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_CONTROL - DISCARD_REPLY]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 21 00 10 00 00 00 00 \ 00 00 00 01 00 07 00 01 \ "], [0], [dnl OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x0): bundle_id=0x1 type=DISCARD_REPLY flags=atomic ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - verify xid]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 22 00 20 00 00 00 00 00 00 00 01 00 00 00 01 \ 05 00 00 08 00 00 00 01 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x0): ***decode error: OFPBFC_MSG_BAD_XID*** 00000000 05 22 00 20 00 00 00 00-00 00 00 01 00 00 00 01 |.". ............| 00000010 05 00 00 08 00 00 00 01-00 00 00 00 00 00 00 00 |................| ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - reject OFPT_HELLO]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\ 05 22 00 20 00 00 00 00 00 00 00 01 00 00 00 01 \ 05 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x0): ***decode error: OFPBFC_MSG_UNSUP*** 00000000 05 22 00 20 00 00 00 00-00 00 00 01 00 00 00 01 |.". ............| 00000010 05 00 00 10 00 00 00 00-00 00 00 00 00 00 00 00 |................| ], [dnl ofp_bundle|WARN|OFPT_HELLO message not allowed inside OFPT14_BUNDLE_ADD_MESSAGE ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - FLOW_MOD]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 22 00 a0 00 00 00 02 00 00 00 01 00 00 00 01 \ 05 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x2): bundle_id=0x1 flags=atomic OFPT_FLOW_MOD (OF1.4) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - PORT_MOD]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 05 22 00 38 00 00 00 03 00 00 00 01 00 00 00 01 \ 05 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \ 50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \ 00 00 00 08 00 00 00 01 "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x3): bundle_id=0x1 flags=atomic OFPT_PORT_MOD (OF1.4) (xid=0x3): port: 3: addr:50:54:00:00:00:01 config: PORT_DOWN mask: PORT_DOWN advertise: 10MB-HD ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - GROUP_MOD]) AT_KEYWORDS([ofp-print bundle]) AT_CHECK([ovs-ofctl ofp-print "\ 06 22 00 c8 00 00 00 03 00 00 00 01 00 00 00 01 \ 06 0f 00 b8 00 00 00 03 00 00 01 00 87 65 43 21 \ 00 78 00 00 ff ff ff ff 00 28 00 10 00 00 00 00 \ 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 \ 00 28 00 10 00 00 00 01 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 00 00 00 00 08 00 c8 00 00 \ 00 01 00 08 00 00 00 02 00 28 00 10 00 00 00 02 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 03 \ ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 \ 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 07 \ "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.5) (xid=0x3): bundle_id=0x1 flags=atomic OFPT_GROUP_MOD (OF1.5) (xid=0x3): ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ]) AT_CLEANUP AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - PACKET_OUT]) AT_KEYWORDS([ofp-print bundle packet-out]) AT_CHECK([ovs-ofctl ofp-print "\ 05 22 00 74 00 00 00 03 00 00 00 01 00 00 00 01 \ 05 0d 00 64 00 00 00 03 ff ff ff ff ff ff ff fe \ 00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \ 05 dc 00 00 00 00 00 00 50 54 00 00 00 05 50 54 \ 00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \ 00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \ 00 00 00 00 \ "], [0], [dnl OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x3): bundle_id=0x1 flags=atomic OFPT_PACKET_OUT (OF1.4) (xid=0x3): in_port=LOCAL actions=FLOOD data_len=60 tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75 ]) AT_CLEANUP AT_SETUP([NXST_IPFIX_BRIDGE - request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 18 00 00 00 02 \ ff ff 00 00 00 00 23 20 00 00 00 03 00 00 00 00 \ "], [0], [dnl NXST_IPFIX_BRIDGE request (xid=0x2): ]) AT_CLEANUP AT_SETUP([NXST_IPFIX_BRIDGE - reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 70 00 00 00 02 \ ff ff 00 00 00 00 23 20 00 00 00 03 00 00 00 00\ 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 10 \ 00 00 00 00 00 00 00 78 \ 00 00 00 00 00 00 00 f0 \ 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 a0 \ 00 00 00 00 00 00 00 02 \ 00 00 00 00 00 00 00 03 \ 00 00 00 00 00 00 00 04 \ 00 00 00 00 00 00 00 05 \ 00 00 00 00 00 00 00 00 \ "], [0], [dnl NXST_IPFIX_BRIDGE reply (xid=0x2): bridge ipfix: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4 pkts errs=160, ipv4 errs=2, ipv6 errs=3, tx errs=5 ]) AT_CLEANUP AT_SETUP([NXST_IPFIX_FLOW - request]) AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST]) AT_CHECK([ovs-ofctl ofp-print "\ 01 10 00 18 00 00 00 02 \ ff ff 00 00 00 00 23 20 00 00 00 04 00 00 00 00 \ "], [0], [dnl NXST_IPFIX_FLOW request (xid=0x2): ]) AT_CLEANUP AT_SETUP([NXST_IPFIX_FLOW - reply]) AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) AT_CHECK([ovs-ofctl ofp-print "\ 01 11 00 C8 00 00 00 02 \ ff ff 00 00 00 00 23 20 00 00 00 04 00 00 00 00\ 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 10 \ 00 00 00 00 00 00 00 78 \ 00 00 00 00 00 00 00 f0 \ 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 a0 \ 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 03 \ 00 00 00 00 00 00 00 04 \ 00 00 00 00 00 00 00 05 \ 00 00 00 01 00 00 00 00 \ 00 00 00 00 00 00 00 01 \ 00 00 00 00 00 00 00 10 \ 00 00 00 00 00 00 00 78 \ 00 00 00 00 00 00 00 f0 \ 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 a0 \ 00 00 00 10 00 00 00 02 \ 00 00 00 00 00 00 00 03 \ 00 00 00 00 00 00 00 04 \ 00 00 00 00 00 00 00 05 \ 00 00 00 02 00 00 00 00 \ "], [0], [dnl NXST_IPFIX_FLOW reply (xid=0x2): 2 ids id 1: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4 pkts errs=160, ipv4 errs=68719476738, ipv6 errs=3, tx errs=5 id 2: flows=1, current flows=16, sampled pkts=120, ipv4 ok=240, ipv6 ok=0, tx pkts=4 pkts errs=160, ipv4 errs=68719476738, ipv6 errs=3, tx errs=5 ]) AT_CLEANUP AT_SETUP([NXT_CT_FLUSH_ZONE]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 1d \ 00 00 00 00 00 00 00 0d \ "], [0], [dnl NXT_CT_FLUSH_ZONE (xid=0x3): zone_id=13 ]) AT_CLEANUP AT_SETUP([NXT_CT_FLUSH]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=13 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 03 00 08 00 00 00 ab \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 mark=0xab 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 04 00 08 00 00 00 cd \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 mark=0/0xcd 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 28 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 03 00 08 00 00 00 ab \ 00 04 00 08 00 00 00 cd \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 mark=0xab/0xcd 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 30 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 05 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ab 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 labels=0xffab00 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 30 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 06 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff cd 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 labels=0/0xffcd00 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 48 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 05 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ab 00 00 00 00 00 \ 00 06 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff cd 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 labels=0xffab00/0xffcd00 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 38 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 03 00 08 00 00 00 ab \ 00 05 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ab 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 mark=0xab labels=0xffab00 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 58 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 03 00 08 00 00 00 ab \ 00 04 00 08 00 00 00 cd \ 00 05 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ab 00 00 00 00 00 \ 00 06 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 ff cd 00 00 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 mark=0xab/0xcd labels=0xffab00/0xffcd00 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ 00 00 00 48 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ 00 02 00 08 00 50 00 00 \ 00 03 00 08 1f 90 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=13 'ct_nw_src=10.10.0.1,ct_nw_dst=10.10.0.2,ct_tp_src=80,ct_tp_dst=8080,ct_nw_proto=6' 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ 00 01 00 48 00 00 00 00 \ 00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ 00 03 00 08 00 50 00 00 \ 00 02 00 08 1f 90 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=13 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' 'ct_nw_src=10.10.0.2,ct_nw_dst=10.10.0.1,ct_tp_src=8080,ct_tp_dst=80' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 b0 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ 00 00 00 48 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ 00 02 00 08 00 50 00 00 \ 00 03 00 08 1f 90 00 00 \ 00 01 00 48 00 00 00 00 \ 00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ 00 03 00 08 00 50 00 00 \ 00 02 00 08 1f 90 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=13 'ct_nw_src=10.10.0.1,ct_nw_dst=10.10.0.2,ct_tp_src=80,ct_tp_dst=8080,ct_nw_proto=6' 'ct_nw_src=10.10.0.2,ct_nw_dst=10.10.0.1,ct_tp_src=8080,ct_tp_dst=80' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 b8 00 00 00 03 00 00 23 20 00 00 00 20 \ 01 \ 00 00 00 00 00 00 00 \ 00 00 00 50 00 00 00 00 \ 00 00 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ 00 01 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ 00 04 00 08 00 0a 00 00 \ 00 05 00 05 01 00 00 00 \ 00 06 00 05 02 00 00 00 \ 00 01 00 50 00 00 00 00 \ 00 01 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ 00 00 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ 00 04 00 08 00 0a 00 00 \ 00 05 00 05 03 00 00 00 \ 00 06 00 05 04 00 00 00 \ "], [0], [dnl NXT_CT_FLUSH (xid=0x3): zone=0 'ct_ipv6_src=fd18::ffff:abcd:1,ct_ipv6_dst=fd18::ffff:abcd:2,icmp_id=10,icmp_type=1,icmp_code=2,ct_nw_proto=1' 'ct_ipv6_src=fd18::ffff:abcd:1,ct_ipv6_dst=fd18::ffff:abcd:2,icmp_id=10,icmp_type=3,icmp_code=4' ]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 58 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ 00 00 00 38 00 00 00 00 \ 00 00 00 14 00 0a 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ " | grep -q OFPBPC_BAD_VALUE], [0]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 60 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 02 00 08 00 0d 00 00 \ 00 00 00 20 00 00 00 00 \ 00 00 00 14 00 0a 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ 00 01 00 20 00 00 00 00 \ 00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ " | grep -q OFPBPC_BAD_VALUE], [0]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 80 00 08 00 00 00 00 \ "| grep -q OFPBPC_BAD_TYPE], [0], [ignore], [stderr]) AT_CHECK([grep -q "unknown NXT_CT_FLUSH property type 128" stderr], [0]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 28 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 00 00 10 00 00 00 00 \ 00 80 00 08 00 50 00 00 \ "| grep -q OFPBPC_BAD_TYPE], [0], [ignore], [stderr]) AT_CHECK([grep -q "unknown NXT_CT_TUPLE property type 128" stderr], [0]) AT_CHECK([ovs-ofctl ofp-print "\ 01 04 00 30 00 00 00 03 00 00 23 20 00 00 00 20 \ 06 \ 00 00 00 00 00 00 00 \ 00 06 00 15 00 00 00 00 00 00 00 00 00 00 00 00 00 ff cd 00 00 00 00 00 \ " | grep -q OFPBPC_BAD_LEN], [0]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofp-util.at000066400000000000000000000045251514270232600222150ustar00rootroot00000000000000AT_BANNER([OpenFlow utilities]) AT_SETUP([encoding hellos]) dnl All versions up to a max version supported: AT_CHECK([ovs-ofctl encode-hello 0x2], [0], [dnl 00000000 01 00 00 08 00 00 00 01 OFPT_HELLO (xid=0x1): version bitmap: 0x01 ]) AT_CHECK([ovs-ofctl encode-hello 0x6], [0], [dnl 00000000 02 00 00 08 00 00 00 01 OFPT_HELLO (OF1.1) (xid=0x1): version bitmap: 0x01, 0x02 ]) AT_CHECK([ovs-ofctl encode-hello 0xe], [0], [dnl 00000000 03 00 00 08 00 00 00 01 OFPT_HELLO (OF1.2) (xid=0x1): version bitmap: 0x01, 0x02, 0x03 ]) AT_CHECK([ovs-ofctl encode-hello 0x1e], [0], [dnl 00000000 04 00 00 08 00 00 00 01 OFPT_HELLO (OF1.3) (xid=0x1): version bitmap: 0x01, 0x02, 0x03, 0x04 ]) AT_CHECK([ovs-ofctl encode-hello 0x3e], [0], [dnl 00000000 05 00 00 08 00 00 00 01 OFPT_HELLO (OF1.4) (xid=0x1): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05 ]) dnl Some versions below max version missing. AT_CHECK([ovs-ofctl encode-hello 0xc], [0], [dnl 00000000 03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 0c OFPT_HELLO (OF1.2) (xid=0x1): version bitmap: 0x02, 0x03 ]) AT_CHECK([ovs-ofctl encode-hello 0xa], [0], [dnl 00000000 03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 0a OFPT_HELLO (OF1.2) (xid=0x1): version bitmap: 0x01, 0x03 ]) AT_CHECK([ovs-ofctl encode-hello 0x8], [0], [dnl 00000000 03 00 00 10 00 00 00 01-00 01 00 08 00 00 00 08 OFPT_HELLO (OF1.2) (xid=0x1): version bitmap: 0x03 ]) AT_CHECK([ovs-ofctl encode-hello 0x4], [0], [dnl 00000000 02 00 00 10 00 00 00 01-00 01 00 08 00 00 00 04 OFPT_HELLO (OF1.1) (xid=0x1): version bitmap: 0x02 ]) AT_CLEANUP AT_SETUP([parsing key-value pairs]) dnl Key-only basics. AT_CHECK([ovs-ofctl parse-key-value a a,b 'a b' 'a b' 'a b'], 0, [a a, b a, b a, b a, b ]) dnl Key-value basics. AT_CHECK([ovs-ofctl parse-key-value a:b a=b a:b,c=d 'a=b c' 'a(b)' 'a(b),c(d)'], 0, [a=b a=b a=b, c=d a=b, c a=b a=b, c=d ]) dnl Values that contain nested delimiters. AT_CHECK([ovs-ofctl parse-key-value 'a:(b,c)' 'a:b(c,d)e' 'a(b,c(d,e),f)'], 0, [a=(b,c) a=b(c,d)e a=b,c(d,e),f ]) dnl Extraneous delimiters. AT_CHECK([ovs-ofctl parse-key-value a,,b ',a b' ' a b ,'], 0, [a, b a, b a, b ]) dnl Missing right parentheses. dnl dnl m4 can't handle unbalanced parentheses so we use @{:@, which dnl Autotest replaces by a left parenthesis. AT_CHECK([ovs-ofctl parse-key-value 'a@{:@b' 'a@{:@b(c)' 'a=b@{:@c'], 0, [a=b a=b(c) a=b@{:@c ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofproto-dpif.at000066400000000000000000024457521514270232600231030ustar00rootroot00000000000000AT_BANNER([ofproto-dpif]) m4_divert_push([PREPARE_TESTS]) # Helper function to check the spread of dp_hash flows over buckets in the datapath check_dpflow_stats () { min_flows=$1 min_buckets=$2 dpflows=`cat` if [[ $# -eq 3 ]]; then echo "$dpflows" | grep "actions:hash" n_flows=`echo "$dpflows" | grep -c dp_hash` n_buckets=`echo "$dpflows" | grep dp_hash | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l` else n_flows=`echo "$dpflows" | wc -l` n_buckets=`echo "$dpflows" | grep -o "actions:[[0-9]]*" | sort | uniq -c | wc -l` fi if [[ $n_flows -ge $min_flows ]]; then flows=ok; else flows=nok; fi if [[ $n_buckets -ge $min_buckets ]]; then buckets=ok; else buckets=nok; fi echo "n_flows=$flows n_buckets=$buckets" } sort_dpif_offload_show () { awk ' /^ -/ { dashlines[[++n]] = $0; next } { print } END { # asort(dashlines) is a GNU extension, so we need to do it # manually here as ofproto-dpif is also executed on FreeBSD. for (i = 1; i <= n; i++) { for (j = i + 1; j <= n; j++) { if (dashlines[[i]] > dashlines[[j]]) { tmp = dashlines[[i]] dashlines[[i]] = dashlines[[j]] dashlines[[j]] = tmp } } } for (i=1; i<=n; i++) print dashlines[[i]] } ' } m4_divert_pop([PREPARE_TESTS]) AT_SETUP([ofproto-dpif - revalidator/wait]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl revalidator/wait]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - lldp revalidator event(REV_RECONFIGURE)]) OVS_VSWITCHD_START( [add-port br0 p1 -- set interface p1 ofport_request=1 type=dummy] ) dnl first revalidation triggered by add interface AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 1 ]) dnl enable lldp AT_CHECK([ovs-vsctl set interface p1 lldp:enable=true]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 2 ]) dnl disable lldp AT_CHECK([ovs-vsctl set interface p1 lldp:enable=false]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 3 ]) dnl remove lldp, no revalidation as lldp was disabled AT_CHECK([ovs-vsctl remove interface p1 lldp enable]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - malformed lldp autoattach tlv]) OVS_VSWITCHD_START() add_of_ports br0 1 dnl Enable lldp AT_CHECK([ovs-vsctl set interface p1 lldp:enable=true]) dnl Send a malformed lldp packet packet="0180c200000ef6b426aa5f0088cc020704f6b426aa5f000403057632060200780c"dnl "5044454144424545464445414442454546444541444245454644454144424545464445414"dnl "4424545464445414442454546444541444245454644454144424545464445414442454546"dnl "4445414442454546fe0500040d0c010000" AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$packet"], [0], [stdout]) OVS_WAIT_UNTIL([grep -q "ISID_VLAN_ASGNS TLV too short" ovs-vswitchd.log]) OVS_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - active-backup bonding (with primary)]) dnl Create br0 with members p1, p2 and p7, creating bond0 with p1 and dnl p2 (p1 as primary) and br1 with members p3, p4 and p8. dnl toggle p1,p2 of bond0 up and down to test bonding in active-backup mode. dnl With p1 down and p2 up/active, bring p1 back up. Since p1 is the primary, dnl it should become active. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=active-backup \ other_config:bond-primary=p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p3 -- set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \ add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) WAIT_FOR_DUMMY_PORTS([p3], [p4]) OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | grep 'active-backup primary: p1'`"]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) ovs-appctl netdev-dummy/set-admin-state up ovs-appctl time/warp 100 ovs-appctl netdev-dummy/set-admin-state p2 down ovs-appctl time/stop ovs-appctl time/warp 100 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 100 ovs-appctl netdev-dummy/set-admin-state p2 up ovs-appctl netdev-dummy/set-admin-state p1 down ovs-appctl time/warp 100 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 2000 100 AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'in_port([[348]])' | strip_xout], [0], [dnl recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035), packets:0, bytes:0, used:never, actions: ]) ovs-appctl netdev-dummy/set-admin-state p1 up ovs-appctl time/warp 100 OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: p1 member p1: enabled active member may_enable: true member p2: enabled may_enable: true ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - active-backup bonding (primary validation)]) dnl Make a switch with 3 ports in a bond, so that when we delete one of dnl the ports from the bond, there are still 2 ports left and the bond dnl remains functional. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 p3 bond_mode=active-backup \ other_config:bond-primary=p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy --]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) dnl Make sure the initial primary member is set OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | grep 'active-backup primary: p1'`"]) dnl Down the primary member and verify that we switched. Then dnl bring the primary back and verify that we switched back to the dnl primary. ovs-appctl netdev-dummy/set-admin-state p1 down ovs-appctl time/warp 100 OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | grep -F 'member p1: disabled'`"]) ovs-appctl netdev-dummy/set-admin-state p1 up ovs-appctl time/warp 100 OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: p1 member p1: enabled active member may_enable: true member p2: enabled may_enable: true member p3: enabled may_enable: true ]) dnl Now delete the primary and verify that the output shows that the dnl primary is no longer an member ovs-vsctl --id=@p1 get Interface p1 -- remove Port bond0 interfaces @p1 ovs-appctl time/warp 100 OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | grep -F 'active-backup primary: p1 (no such member)'`"]) dnl Now re-add the primary and verify that the output shows that the dnl primary is available again. dnl dnl First, get the UUIDs of the members that exist on bond0. dnl Strip the trailing ] so that we can add a new UUID to the end. uuids=`ovs-vsctl get Port bond0 interfaces | sed -e 's/]//'` dnl Create a new port "p1" and add its UUID to the set of members dnl on bond0. ovs-vsctl \ --id=@p1 create Interface name=p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set Port bond0 interfaces="$uuids, @p1]" ovs-appctl time/warp 100 OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: p1 member p1: enabled active member may_enable: true member p2: enabled may_enable: true member p3: enabled may_enable: true ]) dnl Switch to another primary ovs-vsctl set port bond0 other_config:bond-primary=p2 ovs-appctl time/warp 100 OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: p2 member p1: enabled may_enable: true member p2: enabled active member may_enable: true member p3: enabled may_enable: true ]) dnl Remove the "bond-primary" config directive from the bond. AT_CHECK([ovs-vsctl remove Port bond0 other_config bond-primary]) ovs-appctl time/warp 100 OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: member p1: enabled may_enable: true member p2: enabled active member may_enable: true member p3: enabled may_enable: true ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - active-backup bonding (without primary)]) dnl Create br0 with members p1, p2 and p7, creating bond0 with p1 and p2 dnl and br1 with members p3, p4 and p8. dnl toggle p1,p2 of bond0 up and down to test bonding in active-backup mode. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=active-backup --\ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p3 -- set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \ add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) WAIT_FOR_DUMMY_PORTS([p3], [p4]) OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | grep 'active-backup primary: '`"]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) ovs-appctl netdev-dummy/set-admin-state up ovs-appctl time/warp 100 ovs-appctl netdev-dummy/set-admin-state p2 down ovs-appctl time/stop ovs-appctl time/warp 100 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 100 ovs-appctl netdev-dummy/set-admin-state p2 up ovs-appctl netdev-dummy/set-admin-state p1 down ovs-appctl time/warp 100 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(src=10.0.0.5,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(src=10.0.0.6,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 2000 100 AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'in_port([[348]])' | strip_xout], [0], [dnl recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0d),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0e),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035), packets:0, bytes:0, used:never, actions: recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8035), packets:0, bytes:0, used:never, actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - active-backup bonding set primary]) OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=active-backup \ other_config:bond-primary=p1 -- \ set bridge br0 other-config:hwaddr=aa:66:aa:66:aa:00 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy -- \ add-bond br1 bond1 p3 p4 bond_mode=active-backup \ other_config:bond-primary=p3 -- \ set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \ set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy]) WAIT_FOR_DUMMY_PORTS([p3], [p4]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) dnl Create datapath flow with bidirectional traffic. AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl Set p2 and p4 as primary. AT_CHECK([ovs-vsctl set port bond0 other_config:bond-primary=p2 -- \ set port bond1 other_config:bond-primary=p4]) OVS_WAIT_UNTIL([ovs-appctl bond/show | grep -q 'active-backup primary: p4']) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep -q "actions:p[[13]]"], [1]) AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep -q "actions:p[[24]]"], [0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - balance-slb bonding]) # Create br0 with members bond0(p1, p2, p3) and p7, # and br1 with members p4, p5, p6 and p8. # p1 <-> p4, p2 <-> p5, p3 <-> p6 # Send some traffic, make sure the traffic are spread based on source mac. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-slb --\ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p4 -- set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \ add-port br1 p5 -- set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \ add-port br1 p6 -- set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --]) WAIT_FOR_DUMMY_PORTS([p4], [p5], [p6]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) ovs-appctl time/stop ovs-appctl time/warp 100 ( for i in `seq 0 100 |xargs printf '%02x\n'`; do pkt="in_port(7),eth(src=50:54:00:00:00:$i,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p7 $pkt]) done ) ovs-appctl time/warp 100 AT_CHECK([ovs-appctl dpif/dump-flows br1 > br1_flows.txt]) # Make sure there is resonable distribution to all three ports. # We don't want to make this check precise, in case hash function changes. AT_CHECK([test `grep -E 'in_port\(4\)' br1_flows.txt |wc -l` -gt 3]) AT_CHECK([test `grep -E 'in_port\(5\)' br1_flows.txt |wc -l` -gt 3]) AT_CHECK([test `grep -E 'in_port\(6\)' br1_flows.txt |wc -l` -gt 3]) OVS_VSWITCHD_STOP AT_CLEANUP # SEND_TCP_BOND_PKTS([p_name], [p_ofport], [packet_len]) # # Sends 256 packets to port 'p_name' with different TCP destination ports. m4_define([SEND_TCP_BOND_PKTS], [ len_cmd="" if test -n "$3"; then len_cmd=" --len $3" fi for i in `seq 0 255`; do pkt="in_port($2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=$i),tcp_flags(ack)" ovs-appctl netdev-dummy/receive $1 $pkt$len_cmd done ] ) AT_SETUP([ofproto-dpif - balance-tcp bonding]) # Create br0 with members bond0(p1, p2, p3) and p7, # and br1 with members bond1(p4, p5, p6) and p8. # bond0 <-> bond1 # Send some traffic, make sure the traffic are spread based on L4 headers. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ set interface p3 type=dummy options:pstream=punix:$OVS_RUNDIR/p3.sock ofport_request=3 -- \ add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p4 p5 p6 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 --\ set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=4 -- \ set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=5 -- \ set interface p6 type=dummy options:stream=unix:$OVS_RUNDIR/p3.sock ofport_request=6 -- \ add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled ], []) OVS_WAIT_WHILE([ovs-appctl bond/show | grep "may_enable: false"]) ovs-appctl time/stop ovs-appctl time/warp 100 ovs-appctl lacp/show > lacp.txt ovs-appctl bond/show > bond.txt # Check that lb_output is not enabled by default. AT_CHECK([grep -q '^lb_output action: disabled' bond.txt]) AT_CHECK([SEND_TCP_BOND_PKTS([p7], [7])]) ovs-appctl time/warp 300 100 AT_CHECK([ovs-appctl dpif/dump-flows br0 |grep tcp > br0_flows.txt]) AT_CHECK([ovs-appctl dpif/dump-flows br1 |grep tcp > br1_flows.txt]) # Make sure there is resonable distribution to all three ports. # We don't want to make this check precise, in case hash function changes. AT_CHECK([test $(grep -c in_port.4 br1_flows.txt) -gt 24]) AT_CHECK([test $(grep -c in_port.5 br1_flows.txt) -gt 24]) AT_CHECK([test $(grep -c in_port.6 br1_flows.txt) -gt 24]) # Check that bonding is doing dp_hash. AT_CHECK([grep -q dp_hash br0_flows.txt]) # Enabling lb_output. AT_CHECK([ovs-vsctl set Port bond0 other_config:lb-output-action=true]) OVS_WAIT_UNTIL([ovs-appctl bond/show | grep -q '^lb_output action: enabled']) ovs-appctl time/warp 10000 500 ovs-appctl revalidator/wait OVS_WAIT_WHILE([ovs-appctl dpif/dump-flows br1 | grep -q tcp]) AT_CHECK([SEND_TCP_BOND_PKTS([p7], [7])]) ovs-appctl time/warp 300 100 AT_CHECK([ovs-appctl dpif/dump-flows br0 | grep tcp > br0_flows.txt]) AT_CHECK([ovs-appctl dpif/dump-flows br1 | grep tcp > br1_flows.txt]) # Make sure there is resonable distribution to all three ports, again. AT_CHECK([test $(grep -c in_port.4 br1_flows.txt) -gt 24]) AT_CHECK([test $(grep -c in_port.5 br1_flows.txt) -gt 24]) AT_CHECK([test $(grep -c in_port.6 br1_flows.txt) -gt 24]) AT_CHECK([grep -q lb_output br0_flows.txt]) AT_CHECK([test $(ovs-appctl dpif-netdev/bond-show | grep -c bucket) -eq 256]) AT_CHECK([ovs-vsctl set Port bond0 other_config:lb-output-action=false]) OVS_WAIT_UNTIL([test -z "$(ovs-appctl dpif-netdev/bond-show)"]) OVS_VSWITCHD_STOP() AT_CLEANUP # Make sure that rebalancing works after link state changes. AT_SETUP([ofproto-dpif - balance-tcp bonding rebalance after link state changes]) # Create br0 with interfaces bond0(p1, p2) and p5, # and br1 with interfaces bond1(p3, p4) and p6. # bond0 <-> bond1 # Send some traffic, set link state down and up for p2, # send big amount of traffic to trigger rebalancing and # make sure that some hashes rebalanced. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=1000 --\ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 mtu_request=65535 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 mtu_request=65535 -- \ add-port br0 p5 -- set interface p5 ofport_request=5 type=dummy mtu_request=65535 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=1000 --\ set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 mtu_request=65535 -- \ set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 mtu_request=65535 -- \ add-port br1 p6 -- set interface p6 ofport_request=6 type=dummy mtu_request=65535 --]) AT_CHECK([ovs-appctl vlog/set bond:dbg]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled ], []) OVS_WAIT_WHILE([ovs-appctl bond/show | grep "may_enable: false"]) ovs-appctl time/stop ovs-appctl time/warp 2000 200 # Send some traffic to distribute all the hashes between ports. AT_CHECK([SEND_TCP_BOND_PKTS([p5], [5], [65500])]) # Wait for rebalancing for per-hash stats accounting. ovs-appctl time/warp 1000 100 # Check that p2 handles some hashes. ovs-appctl bond/show > bond1.txt AT_CHECK([sed -n '/member p2/,/^$/p' bond1.txt | grep 'hash'], [0], [ignore]) # Move p2 down to force all hashes move to p1 AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 down], 0, [OK ]) ovs-appctl time/warp 200 100 # Check that all hashes moved form p2 ovs-appctl bond/show > bond2.txt AT_CHECK([sed -n '/member p2/,/^$/p' bond2.txt | grep 'hash'], [1], [ignore]) # Move p2 up AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 up], 0, [OK ]) # Send some packets to trigger rebalancing. AT_CHECK([SEND_TCP_BOND_PKTS([p5], [5], [65500])]) # Wait for rebalancing ovs-appctl time/warp 1000 100 # Check that some hashes was shifted to p2 ovs-appctl bond/show > bond3.txt AT_CHECK([sed -n '/member p2/,/^$/p' bond3.txt | grep 'hash'], [0], [ignore]) # Check that both ports doing down and back up doesn't break statistics. AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p1 down], 0, [OK ]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 down], 0, [OK ]) ovs-appctl time/warp 1000 100 AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p1 up], 0, [OK ]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 up], 0, [OK ]) ovs-appctl time/warp 1000 100 AT_CHECK([SEND_TCP_BOND_PKTS([p5], [5], [65500])]) # We sent 49125 KB of data total in 3 batches. No hash should have more # than that amount of load. Just checking that it is within 5 digits. AT_CHECK([ovs-appctl bond/show | grep -E '[[0-9]]{6}'], [1]) OVS_VSWITCHD_STOP() AT_CLEANUP dnl Regression test for a deadlock / double lock on post-recirculation rule dnl updates while processing PACKET_OUT. AT_SETUP([ofproto-dpif - balance-tcp bonding rule updates on packet-out]) dnl Create br0 with interfaces bond0(p1, p2) and p5, dnl and br1 with interfaces bond1(p3, p4) and p6. dnl bond0 <-> bond1 OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active dnl other-config:lacp-time=fast other-config:bond-rebalance-interval=1000 -- dnl set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 mtu_request=65535 -- dnl set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 mtu_request=65535 -- dnl add-port br0 p5 -- set interface p5 ofport_request=5 type=dummy mtu_request=65535 -- dnl add-br br1 -- dnl set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- dnl set bridge br1 datapath-type=dummy other-config:datapath-id=1234 dnl fail-mode=secure -- dnl add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active dnl other-config:lacp-time=fast other-config:bond-rebalance-interval=1000 -- dnl set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 mtu_request=65535 -- dnl set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 mtu_request=65535 -- dnl add-port br1 p6 -- set interface p6 ofport_request=6 type=dummy mtu_request=65535 --]) AT_CHECK([ovs-appctl vlog/set bond:dbg]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) OVS_WAIT_WHILE([ovs-appctl bond/show | grep "may_enable: false"]) ovs-appctl time/stop ovs-appctl time/warp 2000 200 dnl Send some traffic to distribute all the hashes between ports. AT_CHECK([SEND_TCP_BOND_PKTS([p5], [5], [65500])]) dnl Wait for rebalancing for per-hash stats accounting. ovs-appctl time/warp 1000 100 dnl Check that p2 handles some hashes. ovs-appctl bond/show > bond1.txt AT_CHECK([sed -n '/member p2/,/^$/p' bond1.txt | grep 'hash'], [0], [ignore]) dnl Pause revalidators to be sure that they do not update flows while dnl the bonding configuration chnages. ovs-appctl revalidator/pause dnl Move p2 down to trigger update of bonding post-recirculation rules by dnl forcing move of all the hashes to p1. AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 down], 0, [OK ]) dnl Send PACKET_OUT that may lead to flow updates since the bonding dnl configuration changed. packet=ffffffffffff00102030405008004500001c00000000401100000a000002ffffffff0035111100080000 AT_CHECK([ovs-ofctl packet-out br0 "in_port=p5 packet=$packet actions=resubmit(,0)"]) dnl Resume revalidators. ovs-appctl revalidator/resume ovs-appctl revalidator/wait ovs-appctl time/warp 200 100 dnl Check that all hashes moved form p2 and OVS is still working. ovs-appctl bond/show > bond2.txt AT_CHECK([sed -n '/member p2/,/^$/p' bond2.txt | grep 'hash'], [1], [ignore]) OVS_VSWITCHD_STOP() AT_CLEANUP # Makes sure recirculation does not change the way packet is handled. AT_SETUP([ofproto-dpif - balance-tcp bonding, different recirc flow ]) OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=standalone -- \ add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- \ set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \ set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 -- \ add-port br1 br1- -- set interface br1- type=patch options:peer=br1+ ofport_request=100 -- \ add-br br-int -- \ set bridge br-int other-config:hwaddr=aa:77:aa:77:00:00 -- \ set bridge br-int datapath-type=dummy other-config:datapath-id=1235 \ fail-mode=secure -- \ add-port br-int br1+ -- set interface br1+ type=patch options:peer=br1- ofport_request=101 -- \ add-port br-int p5 -- set interface p5 ofport_request=5 type=dummy ]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) # Waits for all ifaces enabled. OVS_WAIT_UNTIL([test `ovs-appctl bond/show | grep -- "may_enable: true" | wc -l` -ge 4]) # The dl_vlan flow should not be ever matched, # since recirculation should not change the flow handling. AT_DATA([flows.txt], [dnl table=0 priority=1 in_port=5 actions=mod_vlan_vid:1,output(101) table=0 priority=2 in_port=5 dl_vlan=1 actions=drop ]) AT_CHECK([ovs-ofctl add-flows br-int flows.txt]) # Sends a packet to trigger recirculation. AT_CHECK([ovs-appctl netdev-dummy/receive p5 "in_port(5),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1)"]) # Collects flow stats. AT_CHECK([ovs-appctl revalidator/purge], [0]) # Checks the flow stats in br1, should only be one flow with non-zero # 'n_packets' from internal table. AT_CHECK([ovs-appctl bridge/dump-flows br1 | ofctl_strip | grep -- "n_packets" | grep -- "table_id" | sed -e 's/output:[[0-9]][[0-9]]*/output/'] , [0], [dnl table_id=254, n_packets=1, n_bytes=38, priority=20,recirc_id=0x0,dp_hash=0x0/0xff,actions=output ]) # Checks the flow stats in br-int, should be only one match. AT_CHECK([ovs-ofctl dump-flows br-int | ofctl_strip | sort], [0], [dnl n_packets=1, n_bytes=34, priority=1,in_port=5 actions=mod_vlan_vid:1,output:101 priority=2,in_port=5,dl_vlan=1 actions=drop NXST_FLOW reply: ]) OVS_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([ofproto-dpif - bond - discard duplicated frames]) dnl With an active/active non-lacp bond, the default behaviour dnl is to discard multicast frames on the secondary interface. OVS_VSWITCHD_START([dnl add-bond br0 bond0 p1 p2 -- dnl set Port bond0 bond-mode=balance-slb other-config:bond-rebalance-interval=0 -- dnl set Interface p1 type=dummy ofport_request=1 -- dnl set Interface p2 type=dummy ofport_request=2 ]) AT_CHECK([ovs-appctl bond/set-active-member bond0 p1], [0], [ignore]) AT_CHECK([ovs-ofctl add-flow br0 actions=NORMAL]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show bond0 | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: balance-slb bond may use recirculation: no, Recirc-ID : -1 bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 all members active: false updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: member p1: enabled active member may_enable: true member p2: enabled may_enable: true ]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff], [0], [dnl Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 bridge("br0") ------------- 0. priority 32768 NORMAL -> no learned MAC for destination, flooding Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=1,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 Datapath actions: 100 ]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=2,dl_dst=ff:ff:ff:ff:ff:ff], [0], [dnl Flow: in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 bridge("br0") ------------- 0. priority 32768 NORMAL -> bonding refused admissibility, dropping Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=2,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 Datapath actions: drop ]) OVS_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([ofproto-dpif - active bond member survives restart]) dnl Create bond0 with members p1, p2 and p3. Initially, set p2 as active. dnl Restart ovs-vswitchd. Check that p2 is still active. OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 p3 bond_mode=active-backup -- \ set interface p1 type=dummy ofport_request=1 -- \ set interface p2 type=dummy ofport_request=2 -- \ set interface p3 type=dummy ofport_request=3 --]) AT_CHECK([ovs-appctl bond/set-active-member bond0 p2], [0], [ignore]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: member p1: enabled may_enable: true member p2: enabled active member may_enable: true member p3: enabled may_enable: true ]) dnl Restart ovs-vswitchd with an empty ovs-vswitchd log file. OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) mv ovs-vswitchd.log ovs-vswitchd_1.log AT_CHECK([ovs-vswitchd --enable-dummy --disable-system --disable-system-route --detach \ --no-chdir --pidfile --log-file -vfile:rconn:dbg -vvconn -vofproto_dpif -vunixctl], [0], [], [stderr]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: active-backup bond may use recirculation: no, bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: member p1: enabled may_enable: true member p2: enabled active member may_enable: true member p3: enabled may_enable: true ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - bond - allow duplicated frames]) dnl Receiving of duplicated multicast frames should be allowed with 'all_members_active'. OVS_VSWITCHD_START([dnl add-bond br0 bond0 p1 p2 -- dnl set Port bond0 bond-mode=balance-slb other-config:bond-rebalance-interval=0 dnl other_config:all-members-active=true -- dnl set Interface p1 type=dummy ofport_request=1 -- dnl set Interface p2 type=dummy ofport_request=2]) AT_CHECK([ovs-appctl bond/set-active-member bond0 p1], [0], [ignore]) AT_CHECK([ovs-ofctl add-flow br0 actions=NORMAL]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show bond0 | STRIP_ACTIVE_MEMBER_MAC], [dnl ---- bond0 ---- bond_mode: balance-slb bond may use recirculation: no, Recirc-ID : -1 bond-hash-basis: 0 lb_output action: disabled, bond-id: -1 all members active: true updelay: 0 ms downdelay: 0 ms lacp_status: off lacp_fallback_ab: false active-backup primary: member p1: enabled active member may_enable: true member p2: enabled may_enable: true ]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff], [0], [dnl Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 bridge("br0") ------------- 0. priority 32768 NORMAL -> no learned MAC for destination, flooding Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=1,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 Datapath actions: 100 ]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=2,dl_dst=ff:ff:ff:ff:ff:ff], [0], [dnl Flow: in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 bridge("br0") ------------- 0. priority 32768 NORMAL -> no learned MAC for destination, flooding Final flow: unchanged Megaflow: recirc_id=0,eth,in_port=2,dl_src=00:00:00:00:00:00,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0000 Datapath actions: 100 ]) OVS_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([ofproto-dpif - resubmit]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 12 13 14 15 16 17 18 19 20 21 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=1000 icmp actions=output(10),resubmit(2),output(19),resubmit(3),output(21) table=0 in_port=2 priority=1500 icmp actions=output(11),resubmit(,1),output(16),resubmit(2,1),output(18) table=0 in_port=3 priority=2000 icmp actions=output(20) table=1 in_port=1 priority=1000 icmp actions=output(12),resubmit(4,1),output(13),resubmit(3),output(15) table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2) table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=p1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - goto table]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 echo "table=0 in_port=1 actions=output(10),goto_table(1)" > flows.txt for i in `seq 1 63`; do echo "table=$i actions=goto_table($(($i+1)))"; done >> flows.txt echo "table=64 actions=output(11)" >> flows.txt AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10,11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - write actions]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 12 13 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip actions=output(10),write_actions(set_field:192.168.3.90->ip_src,output(12)),goto_table(1) table=1 ip actions=write_actions(output(13)),goto_table(2) table=2 ip actions=set_field:192.168.3.91->ip_src,output(11) ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no Datapath actions: 10,set(ipv4(src=192.168.3.91)),11,set(ipv4(src=192.168.3.90)),13 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - modify IPv6 Neighbor Solitication (ND)]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 12 13 AT_DATA([flows.txt], [dnl table=0 in_port=1,icmp6,icmpv6_type=135 actions=output(10),write_actions(set_field:fe80::3->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll,output(12)),goto_table(1) table=1 icmp6 actions=write_actions(output(13)),goto_table(2) table=2 in_port=1,icmp6,icmpv6_type=135 actions=set_field:fe80::4->nd_target,set_field:cc:cc:cc:cc:cc:cc->nd_sll,output(11) ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,icmp6,ipv6_src=fe80::1,ipv6_dst=fe80::2,nw_tos=0,nw_ttl=128,nw_frag=no,icmpv6_type=135,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11'], [0], [stdout]) AT_CHECK([tail -4 stdout], [0], [Megaflow: recirc_id=0,eth,icmp6,in_port=1,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11 Datapath actions: 10,set(nd(target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - clear actions]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 12 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip actions=output(10),write_actions(set_field:192.168.3.90->ip_src,output(12)),goto_table(1) table=1 tcp actions=set_field:91->tp_src,output(11),clear_actions ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,nw_frag=no,tp_src=8,tp_dst=9'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_src=8 Datapath actions: 10,set(tcp(src=91)),11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - group chaining]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=set_field:192.168.3.90->ip_src,group:123,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=123,type=all,bucket=output:10']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - all group in action list]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) # Must match on the source address to be able to restore it's value for # the second bucket AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no Datapath actions: set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - indirect group in action list]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 group_id=1234,type=indirect,bucket=output:10]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - group with ct and dnat recirculation in action list]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 \ 'group_id=1234,type=all,bucket=ct(nat(dst=10.10.10.7:80),commit,table=2)']) AT_DATA([flows.txt], [dnl table=0 ip,ct_state=-trk actions=group:1234 table=2 ip,ct_state=+trk actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 ' in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800, nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no, icmp_type=8,icmp_code=0 '], [0], [stdout]) AT_CHECK([grep 'Datapath actions' stdout], [0], [dnl Datapath actions: ct(commit,nat(dst=10.10.10.7:80)),recirc(0x1) Datapath actions: 10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - group actions have no effect afterwards]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=set_field:192.168.3.90->ip_src,output:10']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234,output:10']) for d in 0 1 2 3; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | sed 's/packets.*actions:/actions:/' | strip_ufid | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:hash(sym_l4(0)),recirc(0x1) recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,proto=1,frag=no), actions:set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - all group in action set]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) # Must match on the source address to be able to restore it's value for # the third bucket AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no Datapath actions: set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - indirect group in action set]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 group_id=1234,type=indirect,bucket=output:10]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - patch port with action set]) OVS_VSWITCHD_START([ \ add-br br1 -- \ set bridge br1 datapath-type=dummy fail-mode=secure -- \ add-port br0 patch10 -- \ set interface patch10 type=patch options:peer=patch20 ofport_request=10 -- \ add-port br1 patch20 -- \ set interface patch20 type=patch options:peer=patch10 ofport_request=20 ]) add_of_ports br0 1 add_of_ports br1 2 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br1 'ip actions=write_actions(pop_vlan,output:2)']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=output:10']) AT_CHECK([ovs-appctl ofproto/trace br1 'in_port=20,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_vlan=100,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_vlan,2 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_vlan=100,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_vlan,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 ovs-appctl vlog/set ofproto_dpif:file:dbg AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11']) OVS_WAIT_UNTIL_EQUAL([grep -A6 "Constructing select group 1234" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1234 ofproto_dpif|DBG|No selection method specified. Trying dp_hash. ofproto_dpif|DBG| Minimum weight: 1, total weight: 2 ofproto_dpif|DBG| Using 16 hash values: ofproto_dpif|DBG| Bucket 0: weight=1, target=8.00 hits=8 ofproto_dpif|DBG| Bucket 1: weight=1, target=8.00 hits=8 ofproto_dpif|DBG|Use dp_hash with 16 hash values using algorithm 1.]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) # Try a bunch of different flows and make sure that they get distributed # # at least somewhat. for d in 0 1 2 3; do for s in 1 2 3 4 ; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.$s,dst=192.168.1.$d,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done done AT_CHECK([ovs-appctl dpctl/dump-flows | sort | strip_ufid | strip_used | check_dpflow_stats 5 2 dp_hash], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:15, bytes:1590, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1) n_flows=ok n_buckets=ok ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with watch port]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=watch_port:10,output:10,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) for d in 0 1 2 3; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done AT_CHECK([ovs-appctl dpctl/dump-flows | sort| sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | strip_ufid | strip_used], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:3, bytes:318, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1) recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:3, bytes:318, used:0.0s, actions:11 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with weights]) # Helper function to check the accuracy of distribution of packets over buckets check_group_stats () { buckets=`grep -o 'packet_count=[[0-9]]*' | cut -d'=' -f2 | tail -n +2` i=0 for bucket in $buckets; do min=$1 shift if [[ $bucket -ge $min ]]; then echo "bucket$i >= $min" else echo "bucket$i < $min" fi i=`expr $i + 1` if [[ $i -ge 4 ]]; then break; fi done } OVS_VSWITCHD_START add_of_ports br0 1 10 11 12 13 14 ovs-appctl vlog/set ofproto_dpif:file:dbg AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 'group_id=1234,type=select,bucket=weight:5,output:10,bucket=weight:10,output:11,bucket=weight:25,output:12,bucket=weight:60,output:13,bucket=weight:0,output:14']) OVS_WAIT_UNTIL_EQUAL([grep -A9 "Constructing select group 1234" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1234 ofproto_dpif|DBG|No selection method specified. Trying dp_hash. ofproto_dpif|DBG| Minimum weight: 5, total weight: 100 ofproto_dpif|DBG| Using 32 hash values: ofproto_dpif|DBG| Bucket 0: weight=5, target=1.60 hits=2 ofproto_dpif|DBG| Bucket 1: weight=10, target=3.20 hits=3 ofproto_dpif|DBG| Bucket 2: weight=25, target=8.00 hits=8 ofproto_dpif|DBG| Bucket 3: weight=60, target=19.20 hits=19 ofproto_dpif|DBG| Bucket 4: weight=0, target=0.00 hits=0 ofproto_dpif|DBG|Use dp_hash with 32 hash values using algorithm 1.]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) # Try 1000 different flows and make sure that they get distributed according to weights for d1 in 0 1 2 3 4 5 6 7 8 9 ; do for d2 in 0 1 2 3 4 5 6 7 8 9 ; do for s in 0 1 2 3 4 5 6 7 8 9 ; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.1.$s,dst=192.168.$d1.$d2,proto=6,tos=0,ttl=128,frag=no),tcp(src=1000$s,dst=1000)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done done done # Check balanced distribution over 32 dp_hash values AT_CHECK([ovs-appctl dpctl/dump-flows | sort | strip_ufid | strip_used | check_dpflow_stats 32 4 dp_hash], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:999, bytes:117882, used:0.0s, actions:hash(sym_l4(0)),recirc(0x1) n_flows=ok n_buckets=ok ]) # Check that actual distribution over the buckets is reasonably accurate: ideal weights dp_hash values # bucket0: 5%*1000 = 50 2/32*1000 = 63 # bucket1: 10%*1000 = 100 3/32*1000 = 94 # bucket2: 25%*1000 = 250 8/32*1000 = 250 # bucket3: 60%*1000 = 600 19/32*1000 = 594 # bucket4: 0 0 ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-group-stats br0 | check_group_stats 40 80 200 500], [0], [dnl bucket0 >= 40 bucket1 >= 80 bucket2 >= 200 bucket3 >= 500 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with dp_hash and equal weights]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-appctl vlog/set ofproto_dpif:file:dbg vconn:file:info]) AT_DATA([stddev.awk], [ { # $1 (target) is a mean value, because all weights are the same. # $2 (hits) is an actual number of hashes assigned to this bucket. n_hashes += $2 n_buckets++ sum_sq_diff += ($2 - $1) * ($2 - $1) } END { mean = n_hashes / n_buckets stddev = sqrt(sum_sq_diff / n_buckets) stddevp = stddev * 100 / mean print "hashes:", n_hashes, "buckets:", n_buckets print "mean:", mean, "stddev:", stddev, "(", stddevp, "% )" # Make sure that standard deviation of load between buckets is below 12.5%. # Note: it's not a strict requirement, but a good number that passes tests. if (stddevp <= 12.5) { print "PASS" } else { print "FAIL" } } ]) m4_define([CHECK_DISTRIBUTION], [ OVS_WAIT_UNTIL([test $(tail -n $1 ovs-vswitchd.log \ | grep -c '|DBG|.*Bucket') -eq $2]) AT_CHECK([tail -n $1 ovs-vswitchd.log | grep '|DBG|.*Bucket' \ | sed 's/.*target=\([[0-9\.]]*\) hits=\([[0-9]]*\)/\1 \2/' \ | awk -f stddev.awk], [0], [stdout]) AT_CHECK([grep -q "buckets: $2" stdout]) AT_CHECK([grep -q 'PASS' stdout]) ]) m4_define([OF_GROUP], [group_id=$1,type=select,selection_method=dp_hash]) m4_define([OFG_BUCKET], [bucket=weight=$1,output:10]) dnl Test load distribution in groups with up to 64 equally weighted buckets. m4_define([OFG_BUCKETS], [OFG_BUCKET(100)]) m4_for([id], [1], [64], [1], [ get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 \ "OF_GROUP(id),OFG_BUCKETS()"]) CHECK_DISTRIBUTION([+$LINENUM], [id]) m4_append([OFG_BUCKETS], [,OFG_BUCKET(100)]) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with dp_hash, insert/remove buckets]) OVS_VSWITCHD_START add_of_ports br0 1 10 AT_CHECK([ovs-appctl vlog/set ofproto_dpif:file:dbg]) dnl Add a group without buckets. AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 \ 'group_id=1235,type=select,selection_method=dp_hash']) AT_CHECK([grep -A3 "Constructing select group 1235" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG|Selection method specified: dp_hash. ofproto_dpif|DBG| Don't apply dp_hash method without buckets. ofproto_dpif|DBG|Falling back to default hash method. ]) m4_define([OFG_BUCKET], [bucket=weight=$1,bucket_id=$1,output:10]) dnl Add two buckets one by one. get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 insert-buckets br0 \ group_id=1235,command_bucket_id=last,OFG_BUCKET([5])]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=16.00 hits=16 ofproto_dpif|DBG|Modifying select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=16.00 hits=16]) get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 insert-buckets br0 \ group_id=1235,command_bucket_id=last,OFG_BUCKET([6])]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG| Bucket 6: weight=6, target=16.00 hits=16 ofproto_dpif|DBG|Modifying select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=7.27 hits=7 ofproto_dpif|DBG| Bucket 6: weight=6, target=8.73 hits=9]) dnl Add two more in the middle. get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 insert-buckets br0 \ group_id=1235,command_bucket_id=5,OFG_BUCKET([7]),OFG_BUCKET([8])]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG| Bucket 7: weight=7, target=7.47 hits=7 ofproto_dpif|DBG| Bucket 8: weight=8, target=8.53 hits=9 ofproto_dpif|DBG|Modifying select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=3.08 hits=3 ofproto_dpif|DBG| Bucket 7: weight=7, target=4.31 hits=4 ofproto_dpif|DBG| Bucket 8: weight=8, target=4.92 hits=5 ofproto_dpif|DBG| Bucket 6: weight=6, target=3.69 hits=4]) dnl Remove the last bucket. get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 remove-buckets br0 \ group_id=1235,command_bucket_id=last]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG|Modifying select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=4.00 hits=4 ofproto_dpif|DBG| Bucket 7: weight=7, target=5.60 hits=6 ofproto_dpif|DBG| Bucket 8: weight=8, target=6.40 hits=6]) dnl Remove the one in the middle. get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 remove-buckets br0 \ group_id=1235,command_bucket_id=7]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG|Modifying select group 1235 ofproto_dpif|DBG| Bucket 5: weight=5, target=6.15 hits=6 ofproto_dpif|DBG| Bucket 8: weight=8, target=9.85 hits=10]) dnl Remove all the remaining. get_log_next_line_num AT_CHECK([ovs-ofctl -O OpenFlow15 remove-buckets br0 \ group_id=1235,command_bucket_id=all]) OVS_WAIT_UNTIL_EQUAL([tail -n +$LINENUM ovs-vswitchd.log \ | grep -E '(Bucket|group 1235)' \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG|Modifying select group 1235]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with explicit dp_hash selection method]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 ovs-appctl vlog/set ofproto_dpif:file:dbg AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=dp_hash,bucket=output:10,bucket=output:11']) OVS_WAIT_UNTIL_EQUAL([grep -A6 "Constructing select group 1234" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1234 ofproto_dpif|DBG|Selection method specified: dp_hash. ofproto_dpif|DBG| Minimum weight: 1, total weight: 2 ofproto_dpif|DBG| Using 16 hash values: ofproto_dpif|DBG| Bucket 0: weight=1, target=8.00 hits=8 ofproto_dpif|DBG| Bucket 1: weight=1, target=8.00 hits=8 ofproto_dpif|DBG|Use dp_hash with 16 hash values using algorithm 0.]) # Fall back to legacy hash with zero buckets AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1235,type=select,selection_method=dp_hash']) OVS_WAIT_UNTIL_EQUAL([grep -A3 "Constructing select group 1235" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1235 ofproto_dpif|DBG|Selection method specified: dp_hash. ofproto_dpif|DBG| Don't apply dp_hash method without buckets. ofproto_dpif|DBG|Falling back to default hash method.]) # Fall back to legacy hash with zero buckets AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1236,type=select,selection_method=dp_hash,bucket=weight=1,output:10,bucket=weight=1000,output:11']) OVS_WAIT_UNTIL_EQUAL([grep -A4 "Constructing select group 1236" ovs-vswitchd.log \ | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [dnl ofproto_dpif|DBG|Constructing select group 1236 ofproto_dpif|DBG|Selection method specified: dp_hash. ofproto_dpif|DBG| Minimum weight: 1, total weight: 1001 ofproto_dpif|DBG| Too many hash values required: 1024 ofproto_dpif|DBG|Falling back to default hash method.]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with legacy hash selection method]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 ovs-appctl vlog/set ofproto_dpif:file:dbg AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=hash,bucket=output:10,bucket=output:11']) AT_CHECK([grep -A2 "Constructing select group 1234" ovs-vswitchd.log | sed 's/^.*ofproto_dpif/ofproto_dpif/'], [0], [dnl ofproto_dpif|DBG|Constructing select group 1234 ofproto_dpif|DBG|Selection method specified: hash. ofproto_dpif|DBG|No hash fields. Falling back to default hash method. ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 'ip actions=write_actions(group:1234)']) # Try 16 flows with differing default hash values. for d in 0 1 2 3; do for s in 1 2 3 4 ; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),ipv4(src=192.168.0.$s,dst=192.168.1.$d,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done done # Check that the packets installed 16 data path flows and each of the two # buckets is hit at least once. AT_CHECK([ovs-appctl dpctl/dump-flows | strip_ufid | strip_used | sort | check_dpflow_stats 16 2], [0], [dnl n_flows=ok n_buckets=ok ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with custom hash selection method]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 # Check that parse failures after 'fields' parsing work AT_CHECK([ovs-ofctl -O OpenFlow10 add-group br0 'group_id=1,type=select,fields(eth_dst),bukket=output:10'], [1], ,[dnl ovs-ofctl: unknown keyword bukket ]) # Check that fields are rejected without "selection_method=hash". AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1235,type=select,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11'], 1, [], [dnl ovs-ofctl: fields may only be specified with "selection_method=hash" ]) # Check that selection_method_param without selection_method is rejected. AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1235,type=select,selection_method_param=1,bucket=output:10,bucket=output:11'], 1, [], [dnl ovs-ofctl: selection_method_param is only allowed with "selection_method" ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 'group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 'ip actions=write_actions(group:1234)']) # Try 16 flows with differing custom hash and check that they give rise to # 16 data path flows and each of the two buckets is hit at least once for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do pkt="in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:$d),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done AT_CHECK([ovs-appctl dpctl/dump-flows | strip_ufid | strip_used | sort | check_dpflow_stats 16 2], [0], [dnl n_flows=ok n_buckets=ok ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) # Try 16 flows that differ only in fields that are not part of the custom # hash and check that there is only a single datapath flow for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do pkt="in_port(1),eth(src=50:54:00:00:00:$d,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done AT_CHECK([ovs-appctl dpctl/dump-flows | grep -c recirc_id], [0], [dnl 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fast failover group]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=ff,bucket=watch_port:10,output:10,bucket=watch_port:11,output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - select group with dp_hash, tracing]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 \ 'group_id=1,type=select,bucket=bucket_id:0,weight:10,actions=output:p1']) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 \ 'in_port=p1,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,actions=group:1']) AT_CHECK([ovs-appctl ofproto/trace br0 \ in_port=p1,tcp,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.2.0.2,nw_dst=10.2.0.3 | grep -- '->'], [0], [dnl -> selection method in use: dp_hash, recirculating ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-group br0 \ 'group_id=2,type=select,selection_method=dp_hash']) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 \ 'in_port=p2,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,actions=group:2']) AT_CHECK([ovs-appctl ofproto/trace br0 \ in_port=p2,tcp,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.2.0.2,nw_dst=10.2.0.3 | grep -- '->'], [0], [dnl -> no live bucket ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - group stats single bucket]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,weight=2000,bucket=output:11,weight=0']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) ( for i in `seq 0 2`; do pkt="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done ) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,ref_count=1,packet_count=3,byte_count=318,bucket0:packet_count=3,byte_count=318,bucket1:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - group stats all buckets]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)']) ( for i in `seq 0 2`; do pkt="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 $pkt]) done ) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,ref_count=1,packet_count=3,byte_count=318,bucket0:packet_count=3,byte_count=318,bucket1:packet_count=3,byte_count=318 OFPST_GROUP reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - registers]) OVS_VSWITCHD_START add_of_ports br0 20 21 22 33 34 90 AT_DATA([flows.txt], [dnl in_port=90 actions=resubmit:2,resubmit:3,resubmit:4,resubmit:91 in_port=91 actions=resubmit:5,resubmit:6,resubmit:7,resubmit:92 in_port=92 actions=resubmit:8,resubmit:9,resubmit:10,resubmit:11,resubmit:93 in_port=93 actions=resubmit:12,resubmit:13,resubmit:14,resubmit:15,resubmit:16,resubmit:17,resubmit:18,resubmit:94 in_port=94 actions=resubmit:22,resubmit:23,resubmit:24,resubmit:25,resubmit:26,resubmit:27,resubmit:28 in_port=2 actions=load:0x000db000->NXM_NX_REG0[[]],load:0x000db000->NXOXM_ET_REG16[[]],load:0x000db000->NXOXM_ET_REG29[[]], in_port=3 actions=load:0xdea->NXM_NX_REG0[[20..31]],load:0xdea->NXOXM_ET_REG16[[20..31]],load:0xdea->NXOXM_ET_REG29[[20..31]] in_port=4 actions=load:0xeef->NXM_NX_REG0[[0..11]],load:0xeef->NXOXM_ET_REG16[[0..11]],load:0xeef->NXOXM_ET_REG29[[0..11]] in_port=5 actions=move:NXM_NX_REG0[[]]->NXM_NX_REG1[[]],move:NXOXM_ET_REG16[[]]->NXOXM_ET_REG17[[]],move:NXOXM_ET_REG29[[]]->NXOXM_ET_REG30[[]] in_port=6 actions=load:0x22222222->NXM_NX_REG2[[]],load:0x22222222->NXOXM_ET_REG18[[]],load:0x22222222->NXOXM_ET_REG31[[]] in_port=7 actions=move:NXM_NX_REG1[[20..31]]->NXM_NX_REG2[[0..11]],move:NXOXM_ET_REG17[[20..31]]->NXOXM_ET_REG18[[0..11]],move:NXOXM_ET_REG30[[20..31]]->NXOXM_ET_REG31[[0..11]] in_port=8 actions=move:NXM_NX_REG1[[0..11]]->NXM_NX_REG2[[20..31]],move:NXOXM_ET_REG17[[0..11]]->NXOXM_ET_REG18[[20..31]],move:NXOXM_ET_REG30[[0..11]]->NXOXM_ET_REG31[[20..31]] in_port=9,reg0=0xdeadbeef,reg16=0xdeadbeef,reg29=0xdeadbeef actions=output:20 in_port=10,reg1=0xdeadbeef,reg17=0xdeadbeef,reg30=0xdeadbeef actions=output:21 in_port=11,reg2=0xeef22dea,reg18=0xeef22dea,reg31=0xeef22dea actions=output:22 dnl Sanity check registers 0-15 in_port=12 actions=load:0x100->NXM_NX_REG0[[]],load:0x101->NXM_NX_REG1[[]],load:0x102->NXM_NX_REG2[[]] in_port=13 actions=load:0x103->NXM_NX_REG3[[]],load:0x104->NXM_NX_REG4[[]],load:0x105->NXM_NX_REG5[[]] in_port=14 actions=load:0x106->NXM_NX_REG6[[]],load:0x107->NXM_NX_REG7[[]],load:0x108->NXM_NX_REG8[[]] in_port=15 actions=load:0x109->NXM_NX_REG9[[]],load:0x110->NXM_NX_REG10[[]],load:0x111->NXM_NX_REG11[[]] in_port=16 actions=load:0x112->NXM_NX_REG12[[]],load:0x113->NXM_NX_REG13[[]],load:0x114->NXM_NX_REG14[[]] in_port=17 actions=load:0x115->NXM_NX_REG15[[]] in_port=18,reg0=0x100,reg1=0x101,reg2=0x102,reg3=0x103,reg4=0x104,reg5=0x105,reg6=0x106,reg7=0x107,reg8=0x108,reg9=0x109,reg10=0x110,reg11=0x111,reg12=0x112,reg13=0x113,reg14=0x114,reg15=0x115 actions=output:33 dnl Sanity check registers 16-31 in_port=22 actions=load:0x116->NXOXM_ET_REG16[[]],load:0x117->NXOXM_ET_REG17[[]],load:0x118->NXOXM_ET_REG18[[]] in_port=23 actions=load:0x119->NXOXM_ET_REG19[[]],load:0x120->NXOXM_ET_REG20[[]],load:0x121->NXOXM_ET_REG21[[]] in_port=24 actions=load:0x122->NXOXM_ET_REG22[[]],load:0x123->NXOXM_ET_REG23[[]],load:0x124->NXOXM_ET_REG24[[]] in_port=25 actions=load:0x125->NXOXM_ET_REG25[[]],load:0x126->NXOXM_ET_REG26[[]],load:0x127->NXOXM_ET_REG27[[]] in_port=26 actions=load:0x128->NXOXM_ET_REG28[[]],load:0x129->NXOXM_ET_REG29[[]],load:0x130->NXOXM_ET_REG30[[]] in_port=27 actions=load:0x131->NXOXM_ET_REG31[[]] in_port=28,reg16=0x116,reg17=0x117,reg18=0x118,reg19=0x119,reg20=0x120,reg21=0x121,reg22=0x122,reg23=0x123,reg24=0x124,reg25=0x125,reg26=0x126,reg27=0x127,reg28=0x128,reg29=0x129,reg30=0x130,reg31=0x131 actions=output:34 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 20,21,22,33,34 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Tests that the standardized xregs are mapped onto the legacy OVS registers dnl in the manner documented in ovs-ofctl(8). AT_SETUP([ofproto-dpif - extended registers]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 actions=load:0xfedcba9876543210->OXM_OF_PKT_REG1[[]],resubmit(,2) table=2 actions=load:0xfedcba9876543210->OXM_OF_PKT_REG15[[]],resubmit(,3) table=3,reg2=0xfedcba98,reg3=0x76543210 actions=resubmit(,4) table=4,reg30=0xfedcba98,reg31=0x76543210 actions=2 # These low-priority rules shouldn't match. They're here only to make really # sure that the test fails if either of the above rules fails to match. table=0,priority=0 actions=3 table=1,priority=0 actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Tests that the standardized xxregs are mapped onto the legacy OVS dnl registers in the manner documented in ovs-ofctl(8). AT_SETUP([ofproto-dpif - extended-extended registers]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 actions=load:0x0123456789abcdeffedcba9876543210->NXM_NX_XXREG1[[]],resubmit(,1) table=1 actions=load:0x0123456789abcdeffedcba9876543210->NXOXM_ET_XXREG7[[]],resubmit(,2) table=2,reg4=0x01234567,reg5=0x89abcdef,reg6=0xfedcba98,reg7=0x76543210 actions=resubmit(,3) table=3,reg28=0x01234567,reg29=0x89abcdef,reg30=0xfedcba98,reg31=0x76543210 actions=2 # These low-priority rules shouldn't match. They're here only to make really # sure that the test fails if either of the above rules fails to match. table=0,priority=0 actions=3 table=1,priority=0 actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - load and move order]) OVS_VSWITCHD_START add_of_ports br0 1 10 11 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,move:NXM_NX_REG1[[]]->NXM_OF_IP_SRC[[]],bucket=output:11']) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(load:0xffffffff->NXM_NX_REG1[[]],move:NXM_NX_REG1[[]]->NXM_NX_REG2[[]],group:1234)']) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no Datapath actions: set(ipv4(src=255.255.255.255)),10,set(ipv4(src=192.168.0.1)),11 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Tests that 1.5 copy-field can copy into the standardized xregs. AT_SETUP([ofproto-dpif - copy-field into extended registers]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 actions=move:OXM_OF_ETH_SRC[[0..47]]->OXM_OF_PKT_REG0[[0..47]],goto_table(1) table=1,xreg0=0x0000505400000005 actions=move:OXM_OF_ETH_DST[[0..47]]->OXM_OF_PKT_REG15[[0..47]],goto_table(2) table=2,xreg15=0x0000505400000007 actions=2 # These low-priority rules shouldn't match. They're here only to make really # sure that the test fails if either of the above rules fails to match. table=0,priority=0 actions=3 table=1,priority=0 actions=3 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Tests that 1.5 set-field with mask in the metadata register. AT_SETUP([ofproto-dpif - masked set-field into metadata]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 actions=set_field:0xfafafafa5a5a5a5a->metadata,goto_table(1) table=1 actions=set_field:0x6b/0xff->metadata,goto_table(2) table=2,metadata=0xfafafafa5a5a5a6b actions=2 # These low-priority rules shouldn't match. They're here only to make really # sure that the test fails if either of the above rules fails to match. table=0,priority=0 actions=3 table=1,priority=0 actions=3 table=2,priority=0 actions=3 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - actset_output]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 7 8 9 10 11 12 13 AT_DATA([flows.txt], [dnl table=0,actset_output=unset actions=write_actions(output(2)),goto_table(1) table=1 actions=move:ONFOXM_ET_ACTSET_OUTPUT[[0..31]]->OXM_OF_PKT_REG0[[0..31]],goto_table(2) # Verify that actset_output got set. table=2,priority=20,actset_output=2 actions=4,goto_table(3) table=2,priority=10 actions=5,goto_table(3) # Verify that xreg0 got copied properly from actset_output. table=3,priority=20,xreg0=2 actions=6,goto_table(4) table=3,priority=10 actions=7,goto_table(4) # Verify that xxreg0 got copied properly from actset_output. table=3,priority=20,xxreg0=2 actions=6,goto_table(4) table=3,priority=10 actions=7,goto_table(4) # Verify that adding a group action unsets actset_output, # even if output follows group. table=4 actions=write_actions(group(5),output(10)),goto_table(5) table=5,priority=20,actset_output=unset actions=8,goto_table(6) table=5,priority=10 actions=9,goto_table(6) # Verify that adding another output action doesn't change actset_output # (since there's still a group). table=6 actions=write_actions(output(3)),goto_table(7) table=7,priority=20,actset_output=unset actions=10,goto_table(8) table=7,priority=10 actions=11,goto_table(8) # Verify that clearing the action set, then writing an output action, # causes actset_output to be set again. table=8,actions=clear_actions,write_actions(output(3),output(2)),goto_table(9) table=9,priority=20,actset_output=2 actions=12 table=9,priority=10 actions=13 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-group br0 'group_id=5,type=all,bucket=output:1']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 4,6,8,10,12,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - push-pop]) OVS_VSWITCHD_START add_of_ports br0 20 21 22 33 90 AT_DATA([flows.txt], [dnl in_port=90 actions=load:20->NXM_NX_REG0[[0..7]],load:21->NXM_NX_REG1[[0..7]],load:22->NXM_NX_REG2[[0..7]], load:33->NXM_NX_REG3[[0..7]], push:NXM_NX_REG0[[]], push:NXM_NX_REG1[[0..7]],push:NXM_NX_REG2[[0..15]], push:NXM_NX_REG3[[]], resubmit:2, resubmit:3, resubmit:4, resubmit:5 in_port=2 actions=pop:NXM_NX_REG0[[0..7]],output:NXM_NX_REG0[[]] in_port=3 actions=pop:NXM_NX_REG1[[0..7]],output:NXM_NX_REG1[[]] in_port=4 actions=pop:NXM_NX_REG2[[0..15]],output:NXM_NX_REG2[[]] in_port=5 actions=pop:NXM_NX_REG3[[]],output:NXM_NX_REG3[[]] ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 33,22,21,20 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - output]) OVS_VSWITCHD_START add_of_ports br0 1 9 10 11 55 66 77 88 AT_DATA([flows.txt], [dnl in_port=1 actions=resubmit:2,resubmit:3,resubmit:4,resubmit:5,resubmit:6,resubmit:7,resubmit:8 in_port=2 actions=output:9 in_port=3 actions=load:55->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]],load:66->NXM_NX_REG1[[]] in_port=4 actions=output:10,output:NXM_NX_REG0[[]],output:NXM_NX_REG1[[]],output:11 in_port=5 actions=load:77->NXM_NX_REG0[[0..15]],load:88->NXM_NX_REG0[[16..31]] in_port=6 actions=output:NXM_NX_REG0[[0..15]],output:NXM_NX_REG0[[16..31]] in_port=7 actions=load:0x110000ff->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]] in_port=8 actions=1,9,load:9->NXM_OF_IN_PORT[[]],1,9 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 9,55,10,55,66,11,77,88,9,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - dec_ttl]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 AT_DATA([flows.txt], [dnl table=0 in_port=1 action=dec_ttl,output:2,resubmit(1,1),output:4 table=1 in_port=1 action=dec_ttl,output:3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=2,frag=no)' -generate], [0], [stdout]) AT_CHECK([tail -4 stdout], [0], [ Final flow: ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=111,nw_tos=0,nw_ecn=0,nw_ttl=1,nw_frag=no Megaflow: recirc_id=0,eth,ip,in_port=1,nw_proto=111,nw_ttl=2,nw_frag=no Datapath actions: set(ipv4(ttl=1)),2,userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)),4 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=3,frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_proto=111,nw_ttl=3,nw_frag=no Datapath actions: set(ipv4(ttl=2)),2,set(ipv4(ttl=1)),3,4 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ipv6,in_port=1,nw_proto=10,nw_ttl=128,nw_frag=no Datapath actions: set(ipv6(hlimit=127)),2,set(ipv6(hlimit=126)),3,4 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl -P nxt_packet_in monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=2,frag=no)' OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=34 in_port=1 (via invalid_ttl) data_len=34 (unbuffered) ip,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=111,nw_tos=0,nw_ecn=0,nw_ttl=1,nw_frag=no ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl A dec_ttl action at offset 32 in ofpacts will cause the ofpacts dnl buffer to be resized just before pushing the id of the dec_ttl action. dnl Thus the implementation must account for this by using the dnl reallocated buffer rather than the original buffer. dnl dnl A number of similar rules are added to try and exercise dnl xrealloc sufficiently that it returns a different base pointer AT_SETUP([ofproto-dpif - dec_ttl without arguments at offset 32 in ofpacts]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 0 255`; do printf "dl_src=10:11:11:11:11:%02x actions=output:1,output:1,output:1,dec_ttl,controller\n" $i done) > flows.txt AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_VSWITCHD_STOP AT_CLEANUP dnl A dec_ttl action at offset 32 in ofpacts will cause the ofpacts dnl buffer to be resized just before pushing the id of the dec_ttl action. dnl Thus the implementation must account for this by using the dnl reallocated buffer rather than the original buffer. dnl dnl A number of similar rules are added to try and exercise dnl xrealloc sufficiently that it returns a different base pointer AT_SETUP([ofproto-dpif - dec_ttl with arguments at offset 32 in ofpacts]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 0 255`; do printf "dl_src=10:11:11:11:11:%02x actions=output:1,output:1,output:1,dec_ttl(1),controller\n" $i done) > flows.txt AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_VSWITCHD_STOP AT_CLEANUP dnl A note action at offset 24 in ofpacts will cause the ofpacts dnl buffer to be resized just before pushing the id of the dec_ttl action. dnl Thus the implementation must account for this by using the dnl reallocated buffer rather than the original buffer. dnl dnl A number of similar rules are added to try and exercise dnl xrealloc sufficiently that it returns a different base pointer AT_SETUP([ofproto-dpif - note at offset 24 in ofpacts]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 0 255`; do printf "dl_src=10:11:11:11:11:%02x actions=output:1,output:1,note:ff,controller\n" $i done) > flows.txt AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_VSWITCHD_STOP AT_CLEANUP dnl As of OVS-2.5, a note action after 4 set_field actions are likely to dnl trigger ofpbuf reallocation during decode (~1KB into ofpacts buffer). dnl Using `make check-valgrind' here checks for use-after-free in this dnl codepath. AT_SETUP([ofproto-dpif - note action deep inside ofpacts]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 'actions=set_field:0x1->metadata,set_field:0x2->metadata,set_field:0x3->metadata,set_field:0x4->metadata,note:00000000000000000000000000000000,note:00000000000000000000000000000000']) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - output, OFPP_NONE ingress port]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 action=normal]) # "in_port" defaults to OFPP_NONE if it's not specified. flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,icmp_type=8,icmp_code=0" AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout | sed 's/Datapath actions: //' | tr "," "\n" | sort -n], [0], [dnl 1 2 100 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - DSCP]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) add_of_ports br0 9 AT_DATA([flows.txt], [dnl actions=output:LOCAL,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:LOCAL ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-vsctl -- \ set Port p1 qos=@newqos --\ --id=@newqos create QoS type=linux-htb queues=1=@q1,2=@q2 --\ --id=@q1 create Queue dscp=1 --\ --id=@q2 create Queue dscp=2], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(9),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,skb_priority=0,eth,icmp,in_port=9,nw_tos=252,nw_frag=no Datapath actions: dnl 100,dnl set(ipv4(tos=0x4/0xfc)),set(skb_priority(0x1)),1,dnl set(ipv4(tos=0x8/0xfc)),set(skb_priority(0x2)),1,dnl 1,dnl set(ipv4(tos=0x4/0xfc)),set(skb_priority(0x1)),1,dnl set(ipv4(tos=0xfc/0xfc)),set(skb_priority(0)),1,dnl set(ipv4(tos=0/0xfc)),1,100 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - output/flood flags]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 7 AT_DATA([flows.txt], [dnl in_port=local actions=local,flood in_port=1 actions=flood in_port=2 actions=all in_port=3 actions=output:LOCAL,output:1,output:2,output:3,output:4,output:5,output:6,output:7 in_port=4 actions=enqueue:LOCAL:1,enqueue:1:1,enqueue:2:1,enqueue:3:2,enqueue:4:1,enqueue:5:1,enqueue:6:1,enqueue:7:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl mod-port br0 5 noforward]) AT_CHECK([ovs-ofctl mod-port br0 6 noflood]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout]) AT_CHECK([tail -1 stdout \ | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl 1 2 3 4 7 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout]) AT_CHECK([tail -1 stdout \ | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl 100 2 3 4 7 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout]) AT_CHECK([tail -1 stdout \ | sed -e 's/Datapath actions: //' | tr ',' '\n' | sort], [0], [dnl 1 100 3 4 6 7 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 100,1,2,4,6,7 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(4),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x0900)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(skb_priority(0x1)),100,1,2,set(skb_priority(0x2)),3,set(skb_priority(0x1)),6,7 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Default Table Miss - OF1.0 (OFPTC_TABLE_MISS_CONTROLLER)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl -P nxt_packet_in monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(syn)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=syn tcp_csum:2e7e dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=syn tcp_csum:2e7e dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=syn tcp_csum:2e7e ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Default Table Miss - OF1.3 (OFPTC_TABLE_MISS_DROP)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl monitor -OOpenFlow13 -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Test that missed packets are dropped for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(ack,syn)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_CONTROLLER]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flow br0 'table=0 actions=goto_table(1)']) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(urg|rst)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=rst|urg tcp_csum:2e5c dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=rst|urg tcp_csum:2e5c dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=rst|urg tcp_csum:2e5c ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, actions=goto_table:1 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_CONTROLLER]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flow br0 'table=0 actions=resubmit(1,1)']) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=354, actions=resubmit(1,1) OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - OFPTC_TABLE_MISS_CONTINUE]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl add-flow br0 'table=1 dl_src=10:11:11:11:11:11 actions=controller']) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all continue]) dnl Miss table 0, Hit table 1 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) dnl Hit table 0, Miss all other tables, sent to controller AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl table=1, n_packets=3, n_bytes=354, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_CONTINUE]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl table=0 actions=goto_table(1) table=2 dl_src=10:11:11:11:11:11 actions=controller ]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all continue]) dnl Hit table 0, Miss table 1, Hit table 2 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x0 total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) dnl Hit table 1, Miss all other tables, sent to controller AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl NXT_PACKET_IN (xid=0x0): table_id=253 cookie=0x0 total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=6, n_bytes=708, actions=goto_table:1 table=2, n_packets=3, n_bytes=354, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_CONTINUE]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl table=0 actions=resubmit(1,1) table=2 dl_src=10:11:11:11:11:11 actions=controller ]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all continue]) dnl Hit table 0, Miss table 1, Dropped AT_CHECK([ovs-ofctl monitor br0 65534 -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) dnl Hit table 1, Dropped AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=6, n_bytes=708, actions=resubmit(1,1) table=2, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - OFPTC_TABLE_MISS_DROP]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all drop]) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Test that missed packets are dropped for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - goto table and OFPTC_TABLE_MISS_DROP]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flow br0 'table=0 actions=goto_table(1)']) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all drop]) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Test that missed packets are dropped for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=354, actions=goto_table:1 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Table Miss - resubmit and OFPTC_TABLE_MISS_DROP]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -OOpenFlow12 add-flow br0 'table=0 actions=resubmit(1,1)']) AT_CHECK([ovs-ofctl -OOpenFlow11 mod-table br0 all drop]) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Test that missed packets are dropped for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -OOpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=354, actions=resubmit(1,1) OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - controller]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl cookie=0x0 dl_src=10:11:11:11:11:11 actions=controller cookie=0x1 dl_src=20:22:22:22:22:22 actions=controller,resubmit(80,1) cookie=0x2 dl_src=30:33:33:33:33:33 actions=mod_vlan_vid:15,controller cookie=0x3 table=1 in_port=80 actions=load:1->NXM_NX_REG0[[]],mod_vlan_vid:80,controller,resubmit(81,2) cookie=0x4 table=2 in_port=81 actions=load:2->NXM_NX_REG1[[]],mod_dl_src:80:81:81:81:81:81,controller,resubmit(82,3) cookie=0x5 table=3 in_port=82 actions=load:3->NXM_NX_REG2[[]],mod_dl_dst:82:82:82:82:82:82,controller,resubmit(83,4) cookie=0x6 table=4 in_port=83 actions=load:4->NXM_NX_REG3[[]],mod_nw_src:83.83.83.83,controller,resubmit(84,5) cookie=0x7 table=5 in_port=84 actions=load:5->NXM_NX_REG4[[]],load:6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,controller,resubmit(85,6) cookie=0x8 table=6 in_port=85 actions=mod_tp_src:85,controller,resubmit(86,7) cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller cookie=0xa dl_src=40:44:44:44:44:41 actions=mod_vlan_vid:99,mod_vlan_pcp:1,controller cookie=0xd dl_src=80:88:88:88:88:88 arp actions=load:2->OXM_OF_ARP_OP[[]],controller,load:0xc0a88001->OXM_OF_ARP_SPA[[]],controller,load:0x404444444441->OXM_OF_ARP_THA[[]],load:0x01010101->OXM_OF_ARP_SPA[[]],load:0x02020202->OXM_OF_ARP_TPA[[]],controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Flow miss. AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via no_match) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) dnl Modified controller action. AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x001)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e dnl OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e dnl OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e ]) dnl Modified VLAN controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:41,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no ]) dnl Checksum TCP. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=11),tcp_flags(0x001)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=58 reg0=0x1,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=58 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d dnl NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d dnl NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:4880 dnl NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:6082 dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=85,tp_dst=11,tcp_flags=fin tcp_csum:6035 dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered) tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea ]) dnl Checksum UDP. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 ; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 1C 00 00 00 00 00 11 00 00 C0 A8 00 01 C0 A8 00 02 00 08 00 0B 00 00 12 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered) udp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:1234 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 reg0=0x1,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:1234 dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:1234 dnl NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:1234 dnl NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:2c37 dnl NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=8,tp_dst=11 udp_csum:4439 dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=11 udp_csum:43ec dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86 udp_csum:43a1 dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered) udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86 udp_csum:43a1 ]) dnl Modified ARP controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=80:88:88:88:88:88,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Checksum SCTP. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 ; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 24 00 00 00 00 00 84 00 00 C0 A8 00 01 C0 A8 00 02 04 58 08 af 00 00 00 00 d9 d7 91 57 01 00 00 34 cf 28 ec 4e 00 01 40 00 00 0a ff ff b7 53 24 19 00 05 00 08 7f 00 00 01 00 05 00 08 c0 a8 02 07 00 0c 00 06 00 05 00 00 80 00 00 04 c0 00 00 04' done AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=98 in_port=1 (via action) data_len=98 (unbuffered) sctp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=102 reg0=0x1,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=102 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157 dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=2223 sctp_csum:dd778f5f dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86 sctp_csum:62051f56 dnl NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered) sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=85,tp_dst=86 sctp_csum:62051f56 ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, n_packets=3, n_bytes=212, dl_src=20:22:22:22:22:22 actions=CONTROLLER:65535,resubmit(80,1) cookie=0x2, n_packets=3, n_bytes=162, dl_src=30:33:33:33:33:33 actions=mod_vlan_vid:15,CONTROLLER:65535 cookie=0x3, table=1, n_packets=3, n_bytes=212, in_port=80 actions=load:0x1->NXM_NX_REG0[[]],mod_vlan_vid:80,CONTROLLER:65535,resubmit(81,2) cookie=0x4, table=2, n_packets=3, n_bytes=212, in_port=81 actions=load:0x2->NXM_NX_REG1[[]],mod_dl_src:80:81:81:81:81:81,CONTROLLER:65535,resubmit(82,3) cookie=0x5, table=3, n_packets=3, n_bytes=212, in_port=82 actions=load:0x3->NXM_NX_REG2[[]],mod_dl_dst:82:82:82:82:82:82,CONTROLLER:65535,resubmit(83,4) cookie=0x6, table=4, n_packets=3, n_bytes=212, in_port=83 actions=load:0x4->NXM_NX_REG3[[]],mod_nw_src:83.83.83.83,CONTROLLER:65535,resubmit(84,5) cookie=0x7, table=5, n_packets=3, n_bytes=212, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6) cookie=0x8, table=6, n_packets=3, n_bytes=212, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7) cookie=0x9, table=7, n_packets=3, n_bytes=212, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=40:44:44:44:44:41 actions=mod_vlan_vid:99,mod_vlan_pcp:1,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=126, arp,dl_src=80:88:88:88:88:88 actions=load:0x2->NXM_OF_ARP_OP[[]],CONTROLLER:65535,load:0xc0a88001->NXM_OF_ARP_SPA[[]],CONTROLLER:65535,load:0x404444444441->NXM_NX_ARP_THA[[]],load:0x1010101->NXM_OF_ARP_SPA[[]],load:0x2020202->NXM_OF_ARP_TPA[[]],CONTROLLER:65535 n_packets=3, n_bytes=162, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - controller with slow-path action]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 "in_port=1,actions=debug_slow,controller"]) AT_CHECK([ovs-ofctl monitor -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c dnl OFPT_PACKET_IN (xid=0x0): total_len=118 in_port=1 (via action) data_len=118 (unbuffered) tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=9,tcp_flags=ack tcp_csum:4a2c ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - controller action without megaflows]) OVS_VSWITCHD_START add_of_ports br0 1 AT_CHECK([ovs-ofctl add-flow br0 in_port=1,action=controller]) AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [dnl megaflows disabled ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)']) done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:1, bytes:14, used:0.001s, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Add a controller meter. AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=controller pktps stats bands=type=drop rate=2']) dnl Advance time by 1 second. AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) for i in `seq 1 8`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x4321)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:7, bytes:98, used:0.001s, actions:sample(sample=100.0%,actions(meter(0),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535)))) ]) AT_CHECK([ovs-appctl time/warp 1], [0], [ignore]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Out of 8 packets we sent, two were passed by the rate limiter, and dnl the rest of packets were blocked. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x4321 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x4321 ]) dnl Check meter stats to make it gives the same picture; dnl 7 packets hit the meter, but 6 packets are dropped by band0. AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:controller flow_count:0 packet_in_count:8 byte_in_count:112 duration:0.0s bands: 0: packet_count:6 byte_count:84 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MPLS handling]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl cookie=0xa dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller cookie=0xa dl_src=41:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],pop_mpls:0x0800,controller cookie=0xa dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller cookie=0xa dl_src=40:44:44:44:44:45 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,controller cookie=0xa dl_src=40:44:44:44:44:46 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),controller cookie=0xa dl_src=40:44:44:44:44:47 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),controller cookie=0xa dl_src=40:44:44:44:44:48 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,controller cookie=0xa mpls,dl_src=40:44:44:44:44:49 actions=push_mpls:0x8848,load:10->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller cookie=0xd dl_src=60:66:66:66:00:01 actions=pop_mpls:0x0800,dec_ttl,controller cookie=0xd dl_src=60:66:66:66:00:02 actions=pop_mpls:0x0800,load:0xa000001->OXM_OF_IPV4_DST[[]],controller cookie=0xd dl_src=60:66:66:66:00:03 actions=pop_mpls:0x0800,move:OXM_OF_IPV4_DST[[]]->OXM_OF_IPV4_SRC[[]],controller cookie=0xd dl_src=60:66:66:66:00:04 actions=pop_mpls:0x0800,push:OXM_OF_IPV4_DST[[]],pop:OXM_OF_IPV4_SRC[[]],controller cookie=0xd dl_src=60:66:66:66:00:05 actions=pop_mpls:0x0800,multipath(eth_src,50,modulo_n,1,0,OXM_OF_IPV4_SRC[[0..7]]),controller cookie=0xd dl_src=60:66:66:66:00:06 actions=pop_mpls:0x0800,bundle_load(eth_src,50,hrw,ofport,OXM_OF_IPV4_SRC[[0..15]],members:1,2),controller cookie=0xd dl_src=60:66:66:66:00:07 actions=pop_mpls:0x0800,learn(table=1,hard_timeout=60,eth_type=0x800,nw_proto=6,OXM_OF_IPV4_SRC[[]]=OXM_OF_IPV4_DST[[]]),controller cookie=0xd dl_src=60:66:66:66:00:08 actions=pop_mpls:0x0806,resubmit(1,1) cookie=0xd table=1 arp actions=controller cookie=0xdeadbeef table=2 dl_src=60:66:66:66:00:09 actions=pop_mpls:0x0800,mod_nw_tos:48 cookie=0xd dl_src=60:66:66:66:00:09 actions=resubmit(,2),controller cookie=0xd dl_src=60:66:66:66:00:0a actions=pop_mpls:0x0800,mod_nw_dst:10.0.0.1,controller cookie=0xd dl_src=60:66:66:66:00:0b actions=pop_mpls:0x0800,mod_nw_src:10.0.0.1,controller cookie=0xd dl_src=60:66:66:66:01:00 actions=pop_mpls:0x8848,controller cookie=0xd dl_src=60:66:66:66:01:01 actions=pop_mpls:0x8847,dec_mpls_ttl,controller cookie=0xd dl_src=60:66:66:66:01:02 actions=pop_mpls:0x8848,load:3->OXM_OF_MPLS_TC[[]],controller cookie=0xd dl_src=60:66:66:66:02:00 actions=pop_mpls:0x8847,pop_mpls:0x0800,controller cookie=0xe dl_src=60:66:66:66:02:01 actions=pop_mpls:0x8848,pop_mpls:0x0800,dec_ttl,controller cookie=0xe dl_src=60:66:66:66:02:10 actions=pop_mpls:0x8847,dec_mpls_ttl,pop_mpls:0x0800,dec_ttl,controller cookie=0xe dl_src=60:66:66:66:03:00 actions=pop_mpls:0x8848,pop_mpls:0x8848,controller cookie=0xe dl_src=60:66:66:66:03:01 actions=pop_mpls:0x8847,pop_mpls:0x8847,dec_mpls_ttl,controller cookie=0xe dl_src=60:66:66:66:03:10 actions=pop_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8848,dec_mpls_ttl,controller cookie=0xf dl_src=60:66:66:66:04:00 actions=pop_mpls:0x0800,push_mpls:0x8847,controller cookie=0xf dl_src=60:66:66:66:04:01 actions=pop_mpls:0x0800,push_mpls:0x8848,dec_mpls_ttl,controller cookie=0xf dl_src=60:66:66:66:04:10 actions=pop_mpls:0x0800,dec_ttl,push_mpls:0x8848,dec_mpls_ttl,controller cookie=0x5 dl_src=60:66:66:66:05:00 actions=push_mpls:0x8848,pop_mpls:0x8847,controller cookie=0x5 dl_src=60:66:66:66:05:01 actions=push_mpls:0x8847,pop_mpls:0x8848,dec_mpls_ttl,controller cookie=0x5 dl_src=60:66:66:66:05:10 actions=push_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8847,dec_mpls_ttl,controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:42,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=no),udp(src=7777,dst=80)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=110 in_port=1 (via action) data_len=110 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=110 in_port=1 (via action) data_len=110 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=110 in_port=1 (via action) data_len=110 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=41:44:44:44:44:42,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=34 in_port=1 (via action) data_len=34 (unbuffered) ip,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=34 in_port=1 (via action) data_len=34 (unbuffered) ip,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=34 in_port=1 (via action) data_len=34 (unbuffered) ip,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:43,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912 ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:44,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1 ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:45,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=63,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=63,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=63,mpls_bos=1 ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:46,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 ]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:47,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:49,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=64,bos=1)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=42816 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=42816 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=42816 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:48,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=16,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=9,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=9,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=9,mpls_bos=1 ]) dnl Modified MPLS actions. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:55,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=18 in_port=1 (via action) data_len=18 (unbuffered) mpls,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=18 in_port=1 (via action) data_len=18 (unbuffered) mpls,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=64,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=18 in_port=1 (via action) data_len=18 (unbuffered) mpls,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=64,mpls_bos=1 ]) dnl Modified MPLS ipv6 controller action. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:77,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=58 in_port=1 (via action) data_len=58 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=128,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=58 in_port=1 (via action) data_len=58 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=128,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=58 in_port=1 (via action) data_len=58 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=128,mpls_bos=1 ]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS headers which tcpdump -vve shows as: dnl 60:66:66:66:66:66 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44) AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 66 66 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done #for i in 2 3; do # ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1)' #done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:01 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 01 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:02 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 02 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:02,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:02,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:02,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:03 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 03 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:03,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:03,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:03,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:04 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 04 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:04,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:04,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:04,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.2,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7743 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:05 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 05 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.0,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7745 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.0,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7745 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.0,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7745 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:06 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 06 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:06,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:06,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:06,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:07 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 07 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:07,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:07,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:07,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is an ARP frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:08 > ff:ff:ff:ff:ff:ff, ethertype MPLS unicast (0x8847), length 46: MPLS (label 20, exp 0, [S], ttl 32) AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'ff ff ff ff ff ff 60 66 66 66 00 08 88 47 00 01 41 20 00 01 08 00 06 04 00 02 60 66 66 66 00 08 c0 a8 00 01 ff ff ff ff ff ff ff ff ff ff' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=255.255.255.255,arp_op=2,arp_sha=60:66:66:66:00:08,arp_tha=ff:ff:ff:ff:ff:ff dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=255.255.255.255,arp_op=2,arp_sha=60:66:66:66:00:08,arp_tha=ff:ff:ff:ff:ff:ff dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0xd total_len=42 in_port=1 (via action) data_len=42 (unbuffered) arp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=255.255.255.255,arp_op=2,arp_sha=60:66:66:66:00:08,arp_tha=ff:ff:ff:ff:ff:ff ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:09 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 09 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:09,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=48,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:09,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=48,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:09,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=48,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:0a > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 0a 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0a,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0a,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0a,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2dee ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:0b > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 00 0b 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0b,dl_dst=50:54:00:00:00:07,nw_src=10.0.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2ded dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0b,dl_dst=50:54:00:00:00:07,nw_src=10.0.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2ded dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:0b,dl_dst=50:54:00:00:00:07,nw_src=10.0.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:2ded ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:01:00 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 01 00 88 48 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:01:01 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 01 01 88 47 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:01:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:01:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:01:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:01:02 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 01 02 88 48 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:02,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=3,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:02,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=3,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:01:02,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=3,mpls_ttl=31,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:02:00 > 50:54:00:00:02:00, ethertype MPLS unicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 02 00 88 47 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:00,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:00,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:00,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:02:01 > 50:54:00:00:02:01, ethertype MPLS multicast (0x8848), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 02 01 88 48 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:01,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with two MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:02:10 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 31) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 02 10 88 47 00 01 40 20 00 01 41 1f 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:10,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:10,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:02:10,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with three MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:03:00 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, ttl 31) dnl (label 20, exp 0, [S], ttl 30) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 03 00 88 47 00 01 40 20 00 01 40 1f 00 01 41 1e 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=30,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with three MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:03:01 > 50:54:00:00:00:00, ethertype MPLS multicast (0x8848), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, ttl 31) dnl (label 20, exp 0, [S], ttl 30) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 03 01 88 48 00 01 40 20 00 01 40 1f 00 01 41 1e 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:03:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:03:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:03:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with three MPLS label stack entries which tcpdump -vve shows as: dnl 60:66:66:66:03:10 > 50:54:00:00:00:00, ethertype MPLS unicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, ttl 31) dnl (label 20, exp 0, [S], ttl 30) dnl (tos 0x0, ttl 254, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 03 10 88 47 00 01 40 20 00 01 40 1f 00 01 41 1e 45 00 00 2c 00 00 00 00 ff 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xe total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:03:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=29,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:04:00 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 04 00 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:04:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=255,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:04:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=255,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:04:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=255,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:04:01 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 04 01 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:01,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=254,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:01,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=254,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:01,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=254,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:04:10 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 04 10 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:10,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=253,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:10,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=253,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0xf total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:04:10,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=253,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:05:00 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 05 00 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=32,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=32,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:00,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=32,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:05:01 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 05 01 88 48 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:05:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:05:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=60:66:66:66:05:01,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:05:10 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 05 10 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 dnl NXT_PACKET_IN (xid=0x0): cookie=0x5 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) mpls,vlan_tci=0x0000,dl_src=60:66:66:66:05:10,dl_dst=50:54:00:00:00:07,mpls_label=20,mpls_tc=0,mpls_ttl=31,mpls_bos=1 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x5, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:00 actions=push_mpls:0x8848,pop_mpls:0x8847,CONTROLLER:65535 cookie=0x5, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:01 actions=push_mpls:0x8847,pop_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 cookie=0x5, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:10 actions=push_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=40:44:44:44:44:45 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=40:44:44:44:44:46 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=40:44:44:44:44:47 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=40:44:44:44:44:48 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=102, dl_src=41:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],pop_mpls:0x0800,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=114, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=318, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=54, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=54, mpls,dl_src=40:44:44:44:44:49 actions=push_mpls:0x8848,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xb, n_packets=3, n_bytes=54, mpls,dl_src=50:55:55:55:55:55 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xc, n_packets=3, n_bytes=162, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=138, dl_src=60:66:66:66:00:08 actions=pop_mpls:0x0806,resubmit(1,1) cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:01 actions=pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:02 actions=pop_mpls:0x0800,load:0xa000001->NXM_OF_IP_DST[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:03 actions=pop_mpls:0x0800,move:NXM_OF_IP_DST[[]]->NXM_OF_IP_SRC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:04 actions=pop_mpls:0x0800,push:NXM_OF_IP_DST[[]],pop:NXM_OF_IP_SRC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:05 actions=pop_mpls:0x0800,multipath(eth_src,50,modulo_n,1,0,NXM_OF_IP_SRC[[0..7]]),CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:06 actions=pop_mpls:0x0800,bundle_load(eth_src,50,hrw,ofport,NXM_OF_IP_SRC[[0..15]],members:1,2),CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:07 actions=pop_mpls:0x0800,learn(table=1,hard_timeout=60,eth_type=0x800,nw_proto=6,NXM_OF_IP_SRC[[]]=NXM_OF_IP_DST[[]]),CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:09 actions=resubmit(,2),CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:0a actions=pop_mpls:0x0800,mod_nw_dst:10.0.0.1,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:0b actions=pop_mpls:0x0800,mod_nw_src:10.0.0.1,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:01:00 actions=pop_mpls:0x8848,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:01:01 actions=pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:01:02 actions=pop_mpls:0x8848,load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:00 actions=pop_mpls:0x8847,pop_mpls:0x0800,CONTROLLER:65535 cookie=0xd, table=1, n_packets=3, n_bytes=126, arp actions=CONTROLLER:65535 cookie=0xdeadbeef, table=2, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:09 actions=pop_mpls:0x0800,mod_nw_tos:48 cookie=0xe, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:01 actions=pop_mpls:0x8848,pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 cookie=0xe, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:10 actions=pop_mpls:0x8847,dec_mpls_ttl,pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 cookie=0xe, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:00 actions=pop_mpls:0x8848,pop_mpls:0x8848,CONTROLLER:65535 cookie=0xe, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:01 actions=pop_mpls:0x8847,pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 cookie=0xe, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:10 actions=pop_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 cookie=0xf, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:00 actions=pop_mpls:0x0800,push_mpls:0x8847,CONTROLLER:65535 cookie=0xf, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:01 actions=pop_mpls:0x0800,push_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 cookie=0xf, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:10 actions=pop_mpls:0x0800,dec_ttl,push_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MPLS handling with goto_table]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl table=0 mplsm actions=pop_mpls:0x800,goto_table(1) table=1 ip,ip_dscp=8 actions=controller ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:08 > 50:54:00:00:00:01, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x20, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 65534 -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 01 60 66 66 66 00 08 88 48 00 01 41 20 45 20 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): table_id=1 total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): table_id=1 total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): table_id=1 total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=186, mplsm actions=pop_mpls:0x0800,goto_table:1 table=1, n_packets=3, n_bytes=174, ip,nw_tos=32 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MPLS handling with write_actions]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' dnl N.B: The first (and only) action that accesses L3 data after the dnl pop_mpls action is present in write_actions. This exercises recirculation dnl triggered in write_actions due to a previous action not in write actions. AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl mplsm actions=pop_mpls:0x800,write_actions(dec_ttl,controller) ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) dnl Modified MPLS pop action. dnl The input is a frame with a single MPLS label stack entry which tcpdump -vve shows as: dnl 60:66:66:66:00:08 > 50:54:00:00:00:01, ethertype MPLS multicast (0x8848), length 62: MPLS (label 20, exp 0, [S], ttl 32) dnl (tos 0x20, ttl 255, id 0, offset 0, flags [none], proto TCP (6), length 44) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x77ec (correct), seq 42:46, win 10000, length 4 AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 65534 -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 01 60 66 66 66 00 08 88 48 00 01 41 20 45 20 00 2c 00 00 00 00 ff 06 3a 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([strip_metadata < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 dnl OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered) tcp,vlan_tci=0x0000,dl_src=60:66:66:66:00:08,dl_dst=50:54:00:00:00:01,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=32,nw_ecn=0,nw_ttl=254,nw_frag=no,tp_src=80,tp_dst=0,tcp_flags=0 tcp_csum:7744 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=186, mplsm actions=pop_mpls:0x0800,write_actions(dec_ttl,CONTROLLER:65535) OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - table-miss flow (OpenFlow 1.0)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) # A table-miss flow has priority 0 and no match AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flow br0 'priority=0 actions=output:CONTROLLER']) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (xid=0x0): total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, priority=0 actions=CONTROLLER:65535 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - table-miss flow (OpenFlow 1.3)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) # A table-miss flow has priority 0 and no match AT_CHECK([ovs-ofctl --protocols=OpenFlow13 add-flow br0 'priority=0 actions=output:CONTROLLER']) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow13 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl --protocols=OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, priority=0 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - table-miss flow with async config (OpenFlow 1.3)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' ovs-appctl time/stop AT_CAPTURE_FILE([ofctl_monitor.log]) # A table-miss flow has priority 0 and no match AT_CHECK([ovs-ofctl --protocols=OpenFlow13 add-flow br0 'priority=0 actions=output:CONTROLLER']) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow13 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) # Become secondary (OF 1.3), which should disable everything except port status. ovs-appctl -t ovs-ofctl ofctl/send 041800180000000200000003000000000000000000000001 # Ensure that ovs-vswitchd gets a chance to reply before sending another command. ovs-appctl time/warp 500 100 # Use OF 1.3 OFPT_SET_ASYNC to enable OFPR_NO_MATCH for secondary only. ovs-appctl -t ovs-ofctl ofctl/send 041c002000000002000000000000000100000000000000000000000000000000 ovs-appctl time/warp 500 100 for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl send: OFPT_ROLE_REQUEST (OF1.3) (xid=0x2): role=secondary generation_id=1 OFPT_ROLE_REPLY (OF1.3) (xid=0x2): role=secondary generation_id=1 dnl send: OFPT_SET_ASYNC (OF1.3) (xid=0x2): primary: PACKET_IN: (off) PORT_STATUS: (off) FLOW_REMOVED: (off) ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) secondary: PACKET_IN: no_match PORT_STATUS: (off) FLOW_REMOVED: (off) ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl --protocols=OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, priority=0 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - table-miss flow (OpenFlow 1.4)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) # A table-miss flow has priority 0 and no match AT_CHECK([ovs-ofctl --protocols=OpenFlow14 add-flow br0 'priority=0 actions=output:CONTROLLER']) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow14 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via no_match) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl --protocols=OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, priority=0 actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - packet-in reasons (Openflow 1.3)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=write_actions(output(CONTROLLER)),goto_table(1) table=1 actions=output(CONTROLLER),goto_table(2) table=2 actions=group:1234 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow13 add-group br0 'group_id=1234,type=all,bucket=output:10,bucket=output:CONTROLLER']) AT_CHECK([ovs-ofctl --protocols=OpenFlow13 add-flows br0 flows.txt]) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow13 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done AT_CHECK([ovs-ofctl packet-out br0 'in_port=NONE, packet=505400000007101111111111080045000028000000004006f97cc0a80001c0a800020008000a0000000000000000500200002e7d0000, actions=controller']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 7]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): total_len=54 in_port=ANY (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl --protocols=OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, in_port=1 actions=write_actions(CONTROLLER:65535),goto_table:1 table=1, n_packets=3, n_bytes=162, actions=CONTROLLER:65535,goto_table:2 table=2, n_packets=3, n_bytes=162, actions=group:1234 OFPST_FLOW reply (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - packet-in reasons (Openflow 1.4)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=write_actions(output(CONTROLLER)),goto_table(1) table=1 actions=output(CONTROLLER),goto_table(2) table=2 actions=group:1234 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow14 add-group br0 'group_id=1234,type=all,bucket=output:10,bucket=output:CONTROLLER']) AT_CHECK([ovs-ofctl --protocols=OpenFlow14 add-flows br0 flows.txt]) dnl Singleton controller action. AT_CHECK([ovs-ofctl monitor -P standard --protocols=OpenFlow14 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3 ; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)' done AT_CHECK([ovs-ofctl packet-out br0 'in_port=NONE, packet=505400000007101111111111080045000028000000004006f97cc0a80001c0a800020008000a0000000000000000500200002e7d0000, actions=controller']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 7]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via group) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action_set) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via group) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action_set) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=1 cookie=0x0 total_len=54 in_port=1 (via action) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): table_id=2 cookie=0x0 total_len=54 in_port=1 (via group) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): cookie=0x0 total_len=54 in_port=1 (via action_set) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d dnl OFPT_PACKET_IN (OF1.4) (xid=0x0): total_len=54 in_port=ANY (via packet_out) data_len=54 (unbuffered) tcp,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=8,tp_dst=10,tcp_flags=syn tcp_csum:2e7d ]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) AT_CHECK([ovs-ofctl --protocols=OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=162, in_port=1 actions=write_actions(CONTROLLER:65535),goto_table:1 table=1, n_packets=3, n_bytes=162, actions=CONTROLLER:65535,goto_table:2 table=2, n_packets=3, n_bytes=162, actions=group:1234 OFPST_FLOW reply (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - packet-in reasons (Openflow 1.3)]) OVS_VSWITCHD_START([dnl set bridge br0 datapath_type=dummy \ protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_CHECK([ ovs-ofctl -OOpenFlow13 del-flows br0 ovs-ofctl -OOpenFlow13 add-group br0 "group_id=6000,type=all,bucket=actions=controller,bucket=actions=resubmit(,48),bucket=actions=resubmit(,81)" ovs-ofctl -OOpenFlow13 add-flow br0 "table=0, in_port=1, vlan_tci=0x0000/0x1fff actions=write_metadata:0x67870000000000/0xffffff0000000001,goto_table:17" ovs-ofctl -OOpenFlow13 add-flow br0 "table=17, priority=10,metadata=0x67870000000000/0xffffff0000000000 actions=write_metadata:0xe067870000000000/0xfffffffffffffffe,goto_table:60" ovs-ofctl -OOpenFlow13 add-flow br0 "table=60, priority=0 actions=resubmit(,17)" ovs-ofctl -OOpenFlow13 add-flow br0 "table=17, priority=10,metadata=0xe067870000000000/0xffffff0000000000 actions=write_metadata:0x67871d4d000000/0xfffffffffffffffe,goto_table:43" ovs-ofctl -OOpenFlow13 add-flow br0 "table=43, priority=100,icmp actions=group:6000" ovs-ofctl -OOpenFlow13 add-flow br0 "table=48, priority=0 actions=resubmit(,49),resubmit(,50)" ovs-ofctl -OOpenFlow13 add-flow br0 "table=50, priority=0 actions=controller" ], [0], [ignore]) AT_CHECK([ovs-ofctl monitor -OOpenFlow13 -P standard br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 1]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=43 cookie=0x0 total_len=98 metadata=0x67871d4d000000,in_port=1 (via action) data_len=98 (unbuffered) icmp,vlan_tci=0x0000,dl_src=3a:6d:d2:09:9c:ab,dl_dst=1e:2c:e9:2a:66:9e,nw_src=192.168.10.10,nw_dst=192.168.10.30,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:6f20 dnl OFPT_PACKET_IN (OF1.3) (xid=0x0): table_id=50 cookie=0x0 total_len=98 metadata=0x67871d4d000000,in_port=1 (via no_match) data_len=98 (unbuffered) icmp,vlan_tci=0x0000,dl_src=3a:6d:d2:09:9c:ab,dl_dst=1e:2c:e9:2a:66:9e,nw_src=192.168.10.10,nw_dst=192.168.10.30,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:6f20 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ARP modification slow-path]) OVS_VSWITCHD_START add_of_ports br0 1 2 ovs-vsctl -- set Interface p2 type=dummy options:pcap=p2.pcap ovs-ofctl add-flow br0 'in_port=1,arp actions=load:2->OXM_OF_ARP_OP[[]],2,load:0xc0a88001->OXM_OF_ARP_SPA[[]],2,load:0x404444444441->OXM_OF_ARP_THA[[]],2' # Input some packets that should follow the arp modification slow-path. for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=80:88:88:88:88:88,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' done AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) # Check the packets that were output. AT_CHECK([ovs-ofctl parse-pcap p2.pcap], [0], [dnl arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00 arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - VLAN handling]) OVS_VSWITCHD_START( [set Bridge br0 fail-mode=standalone -- \ add-port br0 p1 trunks=10,12 -- \ add-port br0 p2 tag=10 -- \ add-port br0 p3 tag=12 \ other-config:priority-tags=if-nonzero -- \ add-port br0 p4 tag=12 -- \ add-port br0 p5 vlan_mode=native-tagged tag=10 -- \ add-port br0 p6 vlan_mode=native-tagged tag=10 trunks=10,12 -- \ add-port br0 p7 vlan_mode=native-untagged tag=12 -- \ add-port br0 p8 vlan_mode=native-untagged tag=12 trunks=10,12 \ other-config:priority-tags=if-nonzero -- \ add-port br0 p9 vlan_mode=dot1q-tunnel tag=10 other-config:qinq-ethtype=802.1q -- \ add-port br0 p10 vlan_mode=dot1q-tunnel tag=10 cvlans=10,12 other-config:qinq-ethtype=802.1q -- \ add-port br0 p11 vlan_mode=dot1q-tunnel tag=12 other-config:qinq-ethtype=802.1q -- \ add-port br0 p12 vlan_mode=dot1q-tunnel tag=12 other-config:qinq-ethtype=802.1q \ other-config:priority-tags=if-nonzero -- \ set Interface p1 type=dummy -- \ set Interface p2 type=dummy -- \ set Interface p3 type=dummy -- \ set Interface p4 type=dummy -- \ set Interface p5 type=dummy -- \ set Interface p6 type=dummy -- \ set Interface p7 type=dummy -- \ set Interface p8 type=dummy -- \ set Interface p9 type=dummy -- \ set Interface p10 type=dummy -- \ set Interface p11 type=dummy -- \ set Interface p12 type=dummy --]) dnl Each of these specifies an in_port by number, a VLAN VID (or "none"), dnl a VLAN PCP (used if the VID isn't "none") and the expected set of datapath dnl actions. for tuple in \ "100 none 0 drop" \ "100 0 0 drop" \ "100 0 1 drop" \ "100 10 0 1,5,6,7,8,pop_vlan,2,9" \ "100 10 1 1,5,6,7,8,pop_vlan,2,9" \ "100 11 0 5,7" \ "100 11 1 5,7" \ "100 12 0 1,5,6,pop_vlan,3,4,7,8,11,12" \ "100 12 1 1,5,6,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \ "1 none 0 drop" \ "1 0 0 drop" \ "1 0 1 drop" \ "1 10 0 5,6,7,8,100,pop_vlan,2,9" \ "1 10 1 5,6,7,8,100,pop_vlan,2,9" \ "1 11 0 drop" \ "1 11 1 drop" \ "1 12 0 5,6,100,pop_vlan,3,4,7,8,11,12" \ "1 12 1 5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \ "2 none 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "2 0 0 pop_vlan,9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "2 0 1 pop_vlan,9,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \ "2 10 0 drop" \ "2 10 1 drop" \ "2 11 0 drop" \ "2 11 1 drop" \ "2 12 0 drop" \ "2 12 1 drop" \ "3 none 0 4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "3 0 0 pop_vlan,4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "3 0 1 8,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \ "3 10 0 drop" \ "3 10 1 drop" \ "3 11 0 drop" \ "3 11 1 drop" \ "3 12 0 drop" \ "3 12 1 drop" \ "4 none 0 3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "4 0 0 pop_vlan,3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "4 0 1 3,8,12,pop_vlan,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \ "4 10 0 drop" \ "4 10 1 drop" \ "4 11 0 drop" \ "4 11 1 drop" \ "4 12 0 drop" \ "4 12 1 drop" \ "5 none 0 2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \ "5 0 0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \ "5 0 1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,6,7,8,100" \ "5 10 0 1,6,7,8,100,pop_vlan,2,9" \ "5 10 1 1,6,7,8,100,pop_vlan,2,9" \ "5 11 0 7,100" \ "5 11 1 7,100" \ "5 12 0 1,6,100,pop_vlan,3,4,7,8,11,12" \ "5 12 1 1,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \ "6 none 0 2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \ "6 0 0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \ "6 0 1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,5,7,8,100" \ "6 10 0 1,5,7,8,100,pop_vlan,2,9" \ "6 10 1 1,5,7,8,100,pop_vlan,2,9" \ "6 11 0 drop" \ "6 11 1 drop" \ "6 12 0 1,5,100,pop_vlan,3,4,7,8,11,12" \ "6 12 1 1,5,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \ "7 none 0 3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "7 0 0 pop_vlan,3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "7 0 1 3,8,12,pop_vlan,4,11,push_vlan(vid=12,pcp=1),1,5,6,100" \ "7 10 0 1,5,6,8,100,pop_vlan,2,9" \ "7 10 1 1,5,6,8,100,pop_vlan,2,9" \ "7 11 0 5,100" \ "7 11 1 5,100" \ "7 12 0 1,5,6,100,pop_vlan,3,4,8,11,12" \ "7 12 1 1,5,6,100,pop_vlan,4,11,push_vlan(vid=0,pcp=1),3,8,12" \ "8 none 0 3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "8 0 0 pop_vlan,3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "8 0 1 3,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \ "8 10 0 1,5,6,7,100,pop_vlan,2,9" \ "8 10 1 1,5,6,7,100,pop_vlan,2,9" \ "8 11 0 drop" \ "8 11 1 drop" \ "8 12 0 1,5,6,100,pop_vlan,3,4,7,11,12" \ "8 12 1 1,5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,12" \ "9 none 0 2,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "9 10 0 10,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "9 11 0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "10 none 0 drop" \ "10 0 0 drop" \ "10 11 0 drop" \ "10 12 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \ "11 10 0 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100" \ "11 10 1 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100" do set $tuple in_port=$1 vlan=$2 pcp=$3 expected=$4 if test $vlan = none; then flow="in_port($in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0xabcd)" else flow="in_port($in_port),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=$vlan,pcp=$pcp),encap(eth_type(0xabcd))" fi echo "----------------------------------------------------------------------" echo "in_port=$in_port vlan=$vlan pcp=$pcp" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - VLAN depth limit]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 ]) AT_DATA([flows.txt], [dnl table=0 in_port=1,eth_type=0x8100,vlan_tci=0x0010/0x01ff actions=output:2 table=0 in_port=1,eth_type=0xabcd,vlan_tci=0x0010/0x01ff actions=output:3 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) flow="in_port(1),eth(src=00:11:22:33:44:55,dst=00:22:44:66:88:00),eth_type(0x8100),vlan(vid=16,pcp=0), \ encap(eth_type(0x8100),vlan(vid=17,pcp=0),encap(eth_type(0xabcd)))" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_CHECK([ovs-vsctl set Open_vswitch `ovs-vsctl show | head -n1` other_config:vlan-limit=0]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Multi-VLAN actions]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 ]) AT_CHECK([ovs-vsctl set Open_vswitch `ovs-vsctl show | head -n1` other_config:vlan-limit=0]) AT_DATA([flows.txt], [dnl table=0 in_port=1,vlan_tci=0x1100/0x1fff actions=pop_vlan,output:2 table=0 in_port=1,vlan_tci=0x1101/0x1fff actions=push_vlan:0x8100,set_field:0x1201/0x1fff->vlan_tci,output:2 table=0 in_port=1,vlan_tci=0x1102/0x1fff actions=push_vlan:0x88a8,set_field:0x1202/0x1fff->vlan_tci,output:2 table=0 in_port=1,vlan_tci=0x1103/0x1fff actions=set_field:0x1203/0x1fff->vlan_tci,output:2 table=0 in_port=1,vlan_tci=0x1104/0x1fff actions=pop_vlan,goto_table:1 table=1 vlan_tci=0 actions=output:2 table=1 vlan_tci=0x1300/0x1fff actions=pop_vlan,output:2 table=1 vlan_tci=0x1301/0x1fff actions=push_vlan:0x88a8,set_field:0x1401/0x1fff->vlan_tci,output:2 table=1 vlan_tci=0x1302/0x1fff actions=set_field:0x1402/0x1fff->vlan_tci,output:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) check_act() { psd_hdr="in_port(1),eth(src=00:11:22:33:44:55,dst=00:22:44:66:88:00)," AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$psd_hdr$1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $2 ]) } check_act "eth_type(0x8100),vlan(vid=0x0100,pcp=0),encap(eth_type(0xabcd))" \ "pop_vlan,2" check_act "eth_type(0x8100),vlan(vid=0x0101,pcp=0),encap(eth_type(0xabcd))" \ "push_vlan(vid=513,pcp=0),2" check_act "eth_type(0x8100),vlan(vid=0x0102,pcp=0),encap(eth_type(0xabcd))" \ "push_vlan(tpid=0x88a8,vid=514,pcp=0),2" check_act "eth_type(0x8100),vlan(vid=0x0103,pcp=0),encap(eth_type(0xabcd))" \ "pop_vlan,push_vlan(vid=515,pcp=0),2" check_act "eth_type(0x8100),vlan(vid=0x0104,pcp=0),encap(eth_type(0xabcd))" \ "pop_vlan,2" check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\ vlan(vid=0x0300,pcp=0),encap(eth_type(0xabcd)))" "pop_vlan,pop_vlan,2" check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\ vlan(vid=0x0301,pcp=0),encap(eth_type(0xabcd)))" \ "pop_vlan,push_vlan(tpid=0x88a8,vid=1025,pcp=0),2" check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\ vlan(vid=0x0302,pcp=0),encap(eth_type(0xabcd)))" \ "pop_vlan,pop_vlan,push_vlan(vid=1026,pcp=0),2" OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MPLS handling]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl dl_src=40:44:44:44:00:00 actions=push_mpls:0x8847,controller dl_src=40:44:44:44:00:01,mpls actions=push_mpls:0x8847,controller dl_src=40:44:44:44:00:02,mpls actions=push_mpls:0x8848,controller ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow12 add-flows br0 flows.txt]) dnl In this test, we push an MPLS tag to an ethernet packet. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:00,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=122 in_port=1 (via action) data_len=122 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 00 00 88 47 00 00 00000010 01 40 45 00 00 68 00 00-00 00 40 06 f9 3c c0 a8 00000020 00 01 c0 a8 00 02 00 00-00 00 00 00 00 00 00 00 00000030 00 00 50 00 00 00 4a 4d-00 00 00 01 02 03 04 05 00000040 06 07 08 09 0a 0b 0c 0d-0e 0f 10 11 12 13 14 15 00000050 16 17 18 19 1a 1b 1c 1d-1e 1f 20 21 22 23 24 25 00000060 26 27 28 29 2a 2b 2c 2d-2e 2f 30 31 32 33 34 35 00000070 36 37 38 39 3a 3b 3c 3d-3e 3f OFPT_PACKET_IN (OF1.2): total_len=122 in_port=1 (via action) data_len=122 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 00 00 88 47 00 00 00000010 01 40 45 00 00 68 00 00-00 00 40 06 f9 3c c0 a8 00000020 00 01 c0 a8 00 02 00 00-00 00 00 00 00 00 00 00 00000030 00 00 50 00 00 00 4a 4d-00 00 00 01 02 03 04 05 00000040 06 07 08 09 0a 0b 0c 0d-0e 0f 10 11 12 13 14 15 00000050 16 17 18 19 1a 1b 1c 1d-1e 1f 20 21 22 23 24 25 00000060 26 27 28 29 2a 2b 2c 2d-2e 2f 30 31 32 33 34 35 00000070 36 37 38 39 3a 3b 3c 3d-3e 3f OFPT_PACKET_IN (OF1.2): total_len=122 in_port=1 (via action) data_len=122 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:00,dl_dst=50:54:00:00:00:07,mpls_label=0,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 00 00 88 47 00 00 00000010 01 40 45 00 00 68 00 00-00 00 40 06 f9 3c c0 a8 00000020 00 01 c0 a8 00 02 00 00-00 00 00 00 00 00 00 00 00000030 00 00 50 00 00 00 4a 4d-00 00 00 01 02 03 04 05 00000040 06 07 08 09 0a 0b 0c 0d-0e 0f 10 11 12 13 14 15 00000050 16 17 18 19 1a 1b 1c 1d-1e 1f 20 21 22 23 24 25 00000060 26 27 28 29 2a 2b 2c 2d-2e 2f 30 31 32 33 34 35 00000070 36 37 38 39 3a 3b 3c 3d-3e 3f ]) dnl In this test, we push an MPLS tag to an MPLS packet. The LSE should be dnl copied exactly, except for the BOS bit. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:01,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=0,ttl=64,bos=1)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:01,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 01 88 47 00 00 00000010 a0 40 00 00 a1 40 dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:01,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 01 88 47 00 00 00000010 a0 40 00 00 a1 40 dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mpls,vlan_tci=0x0000,dl_src=40:44:44:44:00:01,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 01 88 47 00 00 00000010 a0 40 00 00 a1 40 ]) dnl In this test, we push an MPLS tag to an MPLS packet. The LSE should be dnl copied exactly, except for the BOS bit. The ethertype should be updated dnl to the MPLS ethertype of the MPLS push action which differs to that dnl of the input packet. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:02,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=0,ttl=64,bos=1)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:00:02,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 02 88 48 00 00 00000010 a0 40 00 00 a1 40 dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:00:02,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 02 88 48 00 00 00000010 a0 40 00 00 a1 40 dnl OFPT_PACKET_IN (OF1.2): total_len=22 in_port=1 (via action) data_len=22 (unbuffered) mplsm,vlan_tci=0x0000,dl_src=40:44:44:44:00:02,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=0,mpls_lse1=41280 00000000 50 54 00 00 00 07 40 44-44 44 00 02 88 48 00 00 00000010 a0 40 00 00 a1 40 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - VLAN+MPLS handling]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl cookie=0xa dl_src=40:44:44:44:54:50 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,controller cookie=0xa dl_src=40:44:44:44:54:51 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,controller cookie=0xa dl_src=40:44:44:44:54:52 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller cookie=0xa dl_src=40:44:44:44:54:53 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller cookie=0xa dl_src=40:44:44:44:54:54 actions=push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller cookie=0xa dl_src=40:44:44:44:54:55 actions=push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller cookie=0xa dl_src=40:44:44:44:54:56 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller cookie=0xa dl_src=40:44:44:44:54:57 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller cookie=0xa dl_src=40:44:44:44:54:58,vlan_tci=0x1000/0x1000 actions=load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller cookie=0xa dl_src=40:44:44:44:54:59,vlan_tci=0x1000/0x1000 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],set_vlan_pcp:1,load:99->OXM_OF_VLAN_VID[[]],controller ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow12 add-flows br0 flows.txt]) dnl Modified MPLS controller action. dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see dnl both of these in the final flow AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:50,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be kept as dnl inner vlan. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:51,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see dnl both of these in the final flow AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:52,dst=52:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be kept as dnl inner vlan. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:53,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, we push the VLAN tag before pushing a MPLS tag, but these dnl actions are reordered, so we see both of these in the final flow. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:54,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be kept as dnl inner vlan. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:55,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, we push the VLAN tag before pushing a MPLS tag, but these dnl actions are reordered, so we see both of these in the final flow. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:56,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be kept as dnl inner vlan. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 -m 65534 -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:57,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f OFPT_PACKET_IN (OF1.2): total_len=130 in_port=1 (via action) data_len=130 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63 00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 68 00 00 00000020 00 00 40 06 f9 3c c0 a8-00 01 c0 a8 00 02 00 00 00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 4a 4d 00000040 00 00 00 01 02 03 04 05-06 07 08 09 0a 0b 0c 0d 00000050 0e 0f 10 11 12 13 14 15-16 17 18 19 1a 1b 1c 1d 00000060 1e 1f 20 21 22 23 24 25-26 27 28 29 2a 2b 2c 2d 00000070 2e 2f 30 31 32 33 34 35-36 37 38 39 3a 3b 3c 3d 00000080 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be kept as dnl inner vlan. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:58,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) dnl Modified MPLS controller action. dnl In this test, the input packet is vlan-tagged, which should be modified dnl before we push MPLS and VLAN tags. AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:59,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' done OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f OFPT_PACKET_IN (OF1.2): total_len=126 in_port=1 (via action) data_len=126 (unbuffered) mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1 00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63 00000010 88 47 00 00 a1 40 45 00-00 68 00 00 00 00 40 06 00000020 f9 3c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00 00000030 00 00 00 00 00 00 50 00-00 00 4a 4d 00 00 00 01 00000040 02 03 04 05 06 07 08 09-0a 0b 0c 0d 0e 0f 10 11 00000050 12 13 14 15 16 17 18 19-1a 1b 1c 1d 1e 1f 20 21 00000060 22 23 24 25 26 27 28 29-2a 2b 2c 2d 2e 2f 30 31 00000070 32 33 34 35 36 37 38 39-3a 3b 3c 3d 3e 3f ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl --protocols=OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0xa, n_packets=3, n_bytes=354, dl_src=40:44:44:44:54:50 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=354, dl_src=40:44:44:44:54:52 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=354, dl_src=40:44:44:44:54:54 actions=push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=354, dl_src=40:44:44:44:54:56 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, dl_src=40:44:44:44:54:51 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, dl_src=40:44:44:44:54:53 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, dl_src=40:44:44:44:54:55 actions=push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, dl_src=40:44:44:44:54:57 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, vlan_tci=0x1000/0x1000,dl_src=40:44:44:44:54:58 actions=load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xa, n_packets=3, n_bytes=366, vlan_tci=0x1000/0x1000,dl_src=40:44:44:44:54:59 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],set_field:1->vlan_pcp,load:0x63->OXM_OF_VLAN_VID[[]],CONTROLLER:65535 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fragment handling - trace]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 90 AT_DATA([flows.txt], [dnl priority=75 tcp nw_frag=no tp_dst=80 actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:1 priority=75 tcp nw_frag=first tp_dst=80 actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:2 priority=50 tcp nw_frag=no actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:4 priority=50 tcp nw_frag=first actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:5 priority=50 tcp nw_frag=later actions=output:6 ]) AT_CHECK([ovs-ofctl replace-flows br0 flows.txt]) base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128" no_flow="$base_flow,frag=no),tcp(src=12345,dst=80)" first_flow="$base_flow,frag=first),tcp(src=12345,dst=80)" later_flow="$base_flow,frag=later)" # mode no first later for tuple in \ 'normal 1 5 6' \ 'drop 1 drop drop' \ 'nx-match 1 2 6' do set $tuple mode=$1 no=$2 first=$3 later=$4 AT_CHECK([ovs-ofctl set-frags br0 $mode]) for type in no first later; do eval flow=\$${type}_flow exp_output=\$$type printf "\n%s\n" "----$mode $type-----" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) : > expout if test $mode = drop && test $type != no; then echo ' 0. Packets are IP fragments and the fragment handling mode is "drop".' >> expout echo "Datapath actions: $exp_output" >> expout elif test $type = later; then echo "Datapath actions: $exp_output" >> expout else echo "Datapath actions: set(tcp(src=80)),$exp_output" >> expout fi AT_CHECK([grep 'IP fragments' stdout; tail -1 stdout], [0], [expout]) done done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fragment handling - upcall]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 90 AT_DATA([flows.txt], [dnl priority=75 tcp ip_frag=no tp_dst=80 actions=set_field:81->tcp_dst,output:1 priority=75 tcp ip_frag=first tp_dst=80 actions=set_field:81->tcp_dst,output:2 priority=50 tcp ip_frag=no actions=set_field:81->tcp_dst,output:4 priority=50 tcp ip_frag=first actions=set_field:81->tcp_dst,output:5 priority=50 tcp ip_frag=later actions=output:6 ]) AT_CHECK([ovs-ofctl replace-flows br0 flows.txt]) base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128" no_flow="$base_flow,frag=no),tcp(src=12345,dst=80)" first_flow="$base_flow,frag=first),tcp(src=12345,dst=80)" later_flow="$base_flow,frag=later)" AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) mode=normal AT_CHECK([ovs-ofctl set-frags br0 $mode]) for type in no first later; do eval flow=\$${type}_flow printf "\n%s\n" "----$mode $type-----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout]) done AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),5 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, bytes:0, used:never, actions:6 ]) mode=drop AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl set-frags br0 $mode]) for type in no first later; do eval flow=\$${type}_flow printf "\n%s\n" "----$mode $type-----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout]) done AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=first), packets:0, bytes:0, used:never, actions:drop recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=later), packets:0, bytes:0, used:never, actions:drop ]) mode=nx-match AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl set-frags br0 $mode]) for type in no first later; do eval flow=\$${type}_flow printf "\n%s\n" "----$mode $type-----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout]) done AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),2 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, bytes:0, used:never, actions:6 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fragment handling - actions]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 6 90 AT_CHECK([ovs-ofctl add-flow br0 "tcp,ip_frag=later actions=move:OXM_OF_TCP_DST[[0..7]]->OXM_OF_TCP_SRC[[0..7]],output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl source field tcp_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 "tcp,ip_frag=later actions=move:OXM_OF_PKT_REG0[[0..7]]->OXM_OF_TCP_SRC[[0..7]],output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl destination field tcp_src lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl add-flow br0 "udp,ip_frag=later actions=set_field:8888->udp_src,output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl set_field udp_src lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl add-flow br0 "udp,ip_frag=later actions=load:8888->NXM_OF_UDP_DST[[]],output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl set_field udp_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl add-flow br0 "sctp,ip_frag=later actions=set_field:8888->sctp_src,output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl set_field sctp_src lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl add-flow br0 "sctp,ip_frag=later actions=set_field:8888->sctp_dst,output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl set_field sctp_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_CHECK([ovs-ofctl add-flow br0 "tcp,ip_frag=later actions=learn(table=1,hard_timeout=60,eth_type=0x800,nw_proto=6,NXM_OF_IP_SRC[[]]=NXM_OF_IP_DST[[]],NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],output:NXM_NX_REG0[[0..15]]),output:1"], [1], [], [stderr]) AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl source field tcp_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) AT_DATA([flows.txt], [dnl priority=75 tcp actions=load:42->OXM_OF_TCP_SRC[[0..7]],output:1 ]) AT_CHECK([ovs-ofctl -O OpenFlow12 replace-flows br0 flows.txt]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) mode=normal AT_CHECK([ovs-ofctl set-frags br0 $mode]) for frag in 4000 6000 6008 4010; do printf "\n%s\n" "----$mode $frag-----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 003c 2e24 $frag 40 06 465d ac11370d ac11370b 828b 0016 751e267b 00000000 a002 16d0 1736 0000 02 04 05 b4 04 02 08 0a 2d 25 08 5f 00 00 00 00 01 03 03 07"]) done dnl The set_field action only modifies 8 bits of the tcp_src, so both the flow dnl wildcard and the set_field action have a mask of 0xFF. Up to (including) dnl OVS-2.5, the wildcards and set_field mask are shared internally. AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=33419/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=33419/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:74, used:0.001s, actions:1 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl set-frags br0 $mode]) for frag in 4000 6000 6008 4010; do printf "\n%s\n" "----$mode $frag truncated transport header -----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0018 2e24 $frag 40 06 465d ac11370d ac11370b 828b 0016"]) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=0/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=0/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:38, used:0.001s, actions:1 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl set-frags br0 $mode]) for frag in 4000 6000 6001 4002; do printf "\n%s\n" "----$mode $frag missing transport header-----" AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 2e24 $frag 40 06 465d ac11370d ac11370b"]) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=0/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=0/0xff), packets:0, bytes:0, used:never, actions:set(tcp(src=42/0xff)),1 recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:34, used:0.001s, actions:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fragment handling - reassembly]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 90 AT_DATA([flows.txt], [dnl dnl Expand flow mask. table=0 in_port=90,dl_src=55:55:55:55:55:55, actions=drop table=0 in_port=90,ip,nw_src=10.0.0.1, actions=drop table=0 in_port=1,dl_src=55:55:55:55:55:55, actions=drop table=0 in_port=1,ip,nw_src=10.0.0.2, actions=drop table=0 in_port=90,ip,tcp actions=ct(commit, table=1, zone=10) table=0 in_port=1,ip,tcp actions=ct(commit, table=2, zone=20) table=1 tcp actions=2 table=2 tcp actions=3 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 replace-flows br0 flows.txt]) dnl Test frag expiry. AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 0001 8192 40 06 3316 ac11370d ac11370b"]) ovs-appctl time/warp 10000 AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 0001 8192 40 06 3316 ac11370d ac11370b"]) dnl Test that no packets flow. AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\) | sed s'/recirc(.*)/recirc(X)/'], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=later), packets:0, bytes:0, used:never, actions:ct(commit,zone=10),recirc(X) ]) dnl Expire second frag. ovs-appctl time/warp 10000 dnl Test frag purge AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 0002 8192 40 06 3315 ac11370d ac11370b"]) ovs-appctl time/warp 33000 AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 0003 8192 40 06 3314 ac11370d ac11370b"]) dnl Test that no packets flow. AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\) | sed s'/recirc(.*)/recirc(X)/'], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=later), packets:0, bytes:0, used:never, actions:ct(commit,zone=10),recirc(X) ]) dnl Purge second frag ovs-appctl time/warp 33000 dnl Make sure all four packets are counted properly in the coverage. AT_CHECK([ovs-appctl coverage/show | grep -c "^ipf.*total: 2"], [0], [2 ]) zero1208=$(printf '0%.0s' $(seq 2416)) dnl Test that reassembled packets flow. AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 04c4 0002 2000 40 06 8ff7 ac11370d ac11370b dnl 0000 0001 00000000 00000000 50 10 8000 604c 0000 dnl $zero1208"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 04c4 0002 0096 40 06 af61 ac11370d ac11370b dnl $zero1208"]) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/recirc[[^)]]*)/recirc()/g' | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc(),in_port(90),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=first), packets:0, bytes:0, used:never, actions:ct(commit,zone=10),recirc() recirc(),in_port(90),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=later), packets:0, bytes:0, used:never, actions:ct(commit,zone=10),recirc() recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first), packets:0, bytes:0, used:never, actions:2 recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, bytes:0, used:never, actions:2 ]) dnl Clear ipf state. ovs-appctl time/warp 33000 dnl Send a lot of small frags to overload the dp packet batch max. AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 420], [], [dnl setting minimum fragment size successful ]) zero107=$(printf '0%.0s' $(seq 214)) dnl First zone. AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e120004006423e0202020201010101000200010000000000000000500220001c7a0000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e120384006420602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12070400641ce02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e120a84006419602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e120e04006415e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e121184006412602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12150400640ee02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12188400640b602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e121c04006407e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e121f84006404602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e122304006400e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1226840063fd602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e122a040063f9e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e122d840063f6602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1231040063f2e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1234840063ef602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1238040063ebe02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e123b840063e8602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e123f040063e4e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1242840063e1602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1246040063dde02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1249840063da602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e124d040063d6e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1250840063d3602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1254040063cfe02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1257840063cc602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e125b040063c8e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e125e840063c5602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1262040063c1e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1265840063be602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1269040063bae02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e126c840063b7602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1270040063b3e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1273840063b0602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1277040063ace02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e127a840063a9602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e127e040063a5e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e1281840063a2602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12850400639ee02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12888400639b602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e128c04006397e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e128f84006394602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e129304006390e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12968400638d602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e129a04006389e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e129d84006386602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12a104006382e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12a48400637f602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12a80400637be02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12ab84006378602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12af04006374e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12b284006371602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12b60400636de02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12b98400636a602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12bd04006366e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12c084006363602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12c40400635fe02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12c78400635c602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12cb04006358e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12ce84006355602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12d204006351e02020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "1111111111112222222222220800450001d410e12d58400634e602020202010101010000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "11111111111122222222222208004500010810e10d904006557a0202020201010101000000000000000000000000000000000000000000000000000000000000${zero107}${zero107}"]) dnl Second zone. AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2200040064e4d0101010102020202000100020000000000000000500220001c7a0000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2203840064e1501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2207040064ddd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d220a840064da501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d220e040064d6d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2211840064d3501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2215040064cfd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2218840064cc501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d221c040064c8d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d221f840064c5501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2223040064c1d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2226840064be501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d222a040064bad01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d222d840064b7501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2231040064b3d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2234840064b0501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2238040064acd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d223b840064a9501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d223f040064a5d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d2242840064a2501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22460400649ed01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22498400649b501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d224d04006497d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d225084006494501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d225404006490d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22578400648d501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d225b04006489d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d225e84006486501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d226204006482d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22658400647f501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22690400647bd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d226c84006478501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d227004006474d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d227384006471501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22770400646dd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d227a8400646a501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d227e04006466d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d228184006463501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22850400645fd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22888400645c501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d228c04006458d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d228f84006455501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d229304006451d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22968400644e501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d229a0400644ad01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d229d84006447501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22a104006443d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22a484006440501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22a80400643cd01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22ab84006439501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22af04006435d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22b284006432501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22b60400642ed01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22b98400642b501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22bd04006427d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22c084006424501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22c404006420d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22c78400641d501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22cb04006419d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22ce84006416501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22d204006412d01010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "2222222222221111111111110800450001d404d22d58400640f501010101020202020000000000000000000000000000000000000000${zero107}${zero107}${zero107}${zero107}"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "22222222222211111111111108004500010804d20d90400661890101010102020202000000000000000000000000000000000000000000000000000000000000${zero107}${zero107}"]) dnl Bump the remaining queued packets AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 04c4 0002 0096 40 06 af61 ac11370d ac11370b dnl $zero1208"]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "0021853763af 0026b98cb0f9 0800 4500 04c4 0002 0096 40 06 af61 ac11370d ac11370b dnl $zero1208"]) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/recirc[[^)]]*)/recirc()/g' | sed 's/used.*, / /' | sort], [0], [dnl flow-dump from the main thread: recirc(),in_port(1),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=later), packets:0, bytes:0, actions:ct(commit,zone=20),recirc() recirc(),in_port(1),packet_type(ns=0,id=0),eth(src=11:11:11:11:11:11),eth_type(0x0800),ipv4(src=1.1.1.1/248.0.0.0,proto=6,frag=first), packets:0, bytes:0, actions:ct(commit,zone=20),recirc() recirc(),in_port(1),packet_type(ns=0,id=0),eth(src=11:11:11:11:11:11),eth_type(0x0800),ipv4(src=1.1.1.1/248.0.0.0,proto=6,frag=later), packets:61, bytes:29198, actions:ct(commit,zone=20),recirc() recirc(),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first), packets:0, bytes:0, actions:3 recirc(),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:61, bytes:29198, actions:3 recirc(),in_port(90),packet_type(ns=0,id=0),eth(src=00:26:b9:8c:b0:f9),eth_type(0x0800),ipv4(src=172.17.55.13/128.0.0.0,proto=6,frag=later), packets:0, bytes:0, actions:ct(commit,zone=10),recirc() recirc(),in_port(90),packet_type(ns=0,id=0),eth(src=22:22:22:22:22:22),eth_type(0x0800),ipv4(src=2.2.2.2/248.0.0.0,proto=6,frag=first), packets:0, bytes:0, actions:ct(commit,zone=10),recirc() recirc(),in_port(90),packet_type(ns=0,id=0),eth(src=22:22:22:22:22:22),eth_type(0x0800),ipv4(src=2.2.2.2/248.0.0.0,proto=6,frag=later), packets:61, bytes:29198, actions:ct(commit,zone=10),recirc() recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=first), packets:0, bytes:0, actions:2 recirc(),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=later), packets:61, bytes:29198, actions:2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - handling of malformed TCP packets]) OVS_VSWITCHD_START add_of_ports br0 1 90 dnl drop packet has tcp port 0-f but allow other tcp packets AT_DATA([flows.txt], [dnl priority=75 tcp tp_dst=0/0xfff0 actions=drop priority=50 tcp actions=output:1 ]) AT_CHECK([ovs-ofctl replace-flows br0 flows.txt]) dnl good tcp pkt, tcp(sport=100,dpor=16) pkt1="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000005002200053330000" dnl malformed tcp pkt(tcp_hdr < 20 byte), tcp(sport=100,dport=16,dataofs=1) pkt2="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a141414140064001000000000000000001002200093330000" dnl malformed tcp pkt(tcp_hdr > pkt_len), tcp(sport=100,dport=16,dataofs=15) pkt3="be95df40fb57fa163e5ee3570800450000280001000040063e940a0a0a0a14141414006400100000000000000000f002200093330000" AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt1"], [0], [stdout]) dnl for good tcp pkt, ovs can extract the tp_dst=16 AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=16/0xfff0), packets:0, bytes:0, used:never, actions:1 ]) AT_CHECK([ovs-appctl revalidator/purge], [0], [stdout]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt2"], [0], [stdout]) dnl for malformed tcp pkt(tcp_hdr < 20 byte), ovs uses default value tp_dst=0 AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=0/0xfff0), packets:0, bytes:0, used:never, actions:drop ]) AT_CHECK([ovs-appctl revalidator/purge], [0], [stdout]) AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$pkt3"], [0], [stdout]) dnl for malformed tcp pkt(tcp_hdr > pkt_len), ovs uses default value tp_dst=0 AT_CHECK([ovs-appctl dpctl/dump-flows filter=in_port\(90\),tcp], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=0/0xfff0), packets:0, bytes:0, used:never, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - exit]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 10 11 12 13 14 AT_DATA([flows.txt], [dnl in_port=1 actions=output:10,exit,output:11 in_port=2 actions=output:12,resubmit:1,output:12 in_port=3 actions=output:13,resubmit:2,output:14 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 12,10 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 13,12,10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, filter]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-vsctl \ set Bridge br0 mirrors=@m -- \ --id=@p3 get Port p3 -- \ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 filter="icmp"], [0], [ignore]) icmp_flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" tcp_flow1="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128,frag=no),tcp(dst=443)" tcp_flow2="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128,frag=no),tcp(dst=80)" AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 'actions=normal' ]) dnl Add non-matching flows, then change the mirror to match one of the flows, dnl then add a matching flow. AT_CHECK([ovs-appctl netdev-dummy/receive p1 $icmp_flow]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $tcp_flow1]) AT_CHECK([ovs-vsctl set mirror mymirror filter="tcp"], [0]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $tcp_flow2]) AT_CHECK([ovs-appctl dpif/dump-flows --names br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=1,frag=no), packets:0, bytes:0, used:never, actions:br0,p2 recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=6,frag=no), packets:1, bytes:118, used:0.0s, actions:p3,br0,p2 ]) AT_CHECK([ovs-appctl dpctl/dump-flows --names | strip_ufid | strip_used | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=1,frag=no), packets:0, bytes:0, used:never, actions:br0,p2 recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=6,frag=no), packets:1, bytes:118, used:0.0s, actions:p3,br0,p2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 actions=output:2"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=output:1"]) dnl Add mirrored flow after non-mirrored flow. AT_CHECK([ovs-vsctl set mirror mymirror filter="icmp"], [0]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $tcp_flow1]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 $icmp_flow]) AT_CHECK([ovs-appctl dpif/dump-flows --names br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=1,frag=no), packets:1, bytes:106, used:0.0s, actions:p3,p2 recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),dnl eth_type(0x0800),ipv4(proto=6,frag=no), packets:2, bytes:236, used:0.0s, actions:p2 ]) dnl Check one direction, only icmp should mirror. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl Check other direction, only icmp should mirror. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) dnl Change filter to tcp, only tcp should mirror. AT_CHECK([ovs-vsctl set mirror mymirror filter="tcp"], [0]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,1 ]) dnl Invalid filter. Nothing should mirror, error should be logged. AT_CHECK([ovs-vsctl set mirror mymirror filter="invalid"], [0]) dnl Setting an in_port is also invalid. AT_CHECK([ovs-vsctl set mirror mymirror filter="\"in_port=p1\""], [0]) dnl Each of the above two lines should produce two log messages. OVS_WAIT_UNTIL([test $(grep -Ec "filter is invalid|mirror mymirror configuration is invalid" ovs-vswitchd.log) -eq 4]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) dnl Check more complex filter cases with partially overlapping default wildcards. AT_CHECK([ovs-vsctl set mirror mymirror filter="\"tcp,tcp_dst=80\""], [0]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl Change port number. AT_CHECK([ovs-appctl dpif-dummy/change-port-number ovs-dummy p1 8]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(8),$tcp_flow2"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) dnl Empty filter, all traffic should mirror. AT_CHECK([ovs-vsctl clear mirror mymirror filter], [0]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(8),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(8),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,8 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow1"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,8 ]) OVS_VSWITCHD_STOP(["/filter is invalid: invalid: unknown field invalid/d /filter is invalid due to in_port field/d /mirror mymirror configuration is invalid/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_all]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_src]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p1 get Port p1 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_src_port=@p1 output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, metadata modification]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-vsctl set Bridge br0 mirrors=@m -- \ --id=@p3 get Port p3 -- \ --id=@m create Mirror name=mymirror select_all=true output_port=@p3], [0], [ignore]) AT_DATA([flows.txt], [dnl in_port=1 actions=load:0x00->NXM_OF_IN_PORT[[]],output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Metadata modified, duplicate packet shouldn't be delivered to mirror. m4_define([ICMP_FLOW], [m4_join([,], [in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800)], [ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)], [icmp(type=8,code=0)])]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "ICMP_FLOW"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, OFPP_NONE ingress port]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p2 AT_CHECK([ovs-ofctl add-flow br0 action=output:1]) # "in_port" defaults to OFPP_NONE if it's not specified. flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0" AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_dst]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_dst_port=@p2 output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2,3 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_vlan]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1, actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=11,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, output_port]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=mod_vlan_vid:17,output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,push_vlan(vid=17,pcp=0),2,3 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, output_vlan]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@m create Mirror name=mymirror select_all=true output_vlan=12 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=mod_vlan_vid:17,output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="2,push_vlan(vid=12,pcp=0),1,2,100" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="push_vlan(vid=12,pcp=0),100,2,1,pop_vlan,push_vlan(vid=17,pcp=0),1,pop_vlan,push_vlan(vid=12,pcp=0),100,2,1" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP # This verifies that we don't get duplicate mirroring when mirror_packet() # might be invoked recursively, as a check against regression. AT_SETUP([ofproto-dpif - multiple VLAN output mirrors]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ -- set Bridge br0 fail-mode=standalone mirrors=@m1,@m2 \ -- --id=@m1 create Mirror name=m1 select_all=true output_vlan=500 \ -- --id=@m2 create Mirror name=m2 select_all=true output_vlan=501 \ -- set Port br0 tag=0 \ -- set Port p1 tag=0 \ -- set Port p2 tag=500 \ -- set Port p3 tag=501 flow='in_port=1' AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout | sed 's/Datapath actions: // s/,/\ /g' | sort], [0], [100 2 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP # This test verifies that mirror state is preserved across recirculation. # # Otherwise, post-recirculation the ingress and the output to port 4 # would cause the packet to be mirrored to port 3 a second time. AT_SETUP([ofproto-dpif - mirroring with recirculation]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=2,debug_recirc,4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,2,recirc(0x1) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow,recirc_id(1)" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 4 ]) OVS_VSWITCHD_STOP AT_CLEANUP # Tests below verify the snaplen support for mirroring AT_SETUP([ofproto-dpif - mirroring, select_all with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,2 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_all with snaplen and reset snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,2 ]) ovs-vsctl set mirror mymirror snaplen=77 flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(77),3,1 ]) ovs-vsctl set mirror mymirror snaplen=65535 flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_src with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p1 get Port p1 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_src_port=@p1 output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,2 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, OFPP_NONE ingress port with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p2 snaplen=100 AT_CHECK([ovs-ofctl add-flow br0 action=output:1]) # "in_port" defaults to OFPP_NONE if it's not specified. flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0" AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1,trunc(100),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_dst with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_dst_port=@p2 output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2,trunc(100),3 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, select_vlan with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1, actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=11,pcp=0),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0))" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, output_port with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=mod_vlan_vid:17,output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,push_vlan(vid=17,pcp=0),2,trunc(100),3 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: trunc(100),3,1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring, output_vlan with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@m create Mirror name=mymirror select_all=true output_vlan=12 snaplen=100 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=mod_vlan_vid:17,output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` dnl Expect: "trunc(100),100,trunc(100),2,trunc(100),1", with different order AT_CHECK([echo "$actual" | sed -n 's/.*\(trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*\)/\1/p'], [0]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` AT_CHECK([echo "$actual" | sed -n 's/.*\(trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*\)/\1/p'], [0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - multiple VLAN output mirrors with snaplen]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 ovs-vsctl \ -- set Bridge br0 fail-mode=standalone mirrors=@m1,@m2 \ -- --id=@m1 create Mirror name=m1 select_all=true output_vlan=500 snaplen=200 \ -- --id=@m2 create Mirror name=m2 select_all=true output_vlan=501 snaplen=300 \ -- set Port br0 tag=0 \ -- set Port p1 tag=0 \ -- set Port p2 tag=500 \ -- set Port p3 tag=501 flow="in_port=1" AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout | grep -E "trunc\(200\),2,trunc\(300\),3,100|trunc\(300\),3,trunc\(200\),2,100"], [0], [stdout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - mirroring on balance-tcp bonding with dp_hash]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START( [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \ set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=standalone -- \ add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- \ set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 -- \ set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock ofport_request=4 ]) # Waits for all ifaces enabled. OVS_WAIT_UNTIL([test $(ovs-appctl bond/show | grep -c "may_enable: true") -ge 4]) add_of_ports br1 5 6 AT_CHECK([ovs-vsctl \ set Bridge br1 mirrors=@m --\ --id=@bond1 get Port bond1 -- --id=@p6 get Port p6 --\ --id=@m create Mirror name=mymirror \ select_dst_port=@bond1 select_src_port=@bond1 output_port=@p6], [0], [ignore]) # Sends a packet to trigger recirculation. AT_CHECK([ovs-appctl netdev-dummy/receive p5 \ "in_port(5),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1)"]) AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep 'in_port(p5)' | \ sed 's/.*actions://' | grep -c p6 | tr -d '\n'], [0], [1]) OVS_VSWITCHD_STOP() AT_CLEANUP # This test verifies that the table ID is preserved across recirculation # when a resubmit action requires it (because the action is relative to # the current table rather than specifying a table). AT_SETUP([ofproto-dpif - resubmit with recirculation]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=2,resubmit(,1) table=1 in_port=1 actions=debug_recirc,resubmit:55 table=1 in_port=55 actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2,recirc(0x1) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow,recirc_id(1)" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP # This test verifies that tunnel metadata is preserved across # recirculation. At the time of recirculation, fields such as "tun_id" # may be set before the tunnel is "valid" (ie, has a destination # address), but the field should still be available after recirculation. AT_SETUP([ofproto-dpif - resubmit with tun_id]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=2,load:6->NXM_NX_TUN_ID[[]],resubmit(,1) table=1 in_port=1 actions=debug_recirc,resubmit:55 table=1 in_port=55 actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) AT_CHECK([grep "Final flow:" stdout], [0], [Final flow: icmp,tun_id=0x6,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow,recirc_id(1)" -generate], [0], [stdout]) AT_CHECK([grep "Final flow:" stdout], [0], [Final flow: recirc_id=0x1,eth,icmp,tun_id=0x6,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0 ]) OVS_VSWITCHD_STOP AT_CLEANUP # This test verifies that "resubmit", when it triggers recirculation # indirectly through the flow that it recursively invokes, is not # re-executed when execution continues later post-recirculation. AT_SETUP([ofproto-dpif - recirculation after resubmit]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=resubmit(,1),2 table=1 in_port=1 actions=debug_recirc ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: recirc(0x1) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow,recirc_id(1)" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP # Checks for regression against a bug in which OVS dropped packets # with in_port=CONTROLLER when they were recirculated (because # CONTROLLER isn't a real port and could not be looked up). AT_SETUP([ofproto-dpif - packet-out recirculation]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 ip actions=mod_dl_dst:83:83:83:83:83:83,ct(table=1) table=1 ip actions=ct(commit),output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) packet=ffffffffffff00102030405008004500001c00000000401100000a000002ffffffff0035111100080000 AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=$packet actions=table"]) # Dumps out the flow table, extracts the number of packets that have gone # through the (single) flow in table 1, and returns success if it's exactly 1. # # If this remains 0, then the recirculation isn't working properly since the # packet never goes through flow in table 1. check_flows () { n=$(ovs-ofctl dump-flows br0 table=1 | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p') echo "n_packets=$n" test "$n" = 1 } OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0]) OVS_VSWITCHD_STOP AT_CLEANUP # Checks for regression against a bug in which OVS crashed # with in_port=OFPP_NONE or in_port=OFPP_CONTROLLER and # recirculation is involved. AT_SETUP([ofproto-dpif - packet-out recirculation with OFPP_NONE and OFPP_CONTROLLER]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 ip actions=mod_dl_dst:83:83:83:83:83:83,ct(table=1) table=1 ip actions=ct(commit),normal ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) packet=ffffffffffff00102030405008004500001c00000000401100000a000002ffffffff0035111100080000 AT_CHECK([ovs-ofctl packet-out br0 "in_port=none,packet=$packet actions=table"]) AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller,packet=$packet actions=table"]) # Dumps out the flow table, extracts the number of packets that have gone # through the (single) flow in table 1, and returns success if it's exactly 2. check_flows () { n=$(ovs-ofctl dump-flows br0 table=1 | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p') echo "n_packets=$n" test "$n" = 2 } OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Checks for regression against a bug in which OVS dropped packets dnl originating from a controller passing through a patch port. AT_SETUP([ofproto-dpif - packet-out recirculation OFPP_CONTROLLER and patch port]) OVS_VSWITCHD_START( [add-port br0 patch-br1 -- \ set interface patch-br1 type=patch options:peer=patch-br0 -- \ add-br br1 -- set bridge br1 datapath-type=dummy fail-mode=secure -- \ add-port br1 patch-br0 -- set interface patch-br0 type=patch options:peer=patch-br1 ]) add_of_ports --pcap br1 1 AT_DATA([flows-br0.txt], [dnl table=0 icmp actions=output:patch-br1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows-br0.txt]) AT_DATA([flows-br1.txt], [dnl table=0, icmp actions=ct(table=1,zone=1) table=1, ct_state=+trk, icmp actions=p1 ]) AT_CHECK([ovs-ofctl add-flows br1 flows-br1.txt]) packet=50540000000750540000000508004500005c000000008001b94dc0a80001c0a80002080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f AT_CHECK([ovs-ofctl packet-out br0 "in_port=CONTROLLER packet=$packet actions=table"]) OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows -m br1 | grep "ct_state" | ofctl_strip], [dnl table=1, n_packets=1, n_bytes=106, ct_state=+trk,icmp actions=output:2]) OVS_WAIT_UNTIL([ovs-pcap p1-tx.pcap | grep -q "$packet"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - debug_slow action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-ofctl add-flow br0 "in_port=1,actions=debug_slow,2"]) flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow" -generate], [0], [stdout]) AT_CHECK_UNQUOTED([tail -3 stdout], [0], [Datapath actions: 2 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - meter with slow-path action]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 add-meter br0 \ 'meter=1 pktps burst stats bands=type=drop rate=100 burst_size=100']) add_of_ports --pcap br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif:dbg]) eth="eth_src=00:00:00:00:00:ec,eth_dst=ff:ff:ff:ff:ff:ff,arp" arp="arp_tpa=192.168.0.1,arp_spa=192.168.0.100,arp_op=1" packet=$(ovs-ofctl compose-packet --bare "$eth,$arp") AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 \ "table=1,eth_type=0x0806,actions=meter:1,clone(set_field:2->arp_op,output(port=p2,max_len=128)),output:p1"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 CONTROLLER \ "meter:1,resubmit(,1)" "$packet"]) dnl Verify the orinigal packet is received on port 1. OVS_WAIT_UNTIL([test $(ovs-pcap p1-tx.pcap | grep -c "$packet") -eq 1]) dnl Verify the modified packet is received on port 2. arp="arp_tpa=192.168.0.1,arp_spa=192.168.0.100,arp_op=2" packet=$(ovs-ofctl compose-packet --bare "$eth,$arp") OVS_WAIT_UNTIL([test $(ovs-pcap p2-tx.pcap | grep -c "$packet") -eq 1]) dnl Make sure all meters, and outputs datapath actions get executed. OVS_WAIT_UNTIL([grep "sub-execute meter(0),meter(0),2 on packet arp" \ ovs-vswitchd.log]) OVS_WAIT_UNTIL([grep "sub-execute 1 on packet arp" ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP dnl CHECK_CONTINUATION(TITLE, N_PORTS0, N_PORTS1, ACTIONS0, ACTIONS1, [EXTRA_SETUP]) dnl dnl Checks the implementation of the continuation mechanism that allows the dnl packet processing pipeline to be paused and resumed. Starts by creating dnl bridge br0 with N_PORTS0 ports numbered 1 through N_PORTS0, and adds the dnl flows listed in ACTIONS0 to that bridge. Then, injects a packet at port 1 dnl in the bridge, resuming each time the pipeline pauses, and expects a single dnl packet to be output at each port 2 through N_PORTS0. Then, as long as dnl ACTIONS0 still contains at least one "pause" action, removes one of them dnl and repeats the process. dnl dnl If N_PORTS1 is nonzero, also creates a bridge br1 and adds ports numbered dnl N_PORTS0 + 1 to N_PORTS0 + N_PORTS1 to it, as well as flows ACTIONS1. dnl ACTIONS1 may also contain "pause" actions. Packets are only ever injected dnl into port 1 on br0, so br1 only comes into action if a patch port (added dnl by EXTRA_SETUP) jumps from one bridge to another. dnl dnl EXTRA_SETUP is an optional list of extra commands to run after setting up dnl both bridges, e.g. to configure mirrors or patch ports. m4_define([CHECK_CONTINUATION], [dnl m4_foreach([monitor_version], [[OpenFlow10], [OpenFlow13]], [dnl AT_SETUP([ofproto-dpif - continuation - $1 - monitor_version]) AT_KEYWORDS([continuations pause resume]) OVS_VSWITCHD_START # count_matches STRING # # Prints on stdout the number of occurrences of STRING in stdin. count_matches () { sed -n ":start s/$[1]//p t start" | wc -l } add_of_ports --pcap br0 `seq 1 $2` m4_if([$3], [0], [], [add_of_br 1 add_of_ports --pcap br1 `seq m4_eval([$2 + 1]) m4_eval([$2 + $3])`]) AT_CAPTURE_FILE([ofctl_monitor0.log]) AT_CHECK([ovs-ofctl -O monitor_version monitor br0 resume --detach --no-chdir --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log]) m4_if([$3], [0], [], [AT_CAPTURE_FILE([ofctl_monitor1.log]) AT_CHECK([ovs-ofctl -O monitor_version monitor br1 resume --detach --no-chdir --pidfile=ovs-ofctl1.pid 2> ofctl_monitor1.log])]) actions0='$4' actions1='$5' $6 flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" n_packets=0 n_resumes=0 while true; do printf "\n\nactions for br0:\n%s\n" "$actions0" m4_if([$3], [0], [], [printf "actions for br1:\n%s\n" "$actions1"]) # Add flows. AT_CHECK([echo "$actions0" | sed 's/pause/controller(pause)/g' | ovs-ofctl -O OpenFlow13 add-flows br0 -]) m4_if([$3], [0], [], [AT_CHECK([echo "$actions1" | sed 's/pause/controller(pause)/g' | ovs-ofctl -O OpenFlow13 add-flows br1 -])]) # Make sure the datapath is up-to-date before sending the packet. ovs-appctl revalidator/wait # Run a packet through the switch. AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) # Wait for the expected number of packets to show up. n_packets=$(expr $n_packets + 1) nports=m4_eval([$2 + $3]) echo "waiting for $n_packets packets on p2 through p$nports..." OVS_WAIT_UNTIL([ ok=true for i in $(seq 2 $nports); do n=$(ovs-ofctl parse-pcap p$i-tx.pcap | wc -l) printf "%d " $n if test $n_packets != $n; then ok=false; fi done echo $ok ]) # Wait for the expected number of NXT_RESUMEs to be logged. n_resumes=$(expr $n_resumes + $(echo "$actions0 $actions1" | count_matches pause) ) echo "waiting for $n_resumes NXT_RESUMEs..." OVS_WAIT_UNTIL([test $n_resumes = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) # Eliminate one "pause" from the actions. # # If there were none left, then we're done. next_actions0=`echo "$actions0" | sed '1,/pause/s/pause//'` if test X"$actions0" = X"$next_actions0"; then next_actions1=`echo "$actions1" | sed '1,/pause/s/pause//'` if test X"$actions1" = X"$next_actions1"; then break else actions1=$next_actions1 fi else actions0=$next_actions0 fi # Delete all the flows and verify that there are none, so that we # can be sure that our updated flow tables is actually in use # later. AT_CHECK([ovs-ofctl del-flows br0 && ovs-ofctl dump-flows br0 | strip_xids], [0], [NXST_FLOW reply: ]) m4_if([$3], [0], [], [AT_CHECK([ovs-ofctl del-flows br1 && ovs-ofctl dump-flows br1 | strip_xids], [0], [NXST_FLOW reply: ])]) done OVS_VSWITCHD_STOP AT_CLEANUP ]) ]) # Check that pause at the end of the pipeline works OK. # # (xlate_continuation() has a special case for no-op actions; this # fails without that special case.) CHECK_CONTINUATION([pause at end of pipeline], [2], [0], [actions=2 pause]) # Check that remaining actions are preserved following resume. CHECK_CONTINUATION([actions], [7], [0], [in_port=1 actions=pause 2 pause 3 pause 4 pause 5 pause 6 pause 7]) # Check that multiple levels of resubmit continue following resume. # # The "resubmit:55", which is relative to the current table, is # particularly interesting because it checks that the notion of the # current table is correctly preserved. CHECK_CONTINUATION([resubmit], [10], [0], [table=0 in_port=1 actions=pause 2 pause resubmit(,1) pause 10 pause table=1 in_port=1 actions=pause 3 pause resubmit(,2) pause 9 pause table=2 in_port=1 actions=pause 4 pause resubmit(,3) pause 8 pause table=3 in_port=1 actions=pause 5 pause resubmit:55 pause 7 pause table=3 in_port=55 actions=pause 6 pause]) # Check that the action set is preserved across pause/resume. CHECK_CONTINUATION([action set], [3], [0], [in_port=1 actions=1 pause resubmit(,1) pause 2 table=1 actions=write_actions(3)]) # Check that goto_table instructions following pause work as expected. CHECK_CONTINUATION([goto_table], [6], [0], [table=0 in_port=1 actions=pause 2 pause goto_table:1 table=1 in_port=1 actions=pause 3 pause goto_table:2 table=2 in_port=1 actions=pause 4 pause goto_table:3 table=3 in_port=1 actions=pause 5 pause goto_table:4 table=4 in_port=1 actions=pause 6 pause]) # Check that write_metadata instructions following pause work as expected. CHECK_CONTINUATION([write_metadata], [6], [0], [table=0 in_port=1 actions=pause 2 pause write_metadata:1/1 goto_table:1 table=1 in_port=1 metadata=1 actions=pause 3 pause write_metadata:2/2 goto_table:2 table=2 in_port=1 metadata=3 actions=pause 4 pause write_metadata:4/4 goto_table:3 table=3 in_port=1 metadata=7 actions=pause 5 pause write_metadata:8/8 goto_table:4 table=4 in_port=1 metadata=15 actions=pause 6 pause]) # Check that metadata and the stack used by push and pop is preserved # across pause/resume. CHECK_CONTINUATION([data stack], [3], [0], [in_port=1 actions=pause dnl set_field:1->reg0 dnl pause dnl set_field:2->reg1 dnl pause dnl output:NXM_NX_REG0[[]] dnl pause dnl push:NXM_NX_REG1[[]] dnl dnl pop:NXM_NX_REG2[[]] dnl pause dnl output:NXM_NX_REG2[[]] dnl pause dnl 3]) # Check that mirror output occurs once and once only, even if # separated by pause/resume. CHECK_CONTINUATION([mirroring], [5], [0], [in_port=1 actions=pause 2 pause 3 pause 4 pause], [], [ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 --\ --id=@p3 get Port p3 --\ --id=@p4 get Port p4 --\ --id=@p5 get Port p5 --\ --id=@m create Mirror name=mymirror select_dst_port=@p2,@p3,@p4 output_port=@p5]) # Check that pause works in the presence of patch ports. CHECK_CONTINUATION([patch ports], [4], [1], [table=0 in_port=1 actions=pause 2 resubmit(,1) pause 4 table=1 in_port=1 actions=pause 3 pause 10 pause], [table=0 in_port=11 actions=pause 5 pause], [ovs-vsctl \ -- add-port br0 patch10 \ -- set interface patch10 type=patch options:peer=patch11 \ ofport_request=10 \ -- add-port br1 patch11 \ -- set interface patch11 type=patch options:peer=patch10 \ ofport_request=11]) AT_SETUP([ofproto-dpif - continuation flow stats]) AT_KEYWORDS([continuations pause resume]) OVS_VSWITCHD_START add_of_ports --pcap br0 `seq 1 2` flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=set_field:1->reg1 controller(pause) resubmit(,2) table=2 reg1=0x1 actions=2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor0.log]) ovs-ofctl monitor br0 resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log # Run a packet through the switch. AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) # Check flow stats AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | grep 'table=2'], [0], [dnl table=2, n_packets=1, n_bytes=106, reg1=0x1 actions=output:2 ]) # The packet should be received by port 2 AT_CHECK([test 1 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p2-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - continuation with conntrack]) AT_KEYWORDS([continuations pause resume]) OVS_VSWITCHD_START add_of_ports --pcap br0 `seq 1 2` flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_DATA([flows.txt], [dnl table=0, in_port=1 icmp action=ct(table=1) table=1, icmp action=controller(pause) resubmit(,2) table=2, in_port=1 icmp ct_state=+trk+new action=output:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor0.log]) ovs-ofctl monitor br0 resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log # Run a packet through the switch. AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) # Check flow stats AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | grep 'table=2'], [0], [dnl table=2, n_packets=1, n_bytes=106, ct_state=+new+trk,icmp,in_port=1 actions=output:2 ]) # The packet should be received by port 2 AT_CHECK([test 1 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p2-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - continuation with meters]) AT_KEYWORDS([continuations pause meters]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Add meter with id=1. AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1']) AT_DATA([flows.txt], [dnl table=0 dl_dst=50:54:00:00:00:0a actions=goto_table(1) table=1 dl_dst=50:54:00:00:00:0a actions=controller(pause,meter_id=1) ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) on_exit 'kill $(cat ovs-ofctl.pid)' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in \ --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)']) OVS_WAIT_UNTIL([test $(wc -l < ofctl_monitor.log) -ge 2]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=1, n_bytes=14, dl_dst=50:54:00:00:00:0a actions=goto_table:1 table=1, n_packets=1, n_bytes=14, dl_dst=50:54:00:00:00:0a actions=controller(pause,meter_id=1) OFPST_FLOW reply (OF1.3): ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0 | ofctl_strip | sort], [0], [dnl OFPST_METER_CONFIG reply (OF1.3): meter=1 pktps bands= type=drop rate=1 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:0 packet_in_count:1 byte_in_count:14 duration:0.0s bands: 0: packet_count:0 byte_count:0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - continuation with patch port]) AT_KEYWORDS([continuations pause resume]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy -- \ add-port br0 patch- -- \ set interface patch- type=patch options:peer=patch+ -- \ add-br br1 -- set bridge br1 datapath-type=dummy fail-mode=secure -- \ add-port br1 patch+ -- set interface patch+ type=patch options:peer=patch- ]) add_of_ports --pcap br1 1 flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_DATA([flows.txt], [dnl table=0, in_port=patch+ icmp action=controller(pause), resubmit(,1) table=1, in_port=patch+ icmp action=ct(table=2) table=2, in_port=patch+ icmp ct_state=+trk+new action=ct(commit, table=3) table=3, in_port=patch+ icmp action=p1 ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br1 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) ovs-ofctl monitor br1 resume --detach --no-chdir --pidfile=ovs-ofctl.pid 2> ofctl_monitor.log # Run a packet through the switch. AT_CHECK([ovs-appctl netdev-dummy/receive p0 "$flow"], [0], [stdout]) # Check flow stats AT_CHECK([ovs-ofctl dump-flows br1 | ofctl_strip | grep 'table=3' | grep -v 'commit'], [0], [dnl table=3, n_packets=1, n_bytes=106, icmp,in_port=1 actions=output:2 ]) # The packet should be received by port 1 AT_CHECK([test 1 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p1-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP # Check that pause works after the packet is cloned. AT_SETUP([ofproto-dpif - continuation after clone]) AT_KEYWORDS([continuations clone pause resume]) OVS_VSWITCHD_START add_of_ports --pcap br0 `seq 1 3` flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=set_field:1->reg1 resubmit(,2) table=1 reg1=0x5 actions=controller(pause) resubmit(,3) table=1 reg1=0x1 actions=2 table=2 in_port=1 actions=clone(set_field:5->reg1, resubmit(,1)) table=3 reg1=0x1 actions=3 table=3 reg1=0x5 actions=2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor0.log]) ovs-ofctl monitor br0 resume --detach --no-chdir \ --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log # Run a packet through the switch. AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) ovs-vsctl show ovs-ofctl dump-flows br0 # The packet should be received by port 2 and not port 3 AT_CHECK([test 1 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p2-tx.pcap | wc -l`]) AT_CHECK([test 0 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p3-tx.pcap | wc -l`]) # NXT_RESUMEs should be 1 and reg1 should be set to 0x5. OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep NXT_RESUME | grep -c reg1=0x5`]) OVS_VSWITCHD_STOP AT_CLEANUP # Two testcases below are for the ofproto/trace command # The first one tests all correct syntax: # ofproto/trace [dp_name] odp_flow [-generate|packet] # ofproto/trace br_name br_flow [-generate|packet] AT_SETUP([ofproto-dpif - ofproto/trace command 1]) OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) odp_flow="in_port(p1)" br_flow="in_port=1" # Test command: ofproto/trace odp_flow with in_port as a name. AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) odp_flow="in_port(1)" # Test command: ofproto/trace odp_flow AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) # Test command: ofproto/trace dp_name odp_flow AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$odp_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) # Test commmand: ofproto/trace br_name br_flow AT_CHECK([ovs-appctl ofproto/trace br0 "$br_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) # Delete the inserted flows AT_CHECK([ovs-ofctl del-flows br0 "in_port=1"], [0], [stdout]) AT_CHECK([ovs-ofctl del-flows br0 "in_port=2"], [0], [stdout]) # This section below tests the [-generate] option odp_flow="in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff)" br_flow="arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=ff:ff:ff:ff:ff:ff" # Test command: ofproto/trace odp_flow AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout]) # Check for no MAC learning entry AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age ]) # Test command: ofproto/trace br_name br_flow AT_CHECK([ovs-appctl ofproto/trace br0 "$br_flow"], [0], [stdout]) # Check for no MAC learning entry AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age ]) # Test command: ofproto/trace odp_flow -generate AT_CHECK([ovs-appctl ofproto/trace "$odp_flow" -generate], [0], [stdout]) # Check for the MAC learning entry AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 3 0 50:54:00:00:00:05 ? ]) # Test command: ofproto/trace dp_name odp_flow -generate AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05)" \ -generate], [0], [stdout]) # Check for both MAC learning entries AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 3 0 50:54:00:00:00:05 ? 1 0 50:54:00:00:00:06 ? ]) # Test command: ofproto/trace br_name br_flow -generate AT_CHECK([ovs-appctl ofproto/trace br0 \ "in_port=2,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:06" \ -generate], [0], [stdout]) # Check for both MAC learning entries. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 3 0 50:54:00:00:00:05 ? 1 0 50:54:00:00:00:06 ? 2 0 50:54:00:00:00:07 ? ]) # This section beflow tests the [packet] option # The ovs-tcpundump of packets between port1 and port2 pkt1to2="50540000000250540000000108064500001C000100004001F98CC0A80001C0A800020800F7FF00000000" pkt2to1="50540000000150540000000208064500001C000100004001F98CC0A80002C0A800010800F7FF00000000" # Construct the MAC learning table AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(1),eth(src=50:54:00:00:00:01,dst=ff:ff:ff:ff:ff:ff)" \ -generate], [0], [stdout]) # Construct the MAC learning table AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(2),eth(src=50:54:00:00:00:02,dst=ff:ff:ff:ff:ff:ff)" \ -generate], [0], [stdout]) # Test command: ofproto/trace odp_flow packet AT_CHECK([ovs-appctl ofproto/trace \ "in_port(1),skb_priority(1),skb_mark(2)" "$pkt1to2"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) AT_CHECK([head -n 3 stdout], [0], [dnl Flow: pkt_mark=0x2,skb_priority=0x1,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00 bridge("br0") ]) # Test command: ofproto/trace dp_name odp_flow packet AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(1),skb_priority(1),skb_mark(2)" "$pkt1to2"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) AT_CHECK([head -n 3 stdout], [0], [dnl Flow: pkt_mark=0x2,skb_priority=0x1,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00 bridge("br0") ]) # Test command: ofproto/trace br_name br_flow packet AT_CHECK([ovs-appctl ofproto/trace br0 \ "in_port=2,skb_priority=2,pkt_mark=1" "$pkt2to1"], [0], [stdout],[stderr]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 1 ]) AT_CHECK([head -n 3 stdout], [0], [dnl Flow: pkt_mark=0x1,skb_priority=0x2,arp,in_port=2,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00 bridge("br0") ]) # Test command: ofproto/trace br_name br_flow packet --names AT_CHECK([ovs-appctl ofproto/trace br0 \ "in_port=2,skb_priority=2,pkt_mark=1" "$pkt2to1" --names], [0], [stdout],[stderr]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: p1 ]) AT_CHECK([head -n 3 stdout], [0], [dnl Flow: pkt_mark=0x1,skb_priority=0x2,arp,in_port=p2,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00 bridge("br0") ]) OVS_VSWITCHD_STOP AT_CLEANUP # The second test tests the corner cases AT_SETUP([ofproto-dpif - ofproto/trace command 2]) OVS_VSWITCHD_START add_of_ports br0 1 2 # Define flows odp_flow="in_port(1),eth(src=50:54:00:00:00:01,dst=50:54:00:00:00:02)" br_flow="in_port=1,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02" # Define options generate="-generate" pkt="50540000000250540000000108064500001C000100004001F98CC0A80001C0A800020800F7FF00000000" # Test incorrect command: ofproto/trace wrong_name odp_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace wrong_name "$odp_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Cannot find the datapath ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace empty_string odp_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace "" "$odp_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Cannot find the datapath ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace nonexist_dp_name odp_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace ovs-system "$odp_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Cannot find the datapath ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace br_name odp_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace br0 "$odp_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Cannot find the datapath ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace dp_name br_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$br_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl ovs-dummy: unknown bridge ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace br_flow [-generate|packet] m4_foreach( [option], [[], ["$generate"], ["$pkt"]], [AT_CHECK([ovs-appctl ofproto/trace "$br_flow" option], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl syntax error at in_port=1,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02 (or the bridge name was omitted) ovs-appctl: ovs-vswitchd: server returned an error ])]) # Test incorrect command: ofproto/trace dp_name odp_flow garbage_option AT_CHECK([ovs-appctl ofproto/trace \ ovs-dummy "$odp_flow" garbage_option], [2], [stdout],[stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Trailing garbage in packet data ovs-appctl: ovs-vswitchd: server returned an error ]) # Test incorrect command: ofproto/trace with 0 argument AT_CHECK([ovs-appctl ofproto/trace ], [2], [stdout],[stderr]) AT_CHECK([tail -2 stderr], [0], [dnl "ofproto/trace" command requires at least 1 arguments ovs-appctl: ovs-vswitchd: server returned an error ]) # Test incorrect command: ofproto/trace with nonexistent port number AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(42)" ], [2], [stdout], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl no OpenFlow port for datapath port 42 ovs-appctl: ovs-vswitchd: server returned an error ]) # Test incorrect command: ofproto/trace with nonexistent recirc_id AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "recirc_id(0x42)" ], [2], [stdout], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl no recirculation data for recirc_id 0x42 ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP AT_CLEANUP # The third test checks that the output of "ovs-dpctl -m" is valid to trace. AT_SETUP([ofproto-dpif - ofproto/trace from dpctl output]) OVS_VSWITCHD_START([dnl set Open_vSwitch . other_config:max-idle=10000 \ -- add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl revalidator/wait AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | sed 's/, packets.*$//' > dp_flows1.txt]) odp_flow=`cat dp_flows1.txt` AT_CHECK([ovs-appctl ofproto/trace "$odp_flow" | sed 's/\([[Ff]]low:\).*/\1 /'], [0], [dnl Flow: bridge("br0") ------------- 0. No match. drop Final flow: Megaflow: Datapath actions: drop ]) dnl Now, try again without megaflows: ovs-appctl upcall/disable-megaflows AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl revalidator/wait AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | sed 's/, packets.*$//' > dp_flows2.txt]) odp_flow=`cat dp_flows2.txt` AT_CHECK([ovs-appctl ofproto/trace "$odp_flow" | sed 's/\([[Ff]]low:\).*/\1 /'], [0], [dnl Flow: bridge("br0") ------------- 0. No match. drop Final flow: Megaflow: Datapath actions: drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ofproto/trace-packet-out]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace-packet-out br0 in_port=1 'mod_vlan_vid:123,resubmit(,0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: push_vlan(vid=123,pcp=0),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([OFPROTO_TRACE], [flow="$2" AT_CHECK([ovs-appctl ofproto/trace $1 "$flow" $3], [0], [stdout]) actual=`tail -1 stdout | sed 's/Datapath actions: //'` expected="$4" AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected" $5], [0], [stdout]) mv stdout expout AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual" $5], [0], [expout])]) AT_SETUP([ofproto-dpif - MAC learning]) OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 3 arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' # Trace an ARP packet arriving on p3, to create a MAC learning entry. OFPROTO_TRACE( [ovs-dummy], [in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [1,2,100]) # Check for the MAC learning entry. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 3 0 50:54:00:00:00:05 ? ]) # Trace a packet arrival destined for the learned MAC. # (This will also learn a MAC.) OFPROTO_TRACE( [ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),$arp], [-generate], [3]) # Check for both MAC learning entries. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 3 0 50:54:00:00:00:05 ? 1 0 50:54:00:00:00:06 ? ]) dnl Check json output. AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 \ | sed 's/"age": [[0-9]]*/"age": ?/g'], [0], [dnl [[ { "age": ?, "mac": "50:54:00:00:00:05", "port": 3, "vlan": 0}, { "age": ?, "mac": "50:54:00:00:00:06", "port": 1, "vlan": 0}]] ]) # Trace a packet arrival that updates the first learned MAC entry. OFPROTO_TRACE( [ovs-dummy], [in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [1,3,100]) # Check that the MAC learning entry was updated. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 1 0 50:54:00:00:00:06 ? 2 0 50:54:00:00:00:05 ? ]) # Add another bridge. AT_CHECK( [ovs-vsctl \ -- add-br br1 \ -- set bridge br1 datapath-type=dummy]) add_of_ports br1 4 5 # Trace some packet arrivals in br1 to create MAC learning entries there too. OFPROTO_TRACE( [ovs-dummy], [in_port(4),eth(src=50:54:00:00:00:06,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [5,101]) OFPROTO_TRACE( [ovs-dummy], [in_port(5),eth(src=50:54:00:00:00:07,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [4,101]) # Check that the MAC learning entries were added. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 4 0 50:54:00:00:00:06 ? 5 0 50:54:00:00:00:07 ? ]) # Delete port p1 and see that its MAC learning entry disappeared, and # that the MAC learning entry for the same MAC was also deleted from br1. AT_CHECK([ovs-vsctl del-port p1]) AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 2 0 50:54:00:00:00:05 ? ]) AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [dnl port VLAN MAC Age 5 0 50:54:00:00:00:07 ? ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MAC table overflow]) OVS_VSWITCHD_START( [set bridge br0 fail-mode=standalone other-config:mac-table-size=10]) add_of_ports br0 1 2 3 arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' AT_CHECK([ovs-appctl time/stop]) # Trace 10 ARP packets arriving on p3, to create MAC learning entries. for i in 0 1 2 3 4 5 6 7 8 9; do OFPROTO_TRACE( [ovs-dummy], [in_port(3),eth(src=50:54:00:00:00:0$i,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [1,2,100]) ovs-appctl time/warp 1000 done # Check for the MAC learning entries. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort], [0], [dnl 3 0 50:54:00:00:00:00 3 0 50:54:00:00:00:01 3 0 50:54:00:00:00:02 3 0 50:54:00:00:00:03 3 0 50:54:00:00:00:04 3 0 50:54:00:00:00:05 3 0 50:54:00:00:00:06 3 0 50:54:00:00:00:07 3 0 50:54:00:00:00:08 3 0 50:54:00:00:00:09 port VLAN MAC Age ]) # Trace another ARP packet on another MAC. OFPROTO_TRACE( [ovs-dummy], [in_port(3),eth(src=50:54:00:00:00:10,dst=ff:ff:ff:ff:ff:ff),$arp], [-generate], [1,2,100]) # Check that the new one chased the oldest one out of the table. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/' | sort], [0], [dnl 3 0 50:54:00:00:00:01 ? 3 0 50:54:00:00:00:02 ? 3 0 50:54:00:00:00:03 ? 3 0 50:54:00:00:00:04 ? 3 0 50:54:00:00:00:05 ? 3 0 50:54:00:00:00:06 ? 3 0 50:54:00:00:00:07 ? 3 0 50:54:00:00:00:08 ? 3 0 50:54:00:00:00:09 ? 3 0 50:54:00:00:00:10 ? port VLAN MAC Age ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MAC table overflow fairness]) OVS_VSWITCHD_START( [set bridge br0 fail-mode=standalone other-config:mac-table-size=10]) add_of_ports br0 1 2 3 4 5 6 arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' AT_CHECK([ovs-appctl time/stop]) # Trace packets with 2 different source MACs arriving on each of the 5 # ports, filling up the 10-entry learning table. for i in 0 1 2 3 4 5 6 7 8 9; do p=`expr $i / 2 + 1` ovs-appctl ofproto/trace ovs-dummy "in_port($p),eth(src=50:54:00:00:00:0$i,dst=ff:ff:ff:ff:ff:ff),$arp" -generate ovs-appctl time/warp 1000 done # Check for the MAC learning entries. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort], [0], [dnl 1 0 50:54:00:00:00:00 1 0 50:54:00:00:00:01 2 0 50:54:00:00:00:02 2 0 50:54:00:00:00:03 3 0 50:54:00:00:00:04 3 0 50:54:00:00:00:05 4 0 50:54:00:00:00:06 4 0 50:54:00:00:00:07 5 0 50:54:00:00:00:08 5 0 50:54:00:00:00:09 port VLAN MAC Age ]) # Now trace 16 new MACs on another port. for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do ovs-appctl ofproto/trace ovs-dummy "in_port(6),eth(src=50:54:00:00:0$i:ff,dst=ff:ff:ff:ff:ff:ff),$arp" -generate ovs-appctl time/warp 1000 done # Check the results. # # Our eviction algorithm on overflow is that an arbitrary (but deterministic) # one of the ports with the most learned MACs loses the least recently used # one. Thus, the new port will end up with 3 MACs, 3 of the old ports with 1 # MAC each, and the other 2 of the old ports with 2 MACs each. # # (If someone changes lib/heap.c to do something different with equal-priority # nodes, then the output below could change, but it would still follow the # rules explained above.) AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort], [0], [dnl 1 0 50:54:00:00:00:01 2 0 50:54:00:00:00:03 3 0 50:54:00:00:00:04 3 0 50:54:00:00:00:05 4 0 50:54:00:00:00:07 5 0 50:54:00:00:00:08 5 0 50:54:00:00:00:09 6 0 50:54:00:00:0d:ff 6 0 50:54:00:00:0e:ff 6 0 50:54:00:00:0f:ff port VLAN MAC Age ]) OVS_VSWITCHD_STOP AT_CLEANUP # CHECK_SFLOW_SAMPLING_PACKET(LOOPBACK_ADDR) # # Test that sFlow samples packets correctly using IPv4/IPv6 sFlow collector m4_define([CHECK_SFLOW_SAMPLING_PACKET], [AT_XFAIL_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) on_exit 'kill `cat test-sflow.pid`' AT_CHECK([ovstest test-sflow --log-file --detach --no-chdir --pidfile 0:$1 > sflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([sflow.log]) PARSE_LISTENING_PORT([test-sflow.log], [SFLOW_PORT]) ovs-appctl time/stop add_of_ports br0 1 2 ovs-vsctl \ set Interface br0 options:ifindex=1002 -- \ set Interface p1 options:ifindex=1004 -- \ set Interface p2 options:ifindex=1003 -- \ set Bridge br0 sflow=@sf -- \ --id=@sf create sflow targets=\"$1:$SFLOW_PORT\" \ header=128 sampling=1 polling=1 agent=$LOOPBACK_INTERFACE dnl open with ARP packets to seed the bridge-learning. The output dnl ifIndex numbers should be reported predictably after that. dnl Since we set sampling=1 we should see all of these packets dnl reported. Sorting the output by data-source and seqNo makes dnl it deterministic. Ensuring that we send at least two packets dnl into each port means we get to check the seq nos are dnl incrementing correctly. dnl because packets from different ports can be handled by separate dnl threads, put some sleeps ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' sleep 1 ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)' sleep 1 ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' sleep 1 ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)' ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)' dnl sleep long enough to get more than one counter sample dnl from each datasource so we can check sequence numbers ovs-appctl time/warp 2000 100 OVS_VSWITCHD_STOP OVS_APP_EXIT_AND_WAIT([test-sflow]) AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\ /g' | sed 's/\(.*\)ds=\[::1\]\(.*\)/\1ds=127.0.0.1\2/g' ]], [0], [dnl HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=1 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=1 dropEvents=0 in_ifindex=1004 in_format=0 out_ifindex=2 out_format=2 hdr_prot=1 pkt_len=46 stripped=4 hdr_len=42 hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01 HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=2 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=2 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=2 out_format=2 hdr_prot=1 pkt_len=46 stripped=4 hdr_len=42 hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02 HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=3 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=3 dropEvents=0 in_ifindex=1004 in_format=0 out_ifindex=1003 out_format=0 hdr_prot=1 pkt_len=110 stripped=4 hdr_len=106 hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-5C-00-00-00-00-40-01-F9-4D-C0-A8-00-01-C0-A8-00-02-08-00-13-FC-00-00-00-00-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F-20-21-22-23-24-25-26-27-28-29-2A-2B-2C-2D-2E-2F-30-31-32-33-34-35-36-37-38-39-3A-3B-3C-3D-3E-3F HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=4 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=4 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=1004 out_format=0 hdr_prot=1 pkt_len=110 stripped=4 hdr_len=106 hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-5C-00-00-00-00-40-01-F9-4D-C0-A8-00-02-C0-A8-00-01-00-00-1B-FC-00-00-00-00-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F-20-21-22-23-24-25-26-27-28-29-2A-2B-2C-2D-2E-2F-30-31-32-33-34-35-36-37-38-39-3A-3B-3C-3D-3E-3F HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=5 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=5 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=1004 out_format=0 hdr_prot=1 pkt_len=58 stripped=4 hdr_len=54 hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-67-00-00-00-00-00-0A-80-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-01-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-02 ]) AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'ETHCOUNTERS|IFCOUNTERS|ERROR|PORTNAME|OPENFLOWPORT' | head -24 | sed 's/ /\ /g' | sed 's/\(.*\)ds=\[::1\]\(.*\)/\1ds=127.0.0.1\2/g' ]], [0], [dnl ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 ETHCOUNTERS dot3StatsAlignmentErrors=4294967295 dot3StatsFCSErrors=4294967295 dot3StatsSingleCollisionFrames=4294967295 dot3StatsMultipleCollisionFrames=4294967295 dot3StatsSQETestErrors=4294967295 dot3StatsDeferredTransmissions=4294967295 dot3StatsLateCollisions=4294967295 dot3StatsExcessiveCollisions=4294967295 dot3StatsInternalMacTransmitErrors=4294967295 dot3StatsCarrierSenseErrors=4294967295 dot3StatsFrameTooLongs=4294967295 dot3StatsInternalMacReceiveErrors=4294967295 dot3StatsSymbolErrors=4294967295 IFCOUNTERS dgramSeqNo=1 ds=127.0.0.1>0:1003 csSeqNo=1 ifindex=1003 type=6 ifspeed=100000000 direction=0 status=3 in_octets=202 in_unicasts=3 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=148 out_unicasts=2 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 IFCOUNTERS dgramSeqNo=1 ds=127.0.0.1>0:1004 csSeqNo=1 ifindex=1004 type=6 ifspeed=100000000 direction=0 status=3 in_octets=148 in_unicasts=2 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=202 out_unicasts=3 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 IFCOUNTERS dgramSeqNo=2 ds=127.0.0.1>0:1002 csSeqNo=1 ifindex=1002 type=6 ifspeed=100000000 direction=0 status=3 in_octets=0 in_unicasts=0 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=84 out_unicasts=2 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1002 csSeqNo=2 ifindex=1002 type=6 ifspeed=100000000 direction=0 status=3 in_octets=0 in_unicasts=0 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=84 out_unicasts=2 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1003 csSeqNo=2 ifindex=1003 type=6 ifspeed=100000000 direction=0 status=3 in_octets=202 in_unicasts=3 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=148 out_unicasts=2 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1004 csSeqNo=2 ifindex=1004 type=6 ifspeed=100000000 direction=0 status=3 in_octets=148 in_unicasts=2 in_multicasts=4294967295 in_broadcasts=4294967295 in_discards=4294967295 in_errors=4294967295 in_unknownprotos=4294967295 out_octets=202 out_unicasts=3 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=4294967295 out_errors=4294967295 promiscuous=0 OPENFLOWPORT datapath_id=18364758544493064720 port_no=1 OPENFLOWPORT datapath_id=18364758544493064720 port_no=1 OPENFLOWPORT datapath_id=18364758544493064720 port_no=2 OPENFLOWPORT datapath_id=18364758544493064720 port_no=2 OPENFLOWPORT datapath_id=18364758544493064720 port_no=65534 OPENFLOWPORT datapath_id=18364758544493064720 port_no=65534 PORTNAME portName=br0 PORTNAME portName=br0 PORTNAME portName=p1 PORTNAME portName=p1 PORTNAME portName=p2 PORTNAME portName=p2 ])]) AT_SETUP([ofproto-dpif - static-mac add/del/flush]) OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 dnl Generate some dynamic fdb entries on some ports. OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:01)], [-generate], [100,2]) OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=50:54:00:00:00:02)], [-generate], [100,1]) dnl Add some static mac entries. AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:01:01]) AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:02:02]) dnl Check initial fdb. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl 1 0 50:54:00:00:00:01 1 0 50:54:00:00:01:01 static 2 0 50:54:00:00:00:02 2 0 50:54:00:00:02:02 static ]) dnl Check json output. AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 \ | sed 's/"age": [[0-9]]*/"age": ?/g'], [0], [dnl [[ { "age": ?, "mac": "50:54:00:00:00:01", "port": 1, "vlan": 0}, { "age": ?, "mac": "50:54:00:00:00:02", "port": 2, "vlan": 0}, { "mac": "50:54:00:00:01:01", "port": 1, "static": true, "vlan": 0}, { "mac": "50:54:00:00:02:02", "port": 2, "static": true, "vlan": 0}]] ]) dnl Remove static mac entry. AT_CHECK([ovs-appctl fdb/del br0 0 50:54:00:00:01:01]) dnl Check that entry is removed. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | grep "50:54:00:00:01:01"], [1], [dnl ]) # Add some more cache and static entries, to test out flush operation for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do ovs-appctl ofproto/trace ovs-dummy "in_port(1),eth(src=50:54:00:00:0$i:ff)" -generate ovs-appctl fdb/add br0 p2 0 50:54:00:0$i:00:ff done for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do ovs-appctl fdb/add br0 p2 0 50:54:00:0$i:00:ff done dnl Flush mac entries, only dynamic ones should be evicted. AT_CHECK([ovs-appctl fdb/flush br0], [0], [dnl table successfully flushed ]) dnl Count number of static entries remaining. AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 | grep static], [0], [dnl Current static MAC entries in the table : 17 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - static-mac mac moves]) OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 dnl Generate a dynamic entry. OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:00)], [-generate], [100,2]) dnl Convert dynamically learnt dl_src to a static-mac. AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:00], [0], [dnl Overriding already existing dynamic entry on 1 ]) dnl Check fdb. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl 1 0 50:54:00:00:00:00 static ]) dnl Move the static mac to different port. AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:00:00], [0], [dnl Overriding already existing static entry on 1 ]) dnl Check fdb. AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl 2 0 50:54:00:00:00:00 static ]) dnl static-mac should not be converted to a dynamic one when a packet with same dl_src dnl arrives on any port of the switch. dnl Packet arriving on p1. OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:00)], [-generate], [100,2]) AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl 2 0 50:54:00:00:00:00 static ]) dnl Packet arriving on p2. OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=50:54:00:00:00:00)], [-generate], [100,1]) AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl 2 0 50:54:00:00:00:00 static ]) dnl Check mac_move coverage counter mac_learning_static_none_move. AT_CHECK([ovs-appctl coverage/read-counter mac_learning_static_none_move], [0], [dnl 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - static-mac learned mac age out]) OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone -- set bridge br0 other_config:mac-aging-time=5]) add_of_ports br0 1 2 dnl Add some static mac entries. AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:01:01]) AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:02:02]) dnl Generate some dynamic fdb entries on some ports. OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=60:54:00:00:00:01)], [-generate], [100,2]) OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=60:54:00:00:00:02)], [-generate], [100,1]) dnl Waiting for aging out. ovs-appctl time/warp 20000 dnl Count number of static entries remaining. AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 | grep expired], [0], [dnl Total number of expired MAC entries : 2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - basic truncate action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 5 AT_CHECK([ovs-vsctl -- set Interface p1 type=dummy options:pcap=p1.pcap]) AT_CHECK([ovs-vsctl -- set Interface p2 type=dummy options:pstream=punix:p2.sock]) AT_CHECK([ovs-vsctl -- set Interface p3 type=dummy options:stream=unix:p2.sock]) AT_CHECK([ovs-vsctl -- set Interface p4 type=dummy options:pstream=punix:p4.sock]) AT_CHECK([ovs-vsctl -- set Interface p5 type=dummy options:stream=unix:p4.sock]) AT_DATA([flows.txt], [dnl in_port=3,actions=drop in_port=5,actions=drop in_port=1,actions=output(port=2,max_len=64),output:4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Datapath actions AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: trunc(64),2,4 ]) dnl An 170 byte packet AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f']) AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl icmp,in_port=ANY,vlan_tci=0x0000,dl_src=00:50:56:c0:00:08,dl_dst=00:0c:29:c8:a0:a4,nw_src=192.168.218.1,nw_dst=192.168.218.100,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl packet with truncated size AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=64 ]) dnl packet with original size AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=170 ]) dnl More complicated case AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl in_port=3,actions=drop in_port=5,actions=drop in_port=1,actions=output(port=2,max_len=64),output(port=2,max_len=128),output(port=4,max_len=60),output:2,output:4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Datapath actions AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: trunc(64),2,trunc(128),2,trunc(60),4,2,4 ]) dnl An 170 byte packet AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f']) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl packet size: 64 + 128 + 170 = 362 AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=362 ]) dnl packet size: 60 + 170 = 230 AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=230 ]) dnl syntax checking AT_CHECK([ovs-ofctl add-flow br0 'actions=output(port=ALL,max_len=100)'], [1], [], [dnl ovs-ofctl: output to unsupported truncate port: ALL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - truncate and output to patch port]) OVS_VSWITCHD_START([add-br br1 \ -- set bridge br1 datapath-type=dummy fail-mode=secure \ -- add-port br1 pbr1 -- set int pbr1 type=patch options:peer=pbr0 ofport_request=1 \ -- add-port br0 pbr0 -- set int pbr0 type=patch options:peer=pbr1]) add_of_ports br0 2 AT_CHECK([ovs-ofctl add-flow br0 actions='output(port=1,max_len=100),output:2']) AT_CHECK([ovs-ofctl add-flow br1 actions=NORMAL]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=LOCAL,dl_src=10:20:30:40:50:60], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl the output(port=1,max_len=100) fails the translation, only output:2 in datapath AT_CHECK([grep "output_trunc does not support patch port [[0-9]]*" stdout], [0], [stdout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - truncate and output to gre tunnel]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) AT_DATA([flows.txt], [dnl actions=output(max_len=100, port=1) ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1) p2 2/2: (dummy) ]) dnl Basic AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: trunc(100),set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - sFlow packet sampling - IPv4 collector]) CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1]) AT_CLEANUP AT_SETUP([ofproto-dpif - sFlow packet sampling - IPv6 collector]) AT_SKIP_IF([test $HAVE_IPV6 = no]) CHECK_SFLOW_SAMPLING_PACKET([[[::1]]]) AT_CLEANUP dnl Test sFlow LAG structures AT_SETUP([ofproto-dpif - sFlow packet sampling - LACP structures]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START([dnl add-bond br0 bond p1 p2 -- \ set Port bond lacp=active bond-mode=active-backup \ other_config:lacp-time="fast" \ other_config:lacp-system-id=11:22:33:44:55:66 \ other_config:lacp-system-priority=54321 -- \ set Interface p1 type=dummy \ other_config:lacp-port-id=11 \ other_config:lacp-port-priority=111 \ other_config:lacp-aggregation-key=3333 -- \ set Interface p2 type=dummy \ other_config:lacp-port-id=22 \ other_config:lacp-port-priority=222 \ other_config:lacp-aggregation-key=3333 ]) on_exit 'kill `cat test-sflow.pid`' AT_CHECK([ovstest test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([sflow.log]) PARSE_LISTENING_PORT([test-sflow.log], [SFLOW_PORT]) ovs-appctl time/stop ovs-vsctl \ set Interface p1 options:ifindex=1003 -- \ set Bridge br0 sflow=@sf -- \ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \ header=128 sampling=1 polling=1 agent=127.0.0.1 dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second) AT_CHECK([ovs-appctl time/warp 2000 100], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge], [0]) OVS_VSWITCHD_STOP(["/failed to get flags for network device 127.0.0.1/d"]) OVS_APP_EXIT_AND_WAIT([test-sflow]) AT_CHECK([[sort sflow.log | $EGREP 'LACPCOUNTERS|ERROR' | head -n 1 | sed 's/ /\ /g']], [0], [dnl LACPCOUNTERS sysID=11:22:33:44:55:66 partnerID=00:00:00:00:00:00 aggID=3333 actorAdmin=0x7 actorOper=0xbf partnerAdmin=0x0 partnerOper=0x2 LACPDUsRx=0 markerPDUsRx=4294967295 markerRespPDUsRx=4294967295 unknownRx=4294967295 illegalRx=0 LACPDUsTx=1 markerPDUsTx=4294967295 markerRespPDUsTx=4294967295 ]) AT_CLEANUP AT_SETUP([ofproto-dpif - sFlow packet sampling - tunnel set]) AT_XFAIL_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) dnl set up sFlow logging AT_CHECK([ovstest test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([sflow.log]) PARSE_LISTENING_PORT([test-sflow.log], [SFLOW_PORT]) ovs-appctl time/stop OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-vsctl add-port br0 gre0 -- set Interface gre0 type=gre \ options:remote_ip=1.1.1.1 options:key=456 ofport_request=3]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy ofport_request=4]) AT_CHECK([ovs-ofctl add-flow br0 action=3]) dnl enable sflow ovs-vsctl \ set Bridge br0 sflow=@sf -- \ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" agent=127.0.0.1 \ header=128 sampling=1 polling=0 dnl introduce a packet that will be flooded to the tunnel AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(4),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)']) dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second) for i in `seq 1 30`; do ovs-appctl time/warp 100 done OVS_APP_EXIT_AND_WAIT([test-sflow]) AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\ /g']], [0], [dnl HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=1 tunnel4_out_length=0 tunnel4_out_protocol=47 tunnel4_out_src=0.0.0.0 tunnel4_out_dst=1.1.1.1 tunnel4_out_src_port=0 tunnel4_out_dst_port=0 tunnel4_out_tcp_flags=0 tunnel4_out_tos=1 tunnel_out_vni=456 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=1 dropEvents=0 in_ifindex=0 in_format=0 out_ifindex=1 out_format=2 hdr_prot=1 pkt_len=110 stripped=4 hdr_len=106 hdr=50-54-00-00-00-0A-50-54-00-00-00-09-08-00-45-01-00-5C-00-00-00-00-80-01-12-8A-0A-0A-0A-02-0A-0A-0A-01-08-00-13-FC-00-00-00-00-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F-20-21-22-23-24-25-26-27-28-29-2A-2B-2C-2D-2E-2F-30-31-32-33-34-35-36-37-38-39-3A-3B-3C-3D-3E-3F ]) OVS_VSWITCHD_STOP(["/failed to get flags for network device 127.0.0.1/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - sFlow packet sampling - tunnel push]) AT_XFAIL_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 options:ifindex=1010]) dnl set up sFlow logging AT_CHECK([ovstest test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([sflow.log]) PARSE_LISTENING_PORT([test-sflow.log], [SFLOW_PORT]) ovs-appctl time/stop AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl -- add-port int-br t1 -- set Interface t1 type=gre \ options:remote_ip=1.1.2.92 options:key=456 ofport_request=4\ -- add-port int-br vm1 -- set Interface vm1 type=dummy \ options:ifindex=2011 ofport_request=5 ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy: ifindex=1010) int-br: int-br 65534/2: (dummy-internal) t1 4/4: (gre: key=456, remote_ip=1.1.2.92) vm1 5/3: (dummy: ifindex=2011) ]) dnl Add 1.1.2.92 to br0 and action=normal AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local ]) dnl Prime ARP Cache for 1.1.2.92 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) dnl configure sflow on int-br only ovs-vsctl \ set Bridge int-br sflow=@sf -- \ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" agent=127.0.0.1 \ header=128 sampling=1 polling=0 dnl Add 192.168.1.2 to br0, AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.1.1/16], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 192.168.0.0/16 dev br0 SRC 192.168.1.1 Cached: 192.168.1.1/32 dev br0 SRC 192.168.1.1 local ]) dnl add rule for int-br to force packet onto tunnel. There is no ifindex dnl for this port so the sFlow output will just report that it went to dnl 1 output (out_format=2, out_ifindex=1) AT_CHECK([ovs-ofctl add-flow int-br "actions=4"]) AT_CHECK([ovs-appctl netdev-dummy/receive vm1 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.2.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)']) dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second) for i in `seq 1 30`; do ovs-appctl time/warp 100 done OVS_APP_EXIT_AND_WAIT([test-sflow]) AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\ /g']], [0], [dnl HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=1 tunnel4_out_length=0 tunnel4_out_protocol=47 tunnel4_out_src=1.1.2.88 tunnel4_out_dst=1.1.2.92 tunnel4_out_src_port=0 tunnel4_out_dst_port=0 tunnel4_out_tcp_flags=0 tunnel4_out_tos=0 tunnel_out_vni=456 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=1 dropEvents=0 in_ifindex=2011 in_format=0 out_ifindex=1 out_format=2 hdr_prot=1 pkt_len=110 stripped=4 hdr_len=106 hdr=50-54-00-00-00-0A-50-54-00-00-00-05-08-00-45-00-00-5C-00-00-00-00-80-01-B6-4D-C0-A8-01-01-C0-A8-02-02-08-00-13-FC-00-00-00-00-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F-20-21-22-23-24-25-26-27-28-29-2A-2B-2C-2D-2E-2F-30-31-32-33-34-35-36-37-38-39-3A-3B-3C-3D-3E-3F ]) OVS_VSWITCHD_STOP(["/failed to get flags for network device 127.0.0.1/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - sFlow packet sampling - MPLS]) AT_XFAIL_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 dl_src=50:54:00:00:00:09 actions=push_mpls:0x8847,set_mpls_label:789,set_mpls_tc:4,set_mpls_ttl:32,2 table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl set up sFlow logging AT_CHECK([ovstest test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([sflow.log]) PARSE_LISTENING_PORT([test-sflow.log], [SFLOW_PORT]) ovs-appctl time/stop dnl configure sflow ovs-vsctl \ set Bridge br0 sflow=@sf -- \ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" agent=127.0.0.1 \ header=128 sampling=1 polling=0 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second) for i in `seq 1 30`; do ovs-appctl time/warp 100 done OVS_APP_EXIT_AND_WAIT([test-sflow]) AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\ /g']], [0], [dnl HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=1 mpls_label_0=789 mpls_tc_0=4 mpls_ttl_0=32 mpls_bos_0=0 mpls_label_1=11 mpls_tc_1=3 mpls_ttl_1=64 mpls_bos_1=1 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=1 dropEvents=0 in_ifindex=0 in_format=0 out_ifindex=1 out_format=2 hdr_prot=1 pkt_len=22 stripped=4 hdr_len=18 hdr=50-54-00-00-00-0A-50-54-00-00-00-09-88-47-00-00-B7-40 HEADER dgramSeqNo=1 ds=127.0.0.1>2:1000 fsSeqNo=2 mpls_label_0=789 mpls_tc_0=4 mpls_ttl_0=32 mpls_bos_0=1 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=2 dropEvents=0 in_ifindex=0 in_format=0 out_ifindex=1 out_format=2 hdr_prot=1 pkt_len=38 stripped=4 hdr_len=34 hdr=50-54-00-00-00-0A-50-54-00-00-00-09-08-00-45-00-00-14-00-00-00-00-00-00-BA-EB-00-00-00-00-00-00-00-00 ]) OVS_VSWITCHD_STOP(["/failed to get flags for network device 127.0.0.1/d"]) AT_CLEANUP # CHECK_NETFLOW_EXPIRATION(LOOPBACK_ADDR) # # Test that basic NetFlow reports flow statistics correctly: # The initial packet of a flow are correctly accounted. # Later packets within a flow are correctly accounted. # Flow actions changing (in this case, due to MAC learning) # cause a record to be sent. m4_define([CHECK_NETFLOW_EXPIRATION], [OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 ovs-appctl time/stop on_exit 'kill `cat test-netflow.pid`' AT_CHECK([ovstest test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([netflow.log]) PARSE_LISTENING_PORT([test-netflow.log], [NETFLOW_PORT]) ovs-vsctl \ set Bridge br0 netflow=@nf -- \ --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \ engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false for delay in 1000 30000; do ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' sleep 1 # ensure the order in which these two packets are processed ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)' ovs-appctl time/warp $delay done ovs-appctl time/warp 6000 ovs-appctl revalidator/wait OVS_VSWITCHD_STOP OVS_APP_EXIT_AND_WAIT([test-netflow]) AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 106 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1]) AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 106 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1]) combined=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 212 bytes, ICMP 0:0" netflow.log | wc -l` separate=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 1 pkts, 106 bytes, ICMP 0:0" netflow.log | wc -l` AT_CHECK([test $separate = 2 || test $combined = 1], [0])]) AT_SETUP([ofproto-dpif - NetFlow flow expiration - IPv4 collector]) CHECK_NETFLOW_EXPIRATION([127.0.0.1]) AT_CLEANUP AT_SETUP([ofproto-dpif - NetFlow flow expiration - IPv6 collector]) AT_SKIP_IF([test $HAVE_IPV6 = no]) CHECK_NETFLOW_EXPIRATION([[[::1]]]) AT_CLEANUP # CHECK_NETFLOW_ACTIVE_EXPIRATION(LOOPBACK_ADDR) # # Test that basic NetFlow reports active expirations correctly. m4_define([CHECK_NETFLOW_ACTIVE_EXPIRATION], [OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) add_of_ports br0 1 2 on_exit 'kill `cat test-netflow.pid`' AT_CHECK([ovstest test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([netflow.log]) PARSE_LISTENING_PORT([test-netflow.log], [NETFLOW_PORT]) ovs-vsctl \ set Bridge br0 netflow=@nf -- \ --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \ engine_id=1 engine_type=2 active_timeout=10 add-id-to-interface=false AT_CHECK([ovs-appctl time/stop]) n=1 while test $n -le 60; do n=`expr $n + 1` ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=1234,dst=80)' ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=1234)' ovs-appctl time/warp 1000 done ovs-appctl time/warp 10000 ovs-appctl revalidator/wait OVS_VSWITCHD_STOP OVS_APP_EXIT_AND_WAIT([test-netflow]) # Count the number of reported packets: # - From source to destination before MAC learning kicks in (just one). # - From source to destination after that. # - From destination to source. n_learn=0 n_in=0 n_out=0 n_other=0 n_recs=0 none=0 while read line; do pkts=`echo "$line" | sed 's/.*, \([[0-9]]*\) pkts,.*/\1/'` case $pkts in [[0-9]]*) ;; *) continue ;; esac case $line in "seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 65535, "*" pkts, "*" bytes, TCP 1234 > 80, time "*) counter=n_learn ;; "seq "*": 192.168.0.1 > 192.168.0.2, if 1 > 2, "*" pkts, "*" bytes, TCP 1234 > 80, time "*) counter=n_in ;; "seq "*": 192.168.0.2 > 192.168.0.1, if 2 > 1, "*" pkts, "*" bytes, TCP 80 > 1234, time "*) counter=n_out ;; *) counter=n_other ;; esac eval $counter=\`expr \$$counter + \$pkts\` n_recs=`expr $n_recs + 1` done < netflow.log # There should be exactly 1 MAC learning packet, # exactly 59 other packets in that direction, # and exactly 60 packets in the other direction. AT_CHECK([echo $n_learn $n_in $n_out $n_other], [0], [1 59 60 0 ])]) AT_SETUP([ofproto-dpif - NetFlow active expiration - IPv4 collector]) CHECK_NETFLOW_ACTIVE_EXPIRATION([127.0.0.1]) AT_CLEANUP AT_SETUP([ofproto-dpif - NetFlow active expiration - IPv6 collector]) AT_SKIP_IF([test $HAVE_IPV6 = no]) CHECK_NETFLOW_ACTIVE_EXPIRATION([[[::1]]]) AT_CLEANUP dnl In the absence of an IPFIX collector to verify protocol correctness, simply dnl configure bridge IPFIX and ensure that sample action generation works at the dnl datapath level. AT_SETUP([ofproto-dpif - Bridge IPFIX sanity check]) OVS_VSWITCHD_START dnl first revalidation triggered by add interface AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 1 ]) add_of_ports br0 1 2 3 AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 2 ]) dnl Sample every packet using bridge-based sampling. AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \ --id=@fix create ipfix targets=\"127.0.0.1:4739\" \ sampling=2], [0], [ignore]) AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 3 ]) AT_CHECK([ovs-vsctl set ipfix `ovs-vsctl get bridge br0 ipfix` sampling=1], [0]) AT_CHECK([ovs-appctl coverage/read-counter rev_reconfigure], [0], [dnl 4 ]) dnl Send some packets that should be sampled. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:68, used:0.001s, actions:userspace(pid=0,ipfix(output_port=4294967295)) ]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Check sample is performed even if only one of the ports is present. AT_DATA([flows.txt], [dnl table=0,in_port=3,tcp actions=load:0xffff->NXM_OF_IN_PORT[],ct(zone=1,table=1) table=1,tcp, actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:08,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:236, used:0.001s, actions:userspace(pid=0,ipfix(output_port=2)),2 packets:2, bytes:236, used:0.001s, actions:userspace(pid=0,ipfix(output_port=4294967295)),ct(zone=1),recirc(0x1) ]) AT_CHECK([ovs-ofctl del-flows br0 in_port=3]) AT_CHECK([ovs-ofctl del-flows br0 table=1]) AT_CHECK([ovs-appctl revalidator/purge]) dnl dnl Add a slowpath meter. The userspace action should be metered. AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=slowpath pktps burst stats bands=type=drop rate=3 burst_size=1']) dnl Send some packets that should be sampled and metered. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:68, used:0.001s, actions:sample(sample=100.0%,actions(meter(0),userspace(pid=0,ipfix(output_port=4294967295)))) ]) dnl Remove the IPFIX configuration. AT_CHECK([ovs-vsctl clear bridge br0 ipfix]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Send some more packets, to ensure that these are not sampled. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:68, used:0.001s, actions:drop ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP dnl Bridge IPFIX statistics check AT_SETUP([ofproto-dpif - Bridge IPFIX statistics check]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Negative test check. AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED NXST_IPFIX_BRIDGE request (xid=0x2): ]) dnl Sample every packet using bridge-based sampling. AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \ --id=@fix create ipfix targets=\"127.0.0.1:4739\" \ sampling=1], [0], [ignore]) dnl Send some packets that should be sampled. for i in `seq 1 20`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done dnl There are 4 extra IPFIX template packets. AT_CHECK([ovs-ofctl dump-ipfix-bridge br0 | sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | sed 's/tx errs=[[0-9]]*/tx errs=0/'], [0], [dnl NXST_IPFIX_BRIDGE reply (xid=0x2): bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=24 pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=0 ]) dnl Remove the IPFIX configuration. AT_CHECK([ovs-vsctl clear bridge br0 ipfix]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Send some more packets, to ensure that these are not sampled. for i in `seq 1 2`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED NXST_IPFIX_BRIDGE request (xid=0x2): ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP dnl Flow IPFIX sanity check AT_SETUP([ofproto-dpif - Flow IPFIX sanity check]) OVS_VSWITCHD_START add_of_ports br0 1 2 # Check for regression against a bug where an invalid target caused an # assertion failure and a crash. AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"xyzzy\" \ -- --id=@cs create Flow_Sample_Collector_Set id=0 bridge=@br0 ipfix=@ipfix], [0], [ignore]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ -- --id=@cs create Flow_Sample_Collector_Set id=1 bridge=@br0 ipfix=@ipfix], [0], [ignore]) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=65535,collector_set_id=1),output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) dnl Send some packets that should be sampled. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:68, used:0.001s, actions:userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=4294967295)),2 ]) dnl Remove the flow which contains sample action. AT_CHECK([ovs-ofctl del-flows br0 in_port=1], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Send some more packets, to ensure that these are not sampled. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl flow-dump from the main thread: packets:2, bytes:68, used:0.001s, actions:drop ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d /xyzzy/d /no collectors/d"]) AT_CLEANUP dnl Flow IPFIX sanity check for tunnel set AT_SETUP([ofproto-dpif - Flow IPFIX sanity check - tunnel set]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=geneve \ options:remote_ip=1.1.1.2 options:local_ip=2.2.2.3 \ options:key=6 ofport_request=2\ -- add-port br0 p3 -- set Interface p3 type=dummy \ ofport_request=3 \ -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ -- --id=@cs create Flow_Sample_Collector_Set id=1 bridge=@br0 ipfix=@ipfix], [<0> <1> ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP dnl Add openflow sample action without sampling_port. AT_DATA([flows.txt], [dnl in_port=3, actions=sample(probability=65535,collector_set_id=1),output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=4294967295)),set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),1 ]) dnl Remove the flow which contains sample action. AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Add openflow sample action with sampling_port which is dnl equal to output port. AT_DATA([flows2.txt], [dnl in_port=3, actions=sample(probability=65535,collector_set_id=1,sampling_port=1),output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows2.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) dnl Make sure flow sample action in datapath is behind set tunnel dnl action at egress point of tunnel port. AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=1),tunnel_out_port=1),1 ]) dnl Remove the flow which contains sample action. AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Add a rule with two sample actions and each sample action dnl has a sampling_port AT_DATA([flows3.txt], [dnl in_port=3, actions=sample(probability=65535,collector_set_id=1,sampling_port=1),output:1,sample(probability=65535,collector_set_id=1,sampling_port=2),output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows3.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) dnl Make sure flow sample action in datapath is behind set tunnel dnl action at egress point of tunnel port. AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=1),tunnel_out_port=1),1,set(tunnel(tun_id=0x6,src=2.2.2.3,dst=1.1.1.2,tos=0x1,ttl=64,tp_dst=6081,flags(df|key))),userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=0,obs_point_id=0,output_port=6081),tunnel_out_port=6081),6081 ]) dnl Remove the flow which contains sample action. AT_CHECK([ovs-ofctl del-flows br0 in_port=3], [0], [ignore]) AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Flow IPFIX sanity check - from field]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:5500\" \ -- --id=@cs create Flow_Sample_Collector_Set id=0 \ bridge=@br0 ipfix=@ipfix], [0], [ignore]) m4_define([SAMPLE_ACTION], [sample(probability=65535,collector_set_id=1,obs_domain_id=NXM_OF_IN_PORT,obs_point_id=$1)]dnl ) dnl Store in_port in obs_domain_id and dp_hash in the obs_point_id. AT_DATA([flows.txt], [dnl priority=100,arp,action=normal priority=10,in_port=1,ip actions=SAMPLE_ACTION(NXM_NX_DP_HASH),2 priority=10,in_port=2,ip actions=SAMPLE_ACTION(NXM_NX_CT_LABEL[[[0..31]]]),1 priority=10,in_port=3,ip actions=SAMPLE_ACTION(NXM_NX_CT_LABEL[[[10..14]]]),1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(1),dp_hash(45),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,dp_hash=0x2d,eth,ip,in_port=1,nw_frag=no Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=1,obs_point_id=45,output_port=4294967295)),2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(2),ct_label(0x1234567890abcdef1234567890abcdef),\ eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,ct_label=0x90abcdef/0xffffffff,eth,ip,in_port=2,nw_frag=no Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=2,obs_point_id=2427178479,output_port=4294967295)),1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ "in_port(3),ct_label(0x1234567890abcdef1234567890abcdef),\ eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),\ ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)"], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,ct_label=0x4c00/0x7c00,eth,ip,in_port=3,nw_frag=no Datapath actions: userspace(pid=0,flow_sample(probability=65535,collector_set_id=1,obs_domain_id=3,obs_point_id=19,output_port=4294967295)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - clone action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 dnl Reversible open flow clone actions, no datapath clone action should be generated. AT_DATA([flows.txt], [dnl in_port=1, ip, actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(mod_dl_src:80:81:81:81:81:81,set_field:192.168.5.5->ip_dst,output:3),output:4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(eth(src=80:81:81:81:81:81)),set(ipv4(dst=192.168.5.5)),3,set(eth(src=50:54:00:00:00:09)),set(ipv4(dst=10.10.10.1)),4 ]) dnl Test flow xlate openflow clone action without using datapath clone action. AT_CHECK([ovs-appctl dpif/set-dp-features br0 clone false], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(eth(src=80:81:81:81:81:81)),set(ipv4(dst=192.168.5.5)),3,set(eth(src=50:54:00:00:00:09)),set(ipv4(dst=10.10.10.1)),4 ]) AT_CHECK([ovs-appctl dpif/set-dp-features br0 sample_nesting 2], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(eth(src=80:81:81:81:81:81)),set(ipv4(dst=192.168.5.5)),3,set(eth(src=50:54:00:00:00:09)),set(ipv4(dst=10.10.10.1)),4 ]) dnl Mixing reversible and irreversible open flow clone actions. Datapath clone action dnl should be generated when necessary. dnl Restore the datapath support level. AT_CHECK([ovs-appctl dpif/set-dp-features br0 clone true], [0], []) AT_CHECK([ovs-appctl dpif/set-dp-features br0 sample_nesting 10], [0], []) AT_DATA([flows.txt], [dnl in_port=1, ip, actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(ct(commit),output:3),output:4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(ipv4(dst=10.10.10.1)),clone(ct(commit),3),4 ]) AT_CHECK([ovs-appctl dpif/set-dp-features br0 clone false], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(ipv4(dst=10.10.10.1)),sample(sample=100.0%,actions(ct(commit),3)),4 ]) AT_CHECK([ovs-appctl dpif/set-dp-features br0 sample_nesting 2], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: set(ipv4(dst=192.168.4.4)),2,set(ipv4(dst=10.10.10.1)),4 ]) AT_CHECK([grep "Failed to compose clone action" stdout], [0], [ignore]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Flow based IPFIX statistics check AT_SETUP([ofproto-dpif - Flow IPFIX statistics check]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Negative test check. AT_CHECK([ovs-ofctl dump-ipfix-flow br0], [0], [dnl OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED NXST_IPFIX_FLOW request (xid=0x2): ]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ -- --id=@cs create Flow_Sample_Collector_Set id=1 bridge=@br0 ipfix=@ipfix], [0], [ignore]) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=65535,collector_set_id=1),output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore]) dnl Send some packets that should be sampled. for i in `seq 1 20`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done dnl There are 4 extra IPFIX template packets. AT_CHECK([ovs-ofctl dump-ipfix-flow br0 | sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | sed 's/tx errs=[[0-9]]*/tx errs=0/'], [0], [dnl NXST_IPFIX_FLOW reply (xid=0x2): 1 ids id 1: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=24 pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=0 ]) dnl Remove the flow which contains sample action. AT_CHECK([ovs-ofctl del-flows br0 in_port=1], [0], [ignore]) AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge]) dnl Send some more packets, to ensure that these are not sampled. for i in `seq 1 3`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)']) done AT_CHECK([ovs-ofctl dump-ipfix-flow br0], [0], [dnl OFPT_ERROR (xid=0x2): NXST_NOT_CONFIGURED NXST_IPFIX_FLOW request (xid=0x2): ]) OVS_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - flow stats]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"]) AT_CHECK([ovs-ofctl add-flow br0 "icmp,actions=NORMAL"]) ovs-appctl time/stop for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)' done AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | grep actions= | sort], [0], [dnl icmp actions=NORMAL n_packets=10, n_bytes=1180, ip actions=NORMAL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - flow stats reset_counts]) OVS_VSWITCHD_START flow="ip,actions=NORMAL" ovs-appctl time/stop AT_CHECK([ovs-ofctl add-flow br0 $flow]) warp_and_dump_NXM () { AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br0], [0], [stdout]) if [[ $5 -gt 0 ]]; then expected=" cookie=0x0, duration=$1s, table=0, n_packets=$2, n_bytes=$3, idle_age=$4, hard_age=$5, ip actions=NORMAL" else expected=" cookie=0x0, duration=$1s, table=0, n_packets=$2, n_bytes=$3, idle_age=$4, ip actions=NORMAL" fi AT_CHECK_UNQUOTED([strip_xids < stdout | sed -n 's/duration=\([[0-9]]*\)\.*[[0-9]]*s/duration=\1s/p' | sort], [0], [dnl $expected ]) } warp_and_dump_OF () { AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 dump-flows br0], [0], [stdout]) if [[ $1 -lt 13 -o "$5X" = "X" ]]; then expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, ip actions=NORMAL" else expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, $5 ip actions=NORMAL" fi AT_CHECK_UNQUOTED([strip_xids < stdout | sed -n 's/duration=\([[0-9]]*\)\.*[[0-9]]*s/duration=\1s/p' | sort], [0], [dnl $expected ]) } send_packet () { ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)' } # OpenFlow 1.0, implicit reset_counts send_packet warp_and_dump_NXM 1 1 118 1 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow10 add-flow br0 $flow]) # add-flow resets duration and counts, # but idle age is inherited from the old flow warp_and_dump_NXM 1 0 0 2 send_packet warp_and_dump_NXM 2 1 118 1 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow10 mod-flows br0 $flow]) # mod-flows resets hard_age, but not counts # but duration and idle_age is inherited from the old flow warp_and_dump_NXM 3 1 118 2 1 # OpenFlow 1.1, implicit reset_counts send_packet warp_and_dump_OF 11 4 2 236 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow11 add-flow br0 $flow]) # add-flow resets duration and counts, # but idle age is inherited from the old flow warp_and_dump_NXM 1 0 0 2 warp_and_dump_OF 11 2 0 0 send_packet warp_and_dump_OF 11 3 1 118 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow11 mod-flows br0 $flow]) # mod-flows resets hard_age, but not counts # but duration and idle_age is inherited from the old flow warp_and_dump_NXM 4 1 118 2 1 warp_and_dump_OF 11 5 1 118 # OpenFlow 1.2, explicit reset_counts send_packet warp_and_dump_OF 12 6 2 236 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow12 add-flow br0 $flow]) # add-flow without flags resets duration, but not counts, # idle age is inherited from the old flow warp_and_dump_NXM 1 2 236 2 warp_and_dump_OF 12 2 2 236 send_packet warp_and_dump_OF 12 3 3 354 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow12 mod-flows br0 $flow]) # mod-flows without flags does not reset duration nor counts, # idle age is inherited from the old flow warp_and_dump_NXM 4 3 354 2 1 warp_and_dump_OF 12 5 3 354 send_packet warp_and_dump_OF 12 6 4 472 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow12 add-flow br0 reset_counts,$flow]) # add-flow with reset_counts resets both duration and counts, # idle age is inherited from the old flow warp_and_dump_NXM 1 0 0 2 warp_and_dump_OF 12 2 0 0 send_packet warp_and_dump_OF 12 3 1 118 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow12 mod-flows br0 reset_counts,$flow]) # mod-flows with reset_counts resets counts, but not duration, # idle age is inherited from the old flow warp_and_dump_NXM 4 0 0 2 1 warp_and_dump_OF 12 5 0 0 # OpenFlow > 1.3, explicit reset_counts flow_mods_reset_counts () { # Reset to a known state AT_CHECK([ovs-ofctl add-flow br0 $flow]) send_packet warp_and_dump_OF $1 1 1 118 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow]) # add-flow without flags resets duration, but not counts, # idle age is inherited from the old flow warp_and_dump_NXM 1 1 118 2 warp_and_dump_OF $1 2 1 118 send_packet warp_and_dump_OF $1 3 2 236 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow]) # mod-flows without flags does not reset duration nor counts, # idle age is inherited from the old flow warp_and_dump_NXM 4 2 236 2 1 warp_and_dump_OF $1 5 2 236 send_packet warp_and_dump_OF $1 6 3 354 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 reset_counts,$flow]) # add-flow with reset_counts resets both duration and counts, # idle age is inherited from the old flow warp_and_dump_NXM 1 0 0 2 warp_and_dump_OF $1 2 0 0 reset_counts send_packet warp_and_dump_OF $1 3 1 118 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow]) # mod-flows with reset_counts resets counts, but not duration, # idle age is inherited from the old flow warp_and_dump_NXM 4 0 0 2 1 warp_and_dump_OF $1 5 0 0 reset_counts # Modify flow having reset_counts flag without reset_counts send_packet warp_and_dump_OF $1 6 1 118 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow]) warp_and_dump_NXM 7 1 118 2 1 warp_and_dump_OF $1 8 1 118 reset_counts # Add flow having reset_counts flag without reset_counts send_packet warp_and_dump_OF $1 9 2 236 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow]) warp_and_dump_NXM 1 2 236 2 warp_and_dump_OF $1 2 2 236 # Modify flow w/o reset_counts flag with a flow_mod having reset_counts send_packet warp_and_dump_OF $1 3 3 354 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow]) warp_and_dump_NXM 4 0 0 2 1 warp_and_dump_OF $1 5 0 0 } # OpenFlow versions >= 1.3 should behave the same way flow_mods_reset_counts 13 flow_mods_reset_counts 14 OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - flow stats reset_counts OpenFlow1.5]) OVS_VSWITCHD_START flow="ip,actions=NORMAL" ovs-appctl time/stop AT_CHECK([ovs-ofctl add-flow br0 $flow]) warp_and_dump_OF () { AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 dump-flows br0 ], [0], [stdout]) if [[ "$6X" = "X" ]]; then expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, idle_age=$5, ip actions=NORMAL" else expected=" cookie=0x0, duration=$2s, table=0, n_packets=$3, n_bytes=$4, $6 idle_age=$5, ip actions=NORMAL" fi AT_CHECK_UNQUOTED([strip_xids < stdout | sed -n 's/duration=\([[0-9]]*\)\.*[[0-9]]*s/duration=\1s/p' | sort], [0], [dnl $expected ]) } send_packet () { ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)' } # OpenFlow 1.5, explicit reset_counts flow_mods_reset_counts () { # Reset to a known state AT_CHECK([ovs-ofctl add-flow br0 $flow]) send_packet warp_and_dump_OF $1 1 1 118 1 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow]) # add-flow without flags resets duration, but not counts, # idle age is inherited from the old flow warp_and_dump_OF $1 1 1 118 2 send_packet warp_and_dump_OF $1 2 2 236 1 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow]) # mod-flows without flags does not reset duration nor counts, # idle age is inherited from the old flow warp_and_dump_OF $1 3 2 236 2 send_packet warp_and_dump_OF $1 4 3 354 1 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 reset_counts,$flow]) # add-flow with reset_counts resets both duration and counts, # idle age is inherited from the old flow warp_and_dump_OF $1 1 0 0 2 reset_counts send_packet warp_and_dump_OF $1 2 1 118 1 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow]) # mod-flows with reset_counts resets counts, but not duration, # idle age is inherited from the old flow warp_and_dump_OF $1 3 0 0 2 reset_counts # Modify flow having reset_counts flag without reset_counts send_packet warp_and_dump_OF $1 4 1 118 1 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 $flow]) warp_and_dump_OF $1 5 1 118 2 reset_counts # Add flow having reset_counts flag without reset_counts send_packet warp_and_dump_OF $1 6 2 236 1 reset_counts AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 add-flow br0 $flow]) warp_and_dump_OF $1 1 2 236 2 # Modify flow w/o reset_counts flag with a flow_mod having reset_counts send_packet warp_and_dump_OF $1 2 3 354 1 AT_CHECK_UNQUOTED([ovs-ofctl -O OpenFlow$1 mod-flows br0 reset_counts,$flow]) warp_and_dump_OF $1 3 0 0 2 } # OpenFlow versions >= 1.3 should behave the same way flow_mods_reset_counts 15 OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - flow stats, set-n-threads]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"]) AT_CHECK([ovs-ofctl add-flow br0 "icmp,actions=NORMAL"]) ovs-appctl time/stop for i in `seq 1 10`; do ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)' done ovs-appctl time/warp 100 AT_CHECK([ovs-vsctl set Open_vSwitch . other-config:n-revalidator-threads=2]) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | grep actions= | sort], [0], [dnl icmp actions=NORMAL n_packets=10, n_bytes=1180, ip actions=NORMAL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - idle_age and hard_age increase over time]) OVS_VSWITCHD_START # get_ages DURATION HARD IDLE # # Fetch the flow duration, hard age, and idle age into the variables # whose names are given as arguments. Rounds DURATION down to the # nearest integer. If hard_age doesn't appear in the output, sets # HARD to "none". If idle_age doesn't appear in the output, sets IDLE # to 0. get_ages () { AT_CHECK([ovs-ofctl dump-flows br0], [0], [stdout]) duration=`sed -n 's/.*duration=\([[0-9]]*\)\(\.[[0-9]]*\)\{0,1\}s.*/\1/p' stdout` AT_CHECK([[expr X"$duration" : 'X[0-9][0-9]*$']], [0], [ignore]) AS_VAR_COPY([$1], [duration]) hard=`sed -n 's/.*hard_age=\([[0-9]]*\),.*/\1/p' stdout` if test X"$hard" = X; then hard=none else AT_CHECK([[expr X"$hard" : 'X[0-9][0-9]*$']], [0], [ignore]) fi AS_VAR_COPY([$2], [hard]) idle=`sed -n 's/.*idle_age=\([[0-9]]*\),.*/\1/p' stdout` if test X"$idle" = X; then idle=0 else AT_CHECK([[expr X"$idle" : 'X[0-9][0-9]*$']], [0], [ignore]) fi AS_VAR_COPY([$3], [idle]) } # Add a flow and get its initial hard and idle age. AT_CHECK([ovs-ofctl add-flow br0 hard_timeout=199,idle_timeout=188,actions=drop]) get_ages duration1 hard1 idle1 ovs-appctl time/stop # Warp time forward by 10 seconds, then modify the flow's actions. ovs-appctl time/warp 10000 get_ages duration2 hard2 idle2 AT_CHECK([ovs-ofctl mod-flows br0 actions=flood]) # Warp time forward by 10 seconds. ovs-appctl time/warp 10000 get_ages duration3 hard3 idle3 # Warp time forward 10 more seconds, then pass some packets through the flow, # then warp forward a few more times because idle times are only updated # occasionally. ovs-appctl time/warp 10000 ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=1234)' ovs-appctl time/warp 3000 1000 sleep 1 get_ages duration4 hard4 idle4 printf "duration: %4s => %4s => %4s => %4s\n" $duration1 $duration2 $duration3 $duration4 printf "hard_age: %4s => %4s => %4s => %4s\n" $hard1 $hard2 $hard3 $hard4 printf "idle_age: %4s => %4s => %4s => %4s\n" $idle1 $idle2 $idle3 $idle4 # Duration should increase steadily over time. AT_CHECK([test $duration1 -lt $duration2]) AT_CHECK([test $duration2 -lt $duration3]) AT_CHECK([test $duration3 -lt $duration4]) # Hard age should be "none" initially because it's the same as flow_duration, # then it should increase. AT_CHECK([test $hard1 = none]) AT_CHECK([test $hard2 = none]) AT_CHECK([test $hard3 != none]) AT_CHECK([test $hard4 != none]) AT_CHECK([test $hard3 -lt $hard4]) # Idle age should increase from 1 to 2 to 3, then decrease. AT_CHECK([test $idle1 -lt $idle2]) AT_CHECK([test $idle2 -lt $idle3]) AT_CHECK([test $idle3 -gt $idle4]) # Check some invariant relationships. AT_CHECK([test $duration1 = $idle1]) AT_CHECK([test $duration2 = $idle2]) AT_CHECK([test $duration3 = $idle3]) AT_CHECK([test $idle3 -gt $hard3]) AT_CHECK([test $idle4 -lt $hard4]) AT_CHECK([test $hard4 -lt $duration4]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fin_timeout]) OVS_VSWITCHD_START ovs-appctl time/stop AT_CHECK([ovs-ofctl add-flow br0 'idle_timeout=60,actions=fin_timeout(idle_timeout=5)']) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: idle_timeout=60, actions=fin_timeout(idle_timeout=5) ]) # Check that a TCP SYN packet does not change the timeout. (Because # flow stats updates are mainly what implements the fin_timeout # feature, we warp forward a couple of times to ensure that flow stats # run before re-checking the flow table.) AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f908004500003c2e2440004006465dac11370dac11370b828b0016751e267b00000000a00216d017360000020405b40402080a2d25085f0000000001030307]) AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped warped ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: n_packets=1, n_bytes=74, idle_timeout=60, actions=fin_timeout(idle_timeout=5) ]) # Check that a TCP FIN packet does change the timeout. AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f90800451000342e3e40004006463bac11370dac11370b828b0016751e319dfc96399b801100717ae800000101080a2d250a9408579588]) AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped warped ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: n_packets=2, n_bytes=140, idle_timeout=5, actions=fin_timeout(idle_timeout=5) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-dps]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy]) add_of_ports br0 1 2 add_of_ports br1 3 AT_CHECK([ovs-appctl dpif/dump-dps], [0], [dnl dummy@br0 dummy@br1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ovs-appctl dpif/show]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"]) add_pmd_of_ports br0 1 2 add_of_ports br1 3 AT_CHECK([ovs-appctl dpif/show | sed 's/\(dummy-pmd: \).*)/\1)/'], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p1 1/1: (dummy-pmd: ) p2 2/2: (dummy-pmd: ) br1: br1 65534/101: (dummy-internal) p3 3/3: (dummy) ]) AT_CHECK([ovs-appctl --format json --pretty dpif/show], [0], [dnl [{ "dummy@ovs-dummy": { "bridges": { "br0": { "br0": { "ofport": "65534", "port_no": "100", "type": "dummy-internal"}, "p1": { "config": { "n_rxq": "1", "n_txq": "1", "numa_id": "0"}, "ofport": "1", "port_no": "1", "type": "dummy-pmd"}, "p2": { "config": { "n_rxq": "1", "n_txq": "1", "numa_id": "0"}, "ofport": "2", "port_no": "2", "type": "dummy-pmd"}}, "br1": { "br1": { "ofport": "65534", "port_no": "101", "type": "dummy-internal"}, "p3": { "ofport": "3", "port_no": "3", "type": "dummy"}}}, "stats": { "hit": "0", "missed": "0"}}}] ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows]) # bump max-idle to avoid the flows being reclaimed behind us OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy fail-mode=secure -- \ set Open_vSwitch . other_config:max-idle=10000], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"]) add_of_ports br0 1 add_pmd_of_ports br0 2 add_of_ports br1 3 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl revalidator/wait AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop ]) AT_CHECK([ovs-appctl dpif/dump-flows br1 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop ]) AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(p1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(p2),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop ]) AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(p3),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([OFPROTO_DPIF_GET_FLOW], [AT_SETUP([ofproto-dpif - ovs-appctl dpif/get-flow$1]) OVS_VSWITCHD_START([add-br br1 -- \ set bridge br1 datapath-type=dummy fail-mode=secure -- \ set Open_vSwitch . other_config:max-idle=10000], [], [], [m4_if([$1], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"])]) func=`printf '%s_' "$1" | cut -c 4-` add_${func}of_ports br0 1 2 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl revalidator/wait AT_CHECK([ovs-appctl dpif/dump-flows -m br0], [0], [stdout]) UFID=`sed -n 's/\(ufid:[[-0-9a-fA-F]]*\).*/\1/p' stdout` AT_CHECK([ovs-appctl dpctl/get-flow $UFID], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP]) OFPROTO_DPIF_GET_FLOW([]) OFPROTO_DPIF_GET_FLOW([ - pmd]) AT_SETUP([ofproto-dpif - MPLS actions that result in a userspace action]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8847,controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Packet is sent to userspace because a MPLS push or pop action is applied to dnl a packet with 2 MPLS LSEs but dpif-netdev can't handle any labels. dnl dnl The input is a frame with two MPLS labels which tcpdump -vve shows as: dnl 60:66:66:66:66:00 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 for dl_src in 00 01; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"]) done sleep 1 # wait for the datapath flow installed AT_CHECK_UNQUOTED([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=60:66:66:66:66:00),eth_type(0x8847),mpls(label=20,tc=0,ttl=32,bos=0,label=20,tc=0,ttl=32,bos=1), actions:push_mpls(label=20,tc=0,ttl=32,bos=0,eth_type=0x8847),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=60:66:66:66:66:01),eth_type(0x8847),mpls(label=20/0x0,tc=0/0,ttl=32/0x0,bos=0/1,label=20/0xfffff,tc=0/7,ttl=32/0xff,bos=1/1), actions:pop_mpls(eth_type=0x8847),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - MPLS actions that result in a drop]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) on_exit 'kill `cat ovs-ofctl.pid`' AT_CAPTURE_FILE([ofctl_monitor.log]) AT_DATA([flows.txt], [dnl dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8847,controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Packet is dropped because an MPLS PUSH action is applied to a packet with dnl 4 MPLS LSEs but ovs-vswitchd can only handle up to 3 MPLS LSEs and thus dnl can't determine the resulting MPLS label after MPLS push/pop actions. dnl dnl The input is a frame with two MPLS headers which tcpdump -vve shows as: dnl 60:66:66:66:66:01 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32) dnl (label 20, exp 0, ttl 32) dnl (label 20, exp 0, ttl 32) dnl (label 20, exp 0, [S], ttl 32) dnl (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!) dnl 192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4 for dl_src in 00 01; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"]) done sleep 1 # wait for the datapath flow installed AT_CHECK_UNQUOTED([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=60:66:66:66:66:00),eth_type(0x8847),mpls(label=20,tc=0,ttl=32,bos=0,label=20,tc=0,ttl=32,bos=1), actions:push_mpls(label=20,tc=0,ttl=32,bos=0,eth_type=0x8847),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=60:66:66:66:66:01),eth_type(0x8847),mpls(label=20/0x0,tc=0/0,ttl=32/0x0,bos=0/1,label=20/0xfffff,tc=0/7,ttl=32/0xff,bos=1/1), actions:pop_mpls(eth_type=0x8847),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - patch ports]) OVS_VSWITCHD_START([add-br br1 \ -- set bridge br1 datapath-type=dummy fail-mode=secure \ -- add-port br1 pbr1 -- set int pbr1 type=patch options:peer=pbr0 \ -- add-port br0 pbr0 -- set int pbr0 type=patch options:peer=pbr1]) add_of_ports br0 2 add_of_ports br1 3 AT_CHECK([ovs-appctl upcall/disable-ufid], [0], [Datapath dumping tersely using UFID disabled ], []) AT_CHECK([ovs-appctl time/stop]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl vlog/disable-rate-limit dpif]) AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2]) AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3]) for i in $(seq 1 10); do ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' if [[ $i -eq 1 ]]; then sleep 1 fi done for i in $(seq 1 5); do ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' if [[ $i -eq 1 ]]; then sleep 1 fi done AT_CHECK([ovs-appctl time/warp 500], [0], [warped ]) sleep 1 # wait for log writer AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:13 missed:2 br0: br0 65534/100: (dummy-internal) p2 2/2: (dummy) pbr0 1/none: (patch: peer=pbr1) br1: br1 65534/101: (dummy-internal) p3 3/3: (dummy) pbr1 1/none: (patch: peer=pbr0) ]) AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(100),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:101,3,2 recirc_id(0),in_port(101),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:100,2,3 ]) AT_CHECK([grep -e 'in_port(100).*packets:9' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(100),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:954, used:0.0s, actions:101,3,2 ]) AT_CHECK([grep -e 'in_port(101).*packets:4' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(101),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:424, used:0.0s, actions:100,2,3 ]) AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl OFPST_PORT reply (xid=0x4): 1 ports port 1: rx pkts=5, bytes=530, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=10, bytes=1060, drop=?, errs=?, coll=? ]) AT_CHECK([ovs-ofctl dump-ports br1 pbr1], [0], [dnl OFPST_PORT reply (xid=0x4): 1 ports port 1: rx pkts=10, bytes=1060, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=5, bytes=530, drop=?, errs=?, coll=? ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - patch ports - stack]) OVS_VSWITCHD_START([add-br br1 \ -- set bridge br1 datapath-type=dummy fail-mode=secure \ -- add-port br1 pbr1 -- set int pbr1 type=patch options:peer=pbr0 \ -- add-port br0 pbr0 -- set int pbr0 type=patch options:peer=pbr1]) add_of_ports br0 2 add_of_ports br1 3 AT_CHECK([ovs-appctl upcall/disable-ufid], [0], [Datapath dumping tersely using UFID disabled ], []) AT_CHECK([ovs-appctl time/stop]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 "ip actions=push:OXM_OF_IN_PORT[[0..31]],output:1,pop:OXM_OF_IPV4_SRC[[0..31]],output:2"]) # Try to pop from empty stack, and push and leave data to stack. AT_CHECK([ovs-ofctl add-flow br1 "ip actions=pop:OXM_OF_IPV4_DST[[0..31]],push:NXM_NX_REG1[[0..31]],LOCAL"]) ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' AT_CHECK([ovs-appctl time/warp 500], [0], [warped ]) OVS_WAIT_UNTIL([test `grep flow_add ovs-vswitchd.log | wc -l` -ge 1]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:1 br0: br0 65534/100: (dummy-internal) p2 2/2: (dummy) pbr0 1/none: (patch: peer=pbr1) br1: br1 65534/101: (dummy-internal) p3 3/3: (dummy) pbr1 1/none: (patch: peer=pbr0) ]) AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(100),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,frag=no), actions:101,set(ipv4(src=255.255.255.254)),2 ]) AT_CHECK([grep -e '|ofproto_dpif_xlate|WARN|' ovs-vswitchd.log | sed "s/^.*|WARN|//"], [0], [dnl stack underflow on bridge br1 while processing icmp,in_port=LOCAL,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 ]) OVS_VSWITCHD_STOP(["/stack underflow/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - port duration]) OVS_VSWITCHD_START([set Bridge br0 protocols=OpenFlow13]) add_of_ports br0 1 2 ovs-appctl time/stop ovs-appctl time/warp 10000 AT_CHECK([ovs-ofctl -O openflow13 dump-ports br0], [0], [stdout]) AT_CHECK([sed -n 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/p' stdout], [0], [dnl duration=?s duration=?s duration=?s ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - patch ports - meter (clone)]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 -- \ add-port br0 p1 -- set Interface p1 type=patch \ options:peer=p2 ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-port br1 p2 -- set Interface p2 type=patch \ options:peer=p1 -- \ add-port br1 p3 -- set Interface p3 type=dummy ofport_request=3]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br1 'meter=1 pktps stats bands=type=drop rate=2']) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 in_port=local,ip,actions=2,1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br1 in_port=1,ip,actions=meter:1,3]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=10.1.1.22,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=53295,dst=8080)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: clone(meter(0),3),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - patch ports - no additional clone]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 -- dnl add-port br0 p1 -- set Interface p1 type=patch dnl options:peer=p2 ofport_request=2 -- dnl add-br br1 -- dnl set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- dnl set bridge br1 datapath-type=dummy other-config:datapath-id=1234 dnl fail-mode=secure -- dnl add-port br1 p2 -- set Interface p2 type=patch dnl options:peer=p1 -- dnl add-port br1 p3 -- set Interface p3 type=dummy ofport_request=3]) AT_DATA([flows-br0.txt], [dnl priority=10,tcp,action=push:NXM_OF_IN_PORT[],resubmit(,65),pop:NXM_OF_IN_PORT[] table=65,priority=10,ip,in_port=p0,action=p1 ]) AT_DATA([flows-br1.txt], [dnl priority=100,in_port=p2,tcp,ct_state=-trk,action=ct(table=0,zone=1) priority=100,in_port=p2,tcp,ct_state=+trk+est,ct_zone=1,action=p3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows-br0.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br1 flows-br1.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 in_port=p0,tcp --ct-next 'trk,est' | dnl grep "Datapath actions:" | grep -q clone], [1], [], [], [ovs-appctl ofproto/trace br0 in_port=p0,tcp --ct-next 'trk,est']) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - offload - ovs-appctl dpif/offload/]) AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy]) AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: false Datapaths: dummy@ovs-dummy: dummy dummy_x - br0: port_no: 100 - br1: port_no: 101 - ovs-dummy: port_no: 0 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "dummy@ovs-dummy": { "priority": [[ "dummy", "dummy_x"]], "providers": { "dummy": { "ports": { "br0": { "port_no": 100}, "br1": { "port_no": 101}, "ovs-dummy": { "port_no": 0}}}, "dummy_x": { }}}, "enabled": false} ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - offload - priority order - warning]) AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [], [], [-- set Open_vSwitch . other_config:hw-offload-priority=none,dummy]) OVS_WAIT_UNTIL( [grep "hw-offload-priority configuration has an unknown type; none" \ ovs-vswitchd.log]) OVS_VSWITCHD_STOP( ["/hw-offload-priority configuration has an unknown type;/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - offload - priority order]) AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [], [], [-- set Open_vSwitch . other_config:hw-offload-priority=dummy_x,dummy]) AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: false Datapaths: dummy@ovs-dummy: dummy_x dummy - br0: port_no: 100 - br1: port_no: 101 - ovs-dummy: port_no: 0 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "dummy@ovs-dummy": { "priority": [[ "dummy_x", "dummy"]], "providers": { "dummy": { }, "dummy_x": { "ports": { "br0": { "port_no": 100}, "br1": { "port_no": 101}, "ovs-dummy": { "port_no": 0}}}}}, "enabled": false} ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - offload - port priority order]) AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-port br0 p1 -- \ set Interface p1 type=dummy ofport_request=1 -- \ set Interface p1 other_config:hw-offload-priority=dummy_x,dummy -- \ add-port br0 p2 -- \ set Interface p2 type=dummy ofport_request=2 -- \ set Interface p2 other_config:hw-offload-priority=none -- \ add-port br0 p3 -- \ set Interface p3 type=dummy ofport_request=3 -- \ set Interface p3 other_config:hw-offload-priority=dummy_x -- \ add-port br0 p4 -- \ set Interface p4 type=dummy ofport_request=4 -- \ set Interface p4 other_config:hw-offload-priority=dummy]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "dummy@ovs-dummy": { "priority": [[ "dummy", "dummy_x"]], "providers": { "dummy": { "ports": { "br0": { "port_no": 100}, "ovs-dummy": { "port_no": 0}, "p4": { "port_no": 4}}}, "dummy_x": { "ports": { "p1": { "port_no": 1}, "p3": { "port_no": 3}}}}}, "enabled": false} ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - offload - ovs-appctl dpctl/offload-stats-show]) AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-appctl dpctl/offload-stats-show], [0], [dnl HW Offload stats: dummy[[dummy@ovs-dummy]]: Offloaded port count: 3 dummy_x[[dummy@ovs-dummy]]: Offloaded port count: 0 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([ofproto-dpif -- megaflows]) AT_SETUP([ofproto-dpif megaflow - port classification]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - L2 classification]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - L3 classification]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=nw_dst,nw_src], [0], [ignore], []) AT_DATA([flows.txt], [dnl table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.4,proto=1,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - IPv6 classification]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=ipv6_dst,ipv6_src], [0], [ignore], []) AT_DATA([flows.txt], [dnl table=0 in_port=1,ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1,dst=2001:db8:3c4d:1:2:3:4:1,label=0,proto=99,tclass=0x70,hlimit=64,frag=no)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5,frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1/ffff:ffff:ffff:fffc::,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - L4 classification]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,icmp,icmp_type=8 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=1,frag=no),icmp(type=8), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([OFPROTO_DPIF_MEGAFLOW_NORMAL], [AT_SETUP([ofproto-dpif megaflow - normal$1]) OVS_VSWITCHD_START([], [], [], [m4_if([$1], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"])]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) func=`printf '%s_' "$1" | cut -c 4-` add_${func}of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP]) OFPROTO_DPIF_MEGAFLOW_NORMAL([]) OFPROTO_DPIF_MEGAFLOW_NORMAL([ - pmd]) AT_SETUP([ofproto-dpif megaflow - mpls]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 dl_src=50:54:00:00:00:09 actions=push_mpls:0x8847,2 table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout_keep_actions], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), actions:push_mpls(label=11,tc=3,ttl=64,bos=0,eth_type=0x8847),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x8847),mpls(label=11/0x0,tc=3/0,ttl=64/0x0,bos=1/1), actions:pop_mpls(eth_type=0x800),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP # CHECK_MEGAFLOW_NETFLOW(LOOPBACK_ADDR) m4_define([CHECK_MEGAFLOW_NETFLOW], [OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 dnl NetFlow configuration disables wildcarding relevant fields on_exit 'kill `cat test-netflow.pid`' AT_CHECK([ovstest test-netflow --log-file --detach --no-chdir --pidfile 0:$1 > netflow.log], [0], [], [ignore]) AT_CAPTURE_FILE([netflow.log]) PARSE_LISTENING_PORT([test-netflow.log], [NETFLOW_PORT]) ovs-vsctl \ set Bridge br0 netflow=@nf -- \ --id=@nf create NetFlow targets=\"$1:$NETFLOW_PORT\" \ engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0/0xfc,frag=no),icmp(type=8,code=0), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0/0xfc,frag=no),icmp(type=8,code=0), actions: ]) OVS_APP_EXIT_AND_WAIT([test-netflow]) OVS_VSWITCHD_STOP]) AT_SETUP([ofproto-dpif megaflow - netflow - IPv4 collector]) CHECK_MEGAFLOW_NETFLOW([127.0.0.1], [IPv4]) AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - netflow - IPv6 collector]) AT_SKIP_IF([test $HAVE_IPV6 = no]) CHECK_MEGAFLOW_NETFLOW([[[::1]]], [IPv6]) AT_CLEANUP m4_define([OFPROTO_DPIF_MEGAFLOW_NORMAL_ACB_BOND], [AT_SETUP([ofproto-dpif megaflow - normal, active-backup bonding - $1]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=$1 ofport_request=1 -- \ add-bond br0 bond0 p2 p3 bond_mode=active-backup -- \ set interface p2 type=$1 ofport_request=2 -- \ set interface p3 type=$1 ofport_request=3], [], [], [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP]) OFPROTO_DPIF_MEGAFLOW_NORMAL_ACB_BOND([dummy]) OFPROTO_DPIF_MEGAFLOW_NORMAL_ACB_BOND([dummy-pmd]) AT_SETUP([ofproto-dpif megaflow - normal, balance-slb bonding]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ add-bond br0 bond0 p2 p3 bond_mode=balance-slb -- \ set interface p2 type=dummy ofport_request=2 -- \ set interface p3 type=dummy ofport_request=3]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - normal, balance-tcp bonding]) # Create bond0 on br0 with members p0 and p1 # and bond1 on br1 with members p2 and p3 # with p0 patched to p2 and p1 patched to p3. OVS_VSWITCHD_START( [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p0 type=patch options:peer=p2 ofport_request=1 -- \ set interface p1 type=patch options:peer=p3 ofport_request=2 -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ other-config:lacp-time=fast \ other-config:bond-rebalance-interval=0 -- \ set interface p2 type=patch options:peer=p0 ofport_request=3 -- \ set interface p3 type=patch options:peer=p1 ofport_request=4 --]) AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK ]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 7 AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) ovs-appctl time/stop ovs-appctl time/warp 5000 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(7),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - bond rebalance interval reconfiguration]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl time/stop]) AT_CHECK([ovs-vsctl add-bond br0 bond0 p1 p2 bond_mode=balance-slb \ other-config:bond-rebalance-interval=60000 -- \ set interface p1 type=dummy ofport_request=1 -- \ set interface p2 type=dummy ofport_request=2]) # TEST_NEXT_REBALANCE([num]) # Test the next rebalance value is equal to a number. m4_define([TEST_NEXT_REBALANCE_EQ], [ AT_CHECK([ovs-appctl bond/show bond0 | grep 'next rebalance:'], [0], [stdout]) AT_CHECK([cat stdout], [0], [next rebalance: $1 ms ])]) TEST_NEXT_REBALANCE_EQ([60000]) AT_CHECK([ovs-appctl time/warp 50000], [0], [ignore]) TEST_NEXT_REBALANCE_EQ([10000]) # Decreasing the rebalance interval to something lower than the # already-consumed interval should make the next rebalance value negative, # i.e: balance will happen ASAP. AT_CHECK([ovs-vsctl set Port bond0 other-config:bond-rebalance-interval=10000]) TEST_NEXT_REBALANCE_EQ([-40000]) AT_CHECK([ovs-appctl time/warp 1], [0], [ignore]) TEST_NEXT_REBALANCE_EQ([10000]) AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore]) TEST_NEXT_REBALANCE_EQ([5000]) # Increasing the rebalance interval should offset the already consumed # interval. AT_CHECK([ovs-vsctl set Port bond0 other-config:bond-rebalance-interval=60000]) TEST_NEXT_REBALANCE_EQ([55000]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - resubmit port action]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip actions=resubmit(90) table=0 in_port=90,dl_src=50:54:00:00:00:09 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - resubmit table action]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip actions=resubmit(,1) table=1 dl_src=50:54:00:00:00:09 actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto= 1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - goto_table action]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip actions=goto_table(1) table=1 dl_src=50:54:00:00:00:09 actions=output(2) ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - mirroring, select_all]) AT_KEYWORDS([mirror mirrors mirroring]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - mirroring, select_vlan]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 3 ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3 AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0))']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=11,pcp=7/0x0),encap(eth_type(0x0800),ipv4(frag=no)), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - move action]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 ip,actions=move:NXM_OF_IP_SRC[[]]->NXM_NX_REG0[[]],resubmit(90) table=0 in_port=90 ip,actions=move:NXM_NX_REG0[[]]->NXM_NX_REG1[[]],resubmit(91) table=0 in_port=91 reg0=0x0a000002,actions=output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.2,frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.4,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - push action]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 ip,actions=push:NXM_OF_IP_SRC[[]],output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.2,frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.4,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - learning]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=load:2->NXM_NX_REG0[[0..15]],learn(table=1,priority=65535,NXM_OF_ETH_SRC[[]],NXM_OF_VLAN_TCI[[0..11]],output:NXM_NX_REG0[[0..15]]),output:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ovs-appctl time/stop # We send each packet twice because the first packet in each flow causes the # flow table to change and thus revalidations, which (depending on timing) # can keep a megaflow from being installed. The revalidations are done by # the second iteration, allowing the flows to be installed. for i in 1 2; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 100 done sleep 1 dnl The original flow is missing due to a revalidation. AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09),eth_type(0x0800),ipv4(frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - tunnels]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=gre \ options:remote_ip=1.1.1.1 ofport_request=2 options:key=flow]) AT_CHECK([ovs-vsctl add-port br0 p3 -- set Interface p3 type=dummy \ ofport_request=3]) AT_CHECK([ovs-vsctl add-port br0 p4 -- set Interface p4 type=gre \ options:remote_ip=1.1.1.2 options:tos=inherit options:ttl=inherit \ ofport_request=4 options:key=flow]) AT_DATA([flows.txt], [dnl in_port=1,actions=output(2) in_port=3,actions=output(4) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl ECN bits are always copied out, but don't use 0x3 (CE), since that dnl will cause the packet to be dropped. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0xfd,ttl=128,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0xfd,ttl=128,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0xfd/0x3,frag=no), actions: recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0x1,ttl=64,frag=no), actions: recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0xfd,ttl=128,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - dec_ttl]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=nw_dst,nw_src], [0], [ignore], []) AT_DATA([flows.txt], [dnl table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,frag=no), actions: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=10.0.0.4,proto=1,ttl=64,frag=no), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - set dl_dst]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=mod_dl_dst(50:54:00:00:00:0a),output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 dnl The first packet is essentially a no-op, as the new destination MAC is the dnl same as the original. The second entry actually updates the destination dnl MAC. AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions:set(eth(dst=50:54:00:00:00:0a)),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif megaflow - set dl_dst with match on dl_src]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=mod_dl_dst(50:54:00:00:00:0a),output(2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.6,dst=10.0.0.5,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) sleep 1 dnl The first packet is essentially a no-op, as the new destination MAC is the dnl same as the original. The second entry actually updates the destination dnl MAC. The last one must be dropped as it doesn't match with dl_src. AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(frag=no), actions:set(eth(dst=50:54:00:00:00:0a)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b),eth_type(0x0800),ipv4(frag=no), actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_define([OFPROTO_DPIF_MEGAFLOW_DISABLED], [AT_SETUP([ofproto-dpif megaflow - disabled$1]) OVS_VSWITCHD_START([], [], [], [m4_if([$1], [], [], [--dummy-numa="0,0,0,0,1,1,1,1"])]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl vlog/disable-rate-limit dpif]) func=`printf '%s_' "$1" | cut -c 4-` add_${func}of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1,ip,nw_dst=10.0.0.1 actions=output(2) table=0 in_port=1,ip,nw_dst=10.0.0.3 actions=drop ]) AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled ], []) AT_CHECK([ovs-appctl upcall/disable-ufid], [0], [Datapath dumping tersely using UFID disabled ], []) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg], [0], [], []) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) for i in 1 2 3 4; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) if [[ $i -eq 1 ]]; then sleep 1 fi done sleep 1 AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:2 recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:drop ]) AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_dump | grep 'packets:3'], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:318, used:0.0s, actions:2 recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:318, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP]) OFPROTO_DPIF_MEGAFLOW_DISABLED([]) OFPROTO_DPIF_MEGAFLOW_DISABLED([ - pmd]) AT_SETUP([ofproto-dpif - datapath port number change]) OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone]) add_of_ports br0 1 # Trace a flow that should output to p1. AT_CHECK([ovs-appctl ofproto/trace br0 in_port=LOCAL,dl_src=10:20:30:40:50:60], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) # Change p1's port number to 5. AT_CHECK([ovs-appctl dpif-dummy/change-port-number ovs-dummy p1 5]) # Trace a flow that should output to p1 in its new location. AT_CHECK([ovs-appctl ofproto/trace br0 in_port=LOCAL,dl_src=10:20:30:40:50:60], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 5 ]) OVS_VSWITCHD_STOP AT_CLEANUP # Tests the bundling with various bfd and cfm configurations. AT_SETUP([ofproto-dpif - bundle with variable bfd/cfm config]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \ add-bond br0 br0bond p0 p2 bond-mode=active-backup -- \ add-bond br1 br1bond p1 p3 bond-mode=active-backup -- \ set Interface p1 type=patch options:peer=p0 ofport_request=2 -- \ set Interface p3 type=patch options:peer=p2 ofport_request=4 -- \ set Interface p0 type=patch options:peer=p1 ofport_request=1 -- \ set Interface p2 type=patch options:peer=p3 ofport_request=3 -- \ set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 -- \ set Interface p0 cfm_mpid=1 -- \ set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500]) ovs-appctl time/stop # advance the clock to stablize everything. ovs-appctl time/warp 5000 100 # cfm/show should show 'recv' fault. AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl fault: recv ]) # bfd/show should show 'up'. AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl Local Session State: up Remote Session State: up Local Session State: up Remote Session State: up ]) # bond/show should show 'may-enable: true' for all members. AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl may_enable: true may_enable: true may_enable: true may_enable: true ]) # now disable the bfd on p1. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false]) # advance the clock to stablize everything. ovs-appctl time/warp 5000 100 # cfm/show should show 'recv' fault. AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl fault: recv ]) # bfd/show should show 'down'. AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl Local Session State: down Remote Session State: down ]) # bond/show should show 'may-enable: false' for p0. AT_CHECK([ovs-appctl bond/show br0bond | sed -n '/^.*may_enable:.*/p'], [0], [dnl may_enable: false may_enable: true ]) # now enable the bfd on p1 and disable bfd on p0. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true]) AT_CHECK([ovs-vsctl set Interface p0 bfd:enable=false]) # advance the clock to stablize everything. ovs-appctl time/warp 5000 100 # cfm/show should show 'recv' fault. AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl fault: recv ]) # bfd/show should show 'down'. AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl Local Session State: down Remote Session State: down ]) # bond/show should show 'may-enable: false' for p0 and p1. AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl may_enable: false may_enable: true may_enable: false may_enable: true ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ofproto-dpif-monitor 1]) OVS_VSWITCHD_START([add-port br0 p0 -- set interface p0 type=gre options:remote_ip=1.2.3.4]) # enable bfd on p0. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=true]) # check log. OVS_WAIT_UNTIL([grep "monitor thread created" ovs-vswitchd.log]) # disable bfd on p0. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=false]) # check log. OVS_WAIT_UNTIL([grep "monitor thread terminated" ovs-vswitchd.log]) AT_CHECK([sed -e '/^.*ofproto_dpif_monitor.*$/d' < ovs-vswitchd.log > tmp && ovs-appctl vlog/close && mv tmp ovs-vswitchd.log && ovs-appctl vlog/reopen]) # enable cfm on p0. AT_CHECK([ovs-vsctl set interface p0 cfm_mpid=10]) # check log. OVS_WAIT_UNTIL([grep "monitor thread created" ovs-vswitchd.log]) # disable cfm on p0. AT_CHECK([ovs-vsctl remove interface p0 cfm_mpid 10]) # check log. OVS_WAIT_UNTIL([grep "monitor thread terminated" ovs-vswitchd.log]) AT_CHECK([sed -e '/^.*ofproto_dpif_monitor.*$/d' < ovs-vswitchd.log > tmp && ovs-appctl vlog/close && mv tmp ovs-vswitchd.log && ovs-appctl vlog/reopen]) # enable both bfd and cfm on p0. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=true cfm_mpid=10]) # check log. OVS_WAIT_UNTIL([grep "monitor thread created" ovs-vswitchd.log]) # disable bfd on p0. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=false]) # check log, there should not be the log of thread terminated. AT_CHECK([sed -n "s/^.*|ofproto_dpif_monitor(monitor[[0-9]]*)|INFO|\(.* terminated\)$/\1/p" ovs-vswitchd.log], [0], [dnl ]) # reenable bfd on p0. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=true]) # check log, should still be on log of thread created. AT_CHECK([sed -n "s/^.*|ofproto_dpif_monitor(monitor[[0-9]]*)|INFO|\(.* created\)$/\1/p" ovs-vswitchd.log], [0], [dnl monitor thread created ]) # disable bfd and cfm together. AT_CHECK([ovs-vsctl set interface p0 bfd:enable=false -- remove interface p0 cfm_mpid 10]) # check log. OVS_WAIT_UNTIL([grep "monitor thread terminated" ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP # this test helps avoid the deadlock between the main thread and monitor thread. AT_SETUP([ofproto-dpif - ofproto-dpif-monitor 2]) OVS_VSWITCHD_START for i in `seq 1 199` do AT_CHECK([ovs-vsctl add-port br0 p$i -- set interface p$i type=gre options:remote_ip=1.2.3.4 options:key=$i bfd:enable=true]) done OVS_VSWITCHD_STOP AT_CLEANUP AT_BANNER([ofproto-dpif - flow translation resource limits]) dnl Resubmits to later tables do not count against the depth limit, so we dnl can do 99 of them even though the maximum depth is 64. AT_SETUP([ofproto-dpif - forward resubmit]) OVS_VSWITCHD_START (for i in `seq 0 99`; do j=`expr $i + 1` echo "table=$i, actions=resubmit(,$j)" done echo "table=100, actions=local") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'eth_dst=ff:ff:ff:ff:ff:ff'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 100 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Resubmits to the same or an earlier table count against the depth limit, dnl so only 64 of them are allowed. AT_SETUP([ofproto-dpif - backward resubmit]) OVS_VSWITCHD_START (echo "table=0, actions=resubmit(,66)" for i in `seq 2 66`; do j=`expr $i - 1` echo "table=$i, actions=resubmit(,$j)" done echo "table=1, actions=local") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'eth_dst=ff:ff:ff:ff:ff:ff'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Translation failed (Recursion too deep), packet is dropped. ]) AT_CHECK([grep -c 'over max translation depth 64' stdout], [0], [1 ]) OVS_VSWITCHD_STOP(["/resubmit actions recursed/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - infinite resubmit]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 actions=resubmit:1,resubmit:2,output:3]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'eth_dst=ff:ff:ff:ff:ff:ff'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Translation failed (Recursion too deep), packet is dropped. ]) AT_CHECK([grep -c 'over max translation depth 64' stdout], [0], [1 ]) OVS_VSWITCHD_STOP(["/resubmit actions recursed/d"]) AT_CLEANUP dnl Without using ofproto/trace, make sure the dnl ofproto trace is still logged AT_SETUP([ofproto-dpif - backward resubmit without trace]) OVS_VSWITCHD_START (echo "table=0, actions=resubmit(,66)" for i in `seq 2 66`; do j=`expr $i - 1` echo "table=$i, actions=resubmit(,$j)" done echo "table=1, actions=local") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl netdev-dummy/receive br0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)'], [0], [stdout]) OVS_WAIT_UNTIL([grep 'packet is dropped' ovs-vswitchd.log]) dnl make sure the full ofproto trace dump is present AT_CHECK([grep -c "^ *resubmit" ovs-vswitchd.log], [0], [66 ]) OVS_VSWITCHD_STOP(["/over max translation depth/d /ofproto_dpif_upcall/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - exponential resubmit chain]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 1 64`; do j=`expr $i + 1` echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done echo "in_port=65, actions=local") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'in_port=1'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Translation failed (Too many resubmits), packet is dropped. ]) AT_CHECK([grep -c 'over 4096 resubmit actions' stdout], [0], [1 ]) OVS_VSWITCHD_STOP(["/over.*resubmit actions/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - too many output actions]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 1 12`; do j=`expr $i + 1` echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done echo "in_port=13, actions=local,local,local,local,local,local,local,local") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'in_port=1'], [0], [stdout]) AT_CHECK([grep -c -e '- Uses action(s) not supported by datapath' stdout], [0], [1 ]) AT_CHECK([grep -c 'resubmits yielded over 64 kB of actions' stdout], [0], [1 ]) OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of actions/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - stack too deep]) OVS_VSWITCHD_START add_of_ports br0 1 (for i in `seq 1 12`; do j=`expr $i + 1` echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done push="push:NXM_NX_REG0[[]]" echo "in_port=13, actions=$push,$push,$push,$push,$push,$push,$push,$push") > flows AT_CHECK([ovs-ofctl add-flows br0 flows]) AT_CHECK([ovs-appctl -vpoll_loop:off ofproto/trace br0 'in_port=1'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Translation failed (Stack too deep), packet is dropped. ]) AT_CHECK([grep -c 'resubmits yielded over 64 kB of stack' stdout], [0], [1 ]) OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of stack/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif packet-out controller]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 'dl_dst=50:54:00:00:00:0a actions=controller']) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 CONTROLLER controller '50540000000a5054000000091234']) done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl dl_dst=50:54:00:00:00:0a actions=CONTROLLER:65535 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0], [0], [OFPST_TABLE reply (OF1.3) (xid=0x2): table 0: active=1, lookup=0, matched=0 table 1: active=0, lookup=0, matched=0 tables 2...253: ditto ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif packet-out controller (patch port)]) OVS_VSWITCHD_START( [-- \ add-port br0 p1 -- \ set interface p1 type=patch options:peer=p2 -- \ add-br br1 -- \ set bridge br1 datapath-type=dummy -- \ set bridge br1 fail-mode=secure -- \ set bridge br1 protocols='[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]' -- \ add-port br1 p2 -- \ set interface p2 type=patch options:peer=p1 --]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br1 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 CONTROLLER output:1 '50540000000a5054000000091234']) done OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0], [0], [dnl OFPST_TABLE reply (OF1.3) (xid=0x2): table 0: active=0, lookup=0, matched=0 tables 1...253: ditto ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br1], [0], [dnl OFPST_TABLE reply (OF1.3) (xid=0x2): table 0: active=0, lookup=3, matched=0 table 1: active=0, lookup=0, matched=0 tables 2...253: ditto ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif packet-out pipeline match field (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=8}->tun_metadata2"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=1,len=4}->tun_metadata3"]) AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=controller table=0,tun_id=3 actions=controller table=0,metadata=5 actions=controller table=0,reg0=1,reg4=2,reg8=3,reg12=5 actions=controller table=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_gbp_id=0x01,tun_gbp_flags=0x03 actions=controller table=0,tun_metadata3=0x11 actions=controller ]) AT_CHECK([ovs-ofctl -O OpenFlow15 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=1 packet=0001020304050010203040501111 actions=table"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=2,tunnel_id=3 packet=0001020304050010203040502222 actions=table"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=2,metadata=5 packet=0001020304050010203040503333 actions=table"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=2,reg0=1,reg4=2,reg8=3,reg12=5 packet=0001020304050010203040503333 actions=table"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=2,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_gbp_id=0x01,tun_gbp_flags=0x03 packet=0001020304050010203040503333 actions=table"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=2,tun_metadata2=0x22,tun_metadata3=0x11 packet=0001020304050010203040503333 actions=table"]) ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 in_port=1 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1111 OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 tun_id=0x3,in_port=2 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x2222 OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 metadata=0x5,in_port=2 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x3333 OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 reg0=0x1,reg4=0x2,reg8=0x3,reg12=0x5,in_port=2 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x3333 OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_gbp_id=1,tun_gbp_flags=0x3,in_port=2 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x3333 OFPT_PACKET_IN (OF1.5): cookie=0x0 total_len=14 tun_metadata2=0x22,tun_metadata3=0x11,in_port=2 (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x3333 OFPT_BARRIER_REPLY (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif packet-out goto_table]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 dl_dst=50:54:00:00:00:0a actions=goto_table(1) table=1 dl_dst=50:54:00:00:00:0a actions=controller ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=3, n_bytes=42, dl_dst=50:54:00:00:00:0a actions=goto_table:1 table=1, n_packets=3, n_bytes=42, dl_dst=50:54:00:00:00:0a actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.3): ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0], [0], [dnl OFPST_TABLE reply (OF1.3) (xid=0x2): table 0: active=1, lookup=3, matched=3 table 1: ditto table 2: active=0, lookup=0, matched=0 tables 3...253: ditto ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif packet-out table-miss (continue)]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 'table=1 dl_dst=50:54:00:00:00:0a actions=controller']) AT_CHECK([ovs-ofctl -O OpenFlow11 mod-table br0 all continue]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl table=1, n_packets=3, n_bytes=42, dl_dst=50:54:00:00:00:0a actions=CONTROLLER:65535 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0], [0], [dnl OFPST_TABLE reply (OF1.3) (xid=0x2): table 0: active=0, lookup=3, matched=0 table 1: active=1, lookup=3, matched=3 table 2: active=0, lookup=0, matched=0 tables 3...253: ditto ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif packet-out table meter drop]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1']) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 'in_port=1 action=meter:1,output:2']) ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000400080000 actions=resubmit(,0)" ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000400080000 actions=resubmit(,0)" # Check that vswitchd hasn't crashed by dumping the meter added above AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0 | ofctl_strip], [0], [dnl OFPST_METER_CONFIG reply (OF1.3): meter=1 pktps bands= type=drop rate=1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ICMPv6]) OVS_VSWITCHD_START add_of_ports br0 1 AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) ovs-appctl netdev-dummy/receive p1 '0060970769ea0000860580da86dd6000000000203afffe80000000000000020086fffe0580dafe80000000000000026097fffe0769ea870068bd00000000fe80000000000000026097fffe0769ea01010000860580da' OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via no_match) data_len=86 (unbuffered) icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00 icmp6_csum:68bd ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - ICMPv6 type match]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-ofctl add-flow br0 'icmp6,icmp_type=128,actions=2']) AT_CHECK([ovs-ofctl add-flow br0 'icmp6,icmp_type=129,actions=3']) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth(src=f2:49:6e:52:49:0b,dst=02:b7:d7:17:ff:72),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=128)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth(src=f2:49:6e:52:49:0b,dst=02:b7:d7:17:ff:72),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=128)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth(src=f2:49:6e:52:49:0b,dst=02:b7:d7:17:ff:72),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=129)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth(src=f2:49:6e:52:49:0b,dst=02:b7:d7:17:ff:72),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=129)']) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=128), actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=129), actions:3 ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=2, n_bytes=252, icmp6,icmp_type=128 actions=output:2 n_packets=2, n_bytes=252, icmp6,icmp_type=129 actions=output:3 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Neighbor Discovery set-field with checksum update]) OVS_VSWITCHD_START add_of_ports br0 1 AT_CHECK([ovs-ofctl add-flow br0 icmp6,icmpv6_type=135,action=set_field:fe80::1-\>nd_target,set_field:32:21:14:86:11:74-\>nd_sll,output:controller]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) ovs-appctl netdev-dummy/receive p1 '0060970769ea0000860580da86dd6000000000203afffe80000000000000020086fffe0580dafe80000000000000026097fffe0769ea870068bd00000000fe80000000000000026097fffe0769ea01010000860580da' OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via action) data_len=86 (unbuffered) icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,icmp_type=135,icmp_code=0,nd_target=fe80::1,nd_sll=32:21:14:86:11:74,nd_tll=00:00:00:00:00:00 icmp6_csum:19d3 ]) OVS_VSWITCHD_STOP AT_CLEANUP # Tests the exact match of CFI bit in installed datapath flows matching VLAN. AT_SETUP([ofproto-dpif - vlan matching]) OVS_VSWITCHD_START( [add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 "vlan_tci=0x000a/0x0fff,action=output:local"]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=10,pcp=0),encap(eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0))']) OVS_WAIT_UNTIL([grep flow_add: ovs-vswitchd.log]) AT_CHECK([grep 'in_port([[1]])' ovs-vswitchd.log | filter_flow_install | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=10),encap(eth_type(0x0800),ipv4(frag=no)), actions: ]) OVS_VSWITCHD_STOP AT_CLEANUP # Tests in place modification of installed datapath flows. AT_SETUP([ofproto-dpif - in place modification]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:3,output:local]) ovs-appctl vlog/set PATTERN:ANY:'%c|%p|%m' ovs-appctl time/stop AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)']) # Wait for the flow setup to be done. OVS_WAIT_UNTIL([grep 'flow_add:' ovs-vswitchd.log]) for i in 1 2; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:2, bytes:28, used:0.0s, actions:push_vlan(vid=3,pcp=0),100 ]) AT_CHECK([ovs-ofctl add-flow br0 priority=60000,in_port=1,actions=mod_vlan_vid:4,output:local]) ovs-appctl time/warp 500 ovs-appctl time/warp 500 for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:5, bytes:70, used:0.0s, actions:push_vlan(vid=4,pcp=0),100 ]) AT_CHECK([grep 'modify' ovs-vswitchd.log | strip_ufid ], [0], [dnl dpif|DBG|dummy@ovs-dummy: put[[modify]] recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:push_vlan(vid=4,pcp=0),100 ]) OVS_VSWITCHD_STOP AT_CLEANUP # Tests in place modification of installed datapath flows with vlans. AT_SETUP([ofproto-dpif - in place modification (vlan)]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) # Always drop misses AT_CHECK([ovs-ofctl -O OpenFlow11 mod-table br0 all drop]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=output:local]) ovs-appctl vlog/set PATTERN:ANY:'%c|%p|%m' ovs-appctl time/stop AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)']) # Check that a correct datapath flow is created. OVS_WAIT_UNTIL([grep 'flow_add:' ovs-vswitchd.log]) for i in 1 2; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:2, bytes:28, used:0.0s, actions:100 ]) # Delete the flow. Then check that the datapath flow is modified to # drop the packets. A modified flow inherits the stats, a new # datapath flow would start from sero. AT_CHECK([ovs-ofctl del-flows br0]) ovs-appctl time/warp 500 ovs-appctl time/warp 500 for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:5, bytes:70, used:0.0s, actions:drop ]) # Add a flow that matches the non-presence of a vlan tag, and check # that the datapath flow is modified accordingly. AT_CHECK([ovs-ofctl add-flow br0 in_port=1,vlan_tci=0x0000/0x1fff,actions=output:local]) ovs-appctl time/warp 500 ovs-appctl time/warp 500 for i in 1 2 3; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:8, bytes:112, used:0.0s, actions:100 ]) # Check that VLAN packets will not hit the same datapath megaflow. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x1234))']) OVS_WAIT_UNTIL([grep 'flow_add:.*vlan(vid=99' ovs-vswitchd.log]) for i in 1 2; do ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x1234))' done AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:8, bytes:112, used:0.0s, actions:100 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), packets:2, bytes:36, used:0.0s, actions:drop ]) # Check that the new flow matches the CFI bit, while both vid and pcp # are wildcarded. AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | strip_ufid ], [0], [dnl dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), actions:100 dpif|DBG|dummy@ovs-dummy: put[[modify]] recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:drop dpif|DBG|dummy@ovs-dummy: put[[modify]] recirc_id(0),dp_hash(0/0),skb_priority(0/0),in_port(1),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100 dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - trace (unchanged)]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1'], [0], [stdout]) AT_CHECK([grep "Final flow:" stdout], [0], [Final flow: unchanged ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100)'], [0], [stdout]) AT_CHECK([grep "Final flow:" stdout], [0], [Final flow: unchanged ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - controller]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new UDP connections on p1 for dst port 2, drop everything else. AT_DATA([flows.txt], [dnl dnl Table 0 dnl Store zone in reg4 and packet direction in reg3 (IN=1, OUT=2). dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,ip,action=set_field:1->reg4,set_field:1->reg3,ct(zone=NXM_NX_REG4[[0..15]],table=1) table=0,priority=10,in_port=2,ip,action=set_field:1->reg4,set_field:2->reg3,ct(zone=NXM_NX_REG4[[0..15]],table=1) table=0,priority=1,action=drop dnl dnl Pass tracked traffic through ACL, drop everything else. dnl Non-REPLY/RELATED packets get the ACL lookup with the packet headers dnl in the actual packet direction in reg0 (IN=1, OUT=2). REPLY packets dnl get the ACL lookup using the conntrack tuple and the inverted direction. dnl RELATED packets get ACL lookup using the conntrack tuple in the direction dnl of the parent connection, as storted in ct_mark. dnl dnl Incoming non-related packet in the original direction (ACL IN) table=1 reg3=1, ip, ct_state=-rel-rpl+trk-inv action=set_field:1->reg0,resubmit(,3),goto_table:5 dnl Incoming non-related reply packet (CT ACL OUT) table=1 reg3=1, ip, ct_state=-rel+rpl+trk-inv action=set_field:2->reg0,resubmit(,3,ct),goto_table:4 dnl Outgoing non-related packet (ACL OUT) table=1 reg3=2, ip, ct_state=-rel-rpl+trk-inv action=set_field:2->reg0,resubmit(,3),goto_table:5 dnl Outgoing non-related reply packet (CT ACL IN) table=1 reg3=2, ip, ct_state=-rel+rpl+trk-inv action=set_field:1->reg0,resubmit(,3,ct),goto_table:4 dnl dnl Related packet (CT ACL in the direction of the parent connection.) table=1 ip, ct_state=+rel+trk-inv, action=move:NXM_NX_CT_MARK[[]]->NXM_NX_REG0[[]],resubmit(,3,ct),goto_table:4 dnl Drop everything else. table=1 priority=0, action=drop dnl dnl "ACL table" dnl dnl Stateful accept (1->reg2) all incoming (reg0=1) IP connections with dnl UDP destination port '2'. Store rule ID (1234) in reg1, verdict dnl in reg2. table=3 priority=10, reg0=1, udp, udp_dst=2 action=set_field:1234->reg1,set_field:1->reg2 dnl Stateless drop (0->reg2) everything else in both directions. (Rule ID: 1235) table=3 priority=0, action=set_field:1235->reg1,set_field:0->reg2 dnl dnl Re-process stateful traffic that was not accepted by a stateful rule as dnl normal traffic in the current direction. This should also delete the dnl now stale conntrack state, so that new state can be created in it's place. dnl dnl Stateful accepts go to next table. table=4 priority=100 reg2=1, action=goto_table:5 dnl Everything else is reprocessed disregarding the CT state, using the actual dnl packet direction. table=4 priority=0 action=move:NXM_NX_REG3[[]]->NXM_NX_REG0[[]],resubmit(,3),goto_table:5 dnl dnl "ACL verdict processing table." dnl dnl Handle stateful (reg2=1) / stateless (reg2=2) accepts and drops (reg2=0) dnl dnl Drop all non-accepted packets. table=5 reg2=0 priority=1000 action=drop dnl Commit new non-related IP connections. table=5 priority=10 reg2=1 ct_state=+new-rel, ip, action=ct(zone=NXM_NX_REG4[[0..15]],commit,exec(move:NXM_NX_REG3[[0..31]]->NXM_NX_CT_MARK[[0..31]],move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl Commit new related connections in either direction, which inherit the mark dnl (the direction of the original direction parent tuple) from the parent dnl connection. table=5 priority=10 reg2=1 ct_state=+new+rel, ip, action=ct(zone=NXM_NX_REG4[[0..15]],commit,exec(move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl Forward everything else, including stateless accepts. table=5 priority=0 action=goto_table:6 dnl dnl "Forwarding table" dnl table=6 action=controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x1,reg4=0x1,in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_zone=1,ct_mark=0x1,ct_label=0x4d2000000000000000000000000,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x2,reg4=0x1,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl OK, now start a second connection from port 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=3,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=3)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We should see both packets AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x1,reg4=0x1,in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=3,tp_dst=2 udp_csum:551 dnl NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_zone=1,ct_mark=0x1,ct_label=0x4d2000000000000000000000000,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=2,ip,reg0=0x1,reg1=0x4d2,reg2=0x1,reg3=0x2,reg4=0x1,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=3 udp_csum:551 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - force commit]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2, but not on p2->p1. AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp,action=ct(commit),controller table=0,priority=10,in_port=2,udp,action=ct(table=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+est,udp,action=ct(force,commit),controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. dnl Note that the first packet doesn't have the ct_state bits set. This dnl happens because the ct_state field is available only after recirc. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl OK, now start a second connection from port 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=3,dst=4)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=4,dst=3)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We should see both packets dnl Note that the first packet doesn't have the ct_state bits set. This dnl happens because the ct_state field is available only after recirc. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=3,tp_dst=4 udp_csum:54f dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=4,tp_dst=3 udp_csum:54f ]) dnl dnl Check that the directionality has been changed by force commit. dnl AT_CHECK([ovs-appctl dpctl/dump-conntrack | sort], [], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2) udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=4,dport=3),reply=(src=10.1.1.1,dst=10.1.1.2,sport=3,dport=4) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - related nat]) OVS_VSWITCHD_START add_of_ports --pcap br0 1 2 AT_DATA([flows.txt], [dnl table=0,priority=100,arp,action=normal table=0,priority=10,ip,in_port=1,udp,action=ct(commit,table=1,nat(dst=1.2.3.4:10000-10000)) table=0,priority=10,ip,in_port=2,action=ct(nat,table=1) table=0,priority=1,action=drop table=1,priority=10,in_port=1,ct_state=+trk,action=2 table=1,priority=10,in_port=2,ct_state=+trk,action=1 table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl 1. Send and UDP packet to port 5555. packet1=c6f94ecb72dbe64c473528c9080045000021317040004011b138ac100001ac100002a28e15b3000d20966369616f0a AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=$packet1 actions=resubmit(,0)"]) dnl 2. Send an fragmented ICMP related reply from 1.2.3.4:10000. packet2=e64c473528c9c6f94ecb72db080045c000382e87000040019b6701020304ac1000010303ad2d000000004500001c317020004011794aac10000101020304a28e2710000d8623 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=$packet2 actions=resubmit(,0)"]) dnl 3. Check that the inner packet is translated. packet3=e64c473528c9c6f94ecb72db080045c000382e8700004001f35aac100002ac1000010303553a000000004500001c317020004011d13dac100001ac100002a28e15b3000def73 OVS_WAIT_UNTIL([ovs-pcap p1-tx.pcap | grep -q "$packet3"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - fragmentation nat]) OVS_VSWITCHD_START add_of_ports --pcap br0 1 2 AT_DATA([flows.txt], [dnl table=0,priority=10,ip,in_port=1,udp,action=ct(commit,table=1,nat(dst=1.2.3.4)) table=1,priority=10,ip,in_port=1,udp,action=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Set min frag size. AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 400], [], [dnl setting minimum fragment size successful ]) dnl First UDP fragment. AT_CHECK(dnl [ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=]dnl [c6f94ecb72dbe64c473528c9080045000204317020004011cf55ac100001ac100002a28e15b3]dnl [01fc31d741414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [4141414141414141414141414141414141414141414141414141414141414141414141414141]dnl [414141414141414141414141414141414141414141414141414141414141414141414141]dnl [ actions=resubmit(,0)"]) dnl Second UDP fragment. AT_CHECK(dnl [ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=]dnl [c6f94ecb72dbe64c473528c90800450000203170003e4011f0fbac100001ac10000241414141]dnl [4141414141414141]dnl [ actions=resubmit(,0)"]) dnl Validate that both IP headers now have the correct IP address and dnl the UDP header has the correct checksum. OVS_WAIT_UNTIL([ovs-pcap p2-tx.pcap | grep -q "450002043170200040117762ac10000101020304a28e15b301fcd9e3"]) OVS_WAIT_UNTIL([ovs-pcap p2-tx.pcap | grep -q "450000203170003e40119908ac10000101020304"]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ipv6]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2, but not on p2->p1. AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp6,action=ct(commit,zone=0),controller table=0,priority=10,in_port=2,udp6,action=ct(table=1,zone=0) table=0,priority=1,action=drop dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+trk+est-new,udp6,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x86dd),ipv6(src=2001:db8::2,dst=2001:db8::1,label=0,proto=17,tclass=0x70,hlimit=128,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=2001:db8::2,label=0,proto=17,tclass=0x70,hlimit=128,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x86dd),ipv6(src=2001:db8::2,dst=2001:db8::1,label=0,proto=17,tclass=0x70,hlimit=128,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. dnl Note that the first packet doesn't have the ct_state bits set. This dnl happens because the ct_state field is available only after recirc. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=126 in_port=1 (via action) data_len=126 (unbuffered) udp6,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,ipv6_src=2001:db8::1,ipv6_dst=2001:db8::2,ipv6_label=0x00000,nw_tos=112,nw_ecn=0,nw_ttl=128,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:bfe2 NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=126 ct_state=est|rpl|trk,ct_ipv6_src=2001:db8::1,ct_ipv6_dst=2001:db8::2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ipv6,in_port=2 (via action) data_len=126 (unbuffered) udp6,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,ipv6_src=2001:db8::2,ipv6_dst=2001:db8::1,ipv6_label=0x00000,nw_tos=112,nw_ecn=0,nw_ttl=128,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:bfe2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - output action]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl The flows are in two separate tables for two reasons: dnl * To make the pipeline more clear. dnl * To make megaflows more consistent (we check megaflows below). The dnl unwildcarding in megaflows depends on the internal ordering of the dnl subtables, which are sorted using the system qsort(). qsort() dnl is provided by libc and may or may not be stable, so we can't rely dnl on that. By having separate tables we have more control over which dnl subtables are visited, meaning consistent megaflows. dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp,action=ct(commit,zone=0),2 table=0,priority=10,in_port=2,udp,action=ct(table=1,zone=0) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+trk+est-new,udp,action=1 table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a second connection from port 1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) AT_CHECK([cat ovs-vswitchd.log | strip_ufid | filter_flow_install], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:ct(commit),2 recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:ct,recirc(0x1) recirc_id(0x1),in_port(2),ct_state(+new-est+trk),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:drop recirc_id(0x1),in_port(2),ct_state(-new+est+trk),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - expiration]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp,action=ct(commit,zone=0) table=0,priority=10,in_port=2,udp,action=ct(table=1,zone=0) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+trk+est-new,udp,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl time/stop]) dnl Start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) ovs-appctl time/warp 100000 dnl Now try another reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 3]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. Only one reply must be there AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 dnl OFPT_ECHO_REQUEST (xid=0x0): 0 bytes of payload ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - untrackable traffic]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) AT_DATA([flows.txt], [dnl ipv6,ct_state=-trk,action=ct(table=0,zone=0) ct_state=+trk,action=controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl time/stop]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '0060970769ea0000860580da86dd6000000000203afffe80000000000000020086fffe0580dafe80000000000000026097fffe0769ea870068bd00000000fe80000000000000026097fffe0769ea01010000860580da']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 1]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 ct_state=inv|trk,ipv6,in_port=2 (via action) data_len=86 (unbuffered) icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00 icmp6_csum:68bd ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - zones]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2 or p3->p4. dnl Allow only established connections p2->p1 and p4->p3 dnl p1,p2 and p3,p4 are on different zones AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp,action=ct(commit,zone=0),controller table=0,priority=10,in_port=2,udp,action=ct(table=1,zone=0) table=0,priority=10,in_port=3,udp,action=ct(commit,zone=1),controller table=0,priority=10,in_port=4,udp,action=ct(table=1,zone=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+trk+est-new,udp,action=controller table=1,priority=10,in_port=4,ct_state=+trk+est-new,udp,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Basic "only established" test on ports 1,2 AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl Now use the same 5-tuples but on ports 3,4 AT_CHECK([ovs-appctl netdev-dummy/receive p4 'in_port(4),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p4 'in_port(4),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 8]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets (for each zone), not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=3 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_zone=1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=4 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - recirc,commit]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,udp,action=ct(table=1,zone=0) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=1,ct_state=+trk+new,udp,action=ct(commit,zone=0),controller table=1,priority=10,ct_state=+trk+est,udp,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ICMP related]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,ip,in_port=1,udp,action=ct(commit,table=1) table=0,priority=10,ip,in_port=2,action=ct(table=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=1,ct_state=+trk,action=controller table=1,priority=10,in_port=2,ct_state=+trk-inv-new,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl 1. Send an ICMP port unreach reply for port 8738, without any previous request AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 ct\(table=1\) 'f64c473528c9c6f54ecb72db080045c0003d2e8700004001f351ac100004ac1000030303553f0000000045000021317040004011b138ac100003ac10000411112222000da5a06369616f0a']) dnl 2. Send and UDP packet to port 5555 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 1 ct\(commit,table=1\) 'c6f94ecb72dbe64c473528c9080045000021317040004011b138ac100001ac100002a28e15b3000d20966369616f0a']) dnl 3. Send an ICMP port unreach reply for port 5555, related to the first packet AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 ct\(table=1\) 'e64c473528c9c6f94ecb72db080045c0003d2e8700004001f355ac100002ac1000010303553f0000000045000021317040004011b138ac100001ac100002a28e15b3000d20966369616f0a']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the first and the last packet AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,ip,in_port=1 (via action) data_len=47 (unbuffered) udp,vlan_tci=0x0000,dl_src=e6:4c:47:35:28:c9,dl_dst=c6:f9:4e:cb:72:db,nw_src=172.16.0.1,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=41614,tp_dst=5555 udp_csum:2096 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,ip,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f9:4e:cb:72:db,dl_dst=e6:4c:47:35:28:c9,nw_src=172.16.0.2,nw_dst=172.16.0.1,nw_tos=192,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=3,icmp_code=3 icmp_csum:553f ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ct_mark]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,arp,action=normal table=0,ip,in_port=1,udp,tp_src=1,action=ct(commit,exec(set_field:1->ct_mark)),controller table=0,ip,in_port=1,udp,tp_src=3,action=ct(commit,exec(set_field:3->ct_mark)),controller table=0,ip,in_port=1,udp,tp_src=5,action=ct(commit,exec(set_field:5->ct_mark)),controller table=0,ip,in_port=2,actions=ct(table=1) table=0,priority=0,action=drop dnl dnl Table 1 dnl table=1,priority=100,ct_state=+trk+rpl,ct_mark=0/4,actions=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=3,dst=4)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=5,dst=6)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=4,dst=3)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=6,dst=5)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 10]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=3,tp_dst=4 udp_csum:54f dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=5,tp_dst=6 udp_csum:54b dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_mark=0x1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_mark=0x3,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=4,tp_dst=3 udp_csum:54f ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ct_label]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,arp,action=normal table=0,ip,in_port=1,udp,tp_src=1,action=ct(commit,exec(set_field:000000000000000001->ct_label)) table=0,ip,in_port=1,udp,tp_src=3,action=ct(commit,exec(set_field:000000000000000002->ct_label)) table=0,ip,in_port=2,actions=ct(table=1) dnl dnl Table 1 dnl table=1,priority=10,ct_state=+trk+rpl,actions=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=3,dst=4)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=4,dst=3)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_label=0x1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_label=0x2,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=4,tp_dst=3 udp_csum:54f ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ct_label datapath flow]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl The flows are in two separate tables for two reasons: dnl * To make the pipeline more clear. dnl * To make megaflows more consistent (we check megaflows below). The dnl unwildcarding in megaflows depends on the internal ordering of the dnl subtables, which are sorted using the system qsort(). qsort() dnl is provided by libc and may or may not be stable, so we can't rely dnl on that. By having separate tables we have more control over which dnl subtables are visited, meaning consistent megaflows. dnl dnl Table 0 dnl table=0,arp,action=normal table=0,ip,in_port=1,udp,tp_src=1,action=ct(commit,exec(set_field:1->ct_label)),2 table=0,ip,in_port=2,actions=ct(table=1) table=0,priority=0,action=drop dnl dnl Table 1 dnl table=1,priority=10,ct_state=+trk+rpl,ct_label=0x1,actions=1 table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) # Give time for logs to appear. ovs-appctl revalidator/wait AT_CHECK([cat ovs-vswitchd.log | strip_ufid | filter_flow_install], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no),udp(src=1), actions:ct(commit,label=0x1),2 recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:ct,recirc(0x1) recirc_id(0x1),in_port(2),ct_state(+rpl+trk),ct_label(0x1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - no output]) OVS_VSWITCHD_START add_of_ports br0 1 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl in_port=1,udp,action=ct(commit,zone=0) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([cat ovs-vswitchd.log | strip_ufid | filter_flow_install], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:ct(commit) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - tcp port reuse]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,ip,action=ct(commit,table=1) table=0,priority=10,in_port=2,ip,action=ct(table=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl dnl The following two flows are separated to explicitly count the packets dnl that create a new connection table=1,priority=100,cookie=0x1,in_port=1,ip,ct_state=+trk+new-inv-rpl,action=2 table=1,priority=100,in_port=1,ip,ct_state=+trk-new-inv-rpl,action=2 dnl table=1,priority=100,in_port=2,ip,ct_state=+trk+est+rpl-new-inv,action=1 table=1,ip,ct_state=+trk+inv,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a50540000000908004500002ca4e5400040067fe20a0101010a0101020001000259b5d93f0000000060027210dd190000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a08004500002c00004000400624c80a0101020a010101000200017c35468459b5d940601272101a4f0000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e6400040067fe50a0101010a0101020001000259b5d9407c35468550107210320c0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000303b5f40004006e9640a0101020a010101000200017c35468559b5d9405018721074c200007061796c6f61640a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e7400040067fe40a0101010a0101020001000259b5d9407c35468d5010721032040000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6040004006e96b0a0101020a010101000200017c35468d59b5d9405011721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e8400040067fe30a0101010a0101020001000259b5d9407c35468e5010721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e9400040067fe20a0101010a0101020001000259b5d9407c35468e5011721032020000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6140004006e96a0a0101020a010101000200017c35468e59b5d9415010721032020000']) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl dnl Only one new connection n_packets=1 ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a50540000000908004500002cc0a74000400664200a0101010a010102000100025b7dbf1f0000000060027210f5710000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a08004500002c00004000400624c80a0101020a010101000200017c36468f5b7dbf2060127210329b0000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028c0a84000400664230a0101010a010102000100025b7dbf207c364690501072104a580000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a080045000030392840004006eb9b0a0101020a010101000200017c3646905b7dbf20501872108d0e00007061796c6f61640a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028c0a94000400664220a0101010a010102000100025b7dbf207c364698501072104a500000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a080045000028392940004006eba20a0101020a010101000200017c3646985b7dbf20501172104a4f0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028c0aa4000400664210a0101010a010102000100025b7dbf207c364699501072104a4f0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028c0ab4000400664200a0101010a010102000100025b7dbf207c364699501172104a4e0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a080045000028392a40004006eba10a0101020a010101000200017c3646995b7dbf21501072104a4e0000']) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl dnl Two new connections n_packets=2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - tcp pick up]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,tcp,action=ct(commit,table=1) table=0,priority=10,in_port=2,tcp,action=ct(table=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,cookie=0x1,ip,ct_state=+trk+inv,action=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl dnl No dropped packets n_packets=0 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl The first two packets (SYN, SYN|ACK) are commented out. We're making dnl sure that the connection tracker is able to pick up already established dnl connections that use window scaling. dnl dnl AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000030fc2540004006289e0a0101020a01010100020001396bb359000000007002008080cc0000020405b401030307']) dnl AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a50540000000908004500003000004000400624c40a0101010a010102000100028cadbdb3396bb35a70120080365a0000020405b401030307']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000028fc264000400628a50a0101020a01010100020001396bb35a8cadbdb45010000a629b0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000029fc274000400628a30a0101020a01010100020001396bb35a8cadbdb45018000a589200000a']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a505400000009080045000028f2c84000400632030a0101010a010102000100028cadbdb4396bb35b5010000a629a0000']) # dnl Check that the protocol state moved to established after the pickup AT_CHECK([ovs-appctl dpctl/dump-conntrack], [0], [dnl tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),protoinfo=(state=ESTABLISHED) ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a08004500022afc284000400626a10a0101020a01010100020001396bb35b8cadbdb45018000a941f0000 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 dnl 6666666666666666666666666666666666666666666666666666660a']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a505400000009080045000028f2c94000400632020a0101010a010102000100028cadbdb4396bb55d5010000a60980000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a5054000000090800450001fdf2ca40004006302c0a0101010a010102000100028cadbdb4396bb55d5018000aa60c0000 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565 dnl 6565656565656565656565656565656565656565656565656565656565656565656565650a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000028fc294000400628a20a0101020a01010100020001396bb55d8cadbf895010000a5ec30000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000028fc2a4000400628a10a0101020a01010100020001396bb55d8cadbf895011000a5ec20000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a505400000009080045000028f2cb4000400632000a0101010a010102000100028cadbf89396bb55e5010000a5ec20000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000a505400000009080045000028f2cc4000400631ff0a0101010a010102000100028cadbf89396bb55e5011000a5ec10000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000950540000000a080045000028258e40004006ff3d0a0101020a01010100020001396bb55e8cadbf8a5010000a5ec10000']) AT_CHECK([ovs-appctl revalidator/purge]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl dnl No dropped packets n_packets=0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - disable tcp sequence checking]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=10,in_port=1,ip,action=ct(commit,table=1) table=0,priority=10,in_port=2,ip,action=ct(table=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl dnl The following two flows are separated to explicitly count the packets dnl that create a new connection table=1,priority=100,cookie=0x1,in_port=1,ip,ct_state=+trk+new-inv-rpl,action=2 table=1,priority=100,cookie=0x2,in_port=1,ip,ct_state=+trk-new-inv-rpl,action=2 dnl table=1,priority=100,cookie=0x3,in_port=2,ip,ct_state=+trk+est+rpl-new-inv,action=1 table=1,cookie=0x4,ip,ct_state=+trk+inv,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Send 9 packets; one packet will be marked invalid. AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a50540000000908004500002ca4e5400040067fe20a0101010a0101020001000259b5d93f0000000060027210dd190000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a08004500002c00004000400624c80a0101020a010101000200017c35468459b5d940601272101a4f0000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e6400040067fe50a0101010a0101020001000259b5d9407c35468550107210320c0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000303b5f40004006e9640a0101020a010101000200010000000000000000501872106a7300007061796c6f61640a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e7400040067fe40a0101010a0101020001000259b5d9407c35468d5010721032040000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6040004006e96b0a0101020a010101000200017c35468d59b5d9405011721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e8400040067fe30a0101010a0101020001000259b5d9407c35468e5010721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e9400040067fe20a0101010a0101020001000259b5d9407c35468e5011721032020000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6140004006e96a0a0101020a010101000200017c35468e59b5d9415010721032020000']) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=1 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x2 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=4 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x3 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=3 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x4 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=1 ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) AT_CHECK([ovs-appctl dpctl/ct-get-tcp-seq-chk], [], [dnl TCP sequence checking: enabled ]) AT_CHECK([ovs-appctl dpctl/ct-disable-tcp-seq-chk], [], [dnl disabling TCP sequence checking successful ]) AT_CHECK([ovs-appctl dpctl/ct-get-tcp-seq-chk], [], [dnl TCP sequence checking: disabled ]) dnl Send exactly the same 9 packets to confirm no additional packets are marked invalid. AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a50540000000908004500002ca4e5400040067fe20a0101010a0101020001000259b5d93f0000000060027210dd190000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a08004500002c00004000400624c80a0101020a010101000200017c35468459b5d940601272101a4f0000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e6400040067fe50a0101010a0101020001000259b5d9407c35468550107210320c0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000303b5f40004006e9640a0101020a010101000200010000000000000000501872106a7300007061796c6f61640a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e7400040067fe40a0101010a0101020001000259b5d9407c35468d5010721032040000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6040004006e96b0a0101020a010101000200017c35468d59b5d9405011721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e8400040067fe30a0101010a0101020001000259b5d9407c35468e5010721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e9400040067fe20a0101010a0101020001000259b5d9407c35468e5011721032020000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6140004006e96a0a0101020a010101000200017c35468e59b5d9415010721032020000']) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=2 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x2 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=8 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x3 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=7 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x4 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=1 ]) AT_CHECK([ovs-appctl dpctl/ct-enable-tcp-seq-chk], [], [dnl enabling TCP sequence checking successful ]) AT_CHECK([ovs-appctl dpctl/ct-get-tcp-seq-chk], [], [dnl TCP sequence checking: enabled ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Send exactly the same 9 packets after disabling TCP sequence checking to dnl confirm one more packet is marked invalid. AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a50540000000908004500002ca4e5400040067fe20a0101010a0101020001000259b5d93f0000000060027210dd190000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a08004500002c00004000400624c80a0101020a010101000200017c35468459b5d940601272101a4f0000020405b4']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e6400040067fe50a0101010a0101020001000259b5d9407c35468550107210320c0000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000303b5f40004006e9640a0101020a010101000200010000000000000000501872106a7300007061796c6f61640a']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e7400040067fe40a0101010a0101020001000259b5d9407c35468d5010721032040000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6040004006e96b0a0101020a010101000200017c35468d59b5d9405011721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e8400040067fe30a0101010a0101020001000259b5d9407c35468e5010721032030000']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50540000000a505400000009080045000028a4e9400040067fe20a0101010a0101020001000259b5d9407c35468e5011721032020000']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 '50540000000950540000000a0800450000283b6140004006e96a0a0101020a010101000200017c35468e59b5d9415010721032020000']) AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x1 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=3 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x2 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=12 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x3 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=10 ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep cookie=0x4 | grep -o "n_packets=[[0-9]]*"], [0], [dnl n_packets=2 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is a truncated version of "ofproto-dpif - conntrack - controller", dnl with extra send-to-controller actions following ct_clear to show that dnl the connection tracking data has been cleared. AT_SETUP([ofproto-dpif - conntrack - ct_clear]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2, but not on p2->p1. AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,in_port=1,udp,action=ct(commit,zone=0),controller,ct_clear,controller table=0,priority=10,in_port=2,udp,action=ct(table=1,zone=0) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=2,ct_state=+trk+est-new,udp,action=controller,ct_clear,controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) dnl Now try a reply from port 2. AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 8]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. dnl Note that the first packet doesn't have the ct_state bits set. This dnl happens because the ct_state field is available only after recirc. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) dnl The next test verifies that ct_clear at the datapath only gets executed dnl if conntrack information is present. AT_DATA([flows.txt], [dnl table=0 in_port=1 actions=ct_clear,ct_clear,ct_clear,p2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=p1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) AT_DATA([flows.txt], [dnl table=0 in_port=1 ip actions=ct_clear,ct(table=1) table=1 in_port=1 actions=ct_clear,ct_clear,goto_table:2 table=2 in_port=1 actions=ct_clear,p2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=p1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2'], [0], [stdout]) AT_CHECK([grep Datapath stdout | sed 's/recirc(.*)/recirc(X)/'], [0], [Datapath actions: ct,recirc(X) Datapath actions: ct_clear,2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - match masked ct fields]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info]) dnl Allow new connections on p1->p2. Allow only established connections p2->p1 AT_DATA([flows.txt], [dnl table=0,arp,action=normal table=0,ip,in_port=1,udp,nw_src=10.1.2.1/24,action=ct(commit) table=0,ip,in_port=1,udp6,ipv6_dst=2001:db8::1/64,action=ct(commit) table=0,ip,in_port=1,udp,tp_src=3/0x1,action=ct(commit) table=0,ip,in_port=2,actions=ct(table=1) table=0,ip6,in_port=2,actions=ct(table=1) table=1,priority=10,udp,ct_state=+trk+rpl,ct_nw_src=10.1.2.1/24,actions=controller table=1,priority=10,udp6,ct_state=+trk+rpl,ct_ipv6_dst=2001:db8::1/64,actions=controller table=1,priority=10,udp,ct_state=+trk+rpl,ct_tp_src=3/0x1,actions=controller table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Match ct_nw_src=10.1.2.1/24 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.2.100,dst=10.1.2.200,proto=17,tos=0,ttl=64,frag=no),udp(src=6,dst=6)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.2.200,dst=10.1.2.100,proto=17,tos=0,ttl=64,frag=no),udp(src=6,dst=6)']) dnl Match ct_ipv6_dst=2001:db8::1/64 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=2001:db8::2,label=0,proto=17,tclass=0x70,hlimit=128,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x86dd),ipv6(src=2001:db8::2,dst=2001:db8::1,label=0,proto=17,tclass=0x70,hlimit=128,frag=no),udp(src=2,dst=1)']) dnl Match ct_tp_src=3/0x1 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.2.100,ct_nw_dst=10.1.2.200,ct_nw_proto=17,ct_tp_src=6,ct_tp_dst=6,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.2.200,nw_dst=10.1.2.100,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=6,tp_dst=6 udp_csum:221 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=126 ct_state=est|rpl|trk,ct_ipv6_src=2001:db8::1,ct_ipv6_dst=2001:db8::2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ipv6,in_port=2 (via action) data_len=126 (unbuffered) udp6,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,ipv6_src=2001:db8::2,ipv6_dst=2001:db8::1,ipv6_label=0x00000,nw_tos=112,nw_ecn=0,nw_ttl=128,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:bfe2 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=106 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=106 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:553 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - conntrack - ofproto/trace]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 AT_DATA([flows.txt], [dnl dnl Table 0 dnl table=0,priority=100,arp,action=normal table=0,priority=10,udp,action=ct(table=1,zone=0) table=0,priority=10,tcp,action=ct(table=2,zone=1) table=0,priority=1,action=drop dnl dnl Table 1 dnl table=1,priority=10,in_port=1,ct_zone=0,ct_state=+trk+new,udp,action=ct(commit,zone=0),2 table=1,priority=10,in_port=1,ct_zone=0,ct_state=+trk+est,udp,action=2 table=1,priority=10,in_port=2,ct_zone=0,ct_state=+trk+est,udp,action=1 table=1,priority=1,action=drop dnl dnl Table 2 dnl table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+new,tcp,action=ct(commit,zone=1),ct(table=3,zone=2) table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+est,tcp,action=ct(table=3,zone=2) table=2,priority=1,action=drop dnl dnl Table 3 dnl table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+new,tcp,action=ct(commit,zone=2),4 table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+est,tcp,action=3 table=2,priority=1,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,udp'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: drop ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,udp'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: ct(commit),2 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,tcp'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: ct(commit,zone=2),4 ]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,tcp' --ct-next 'trk,est' --ct-next 'trk,est' ], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - nat - ofproto/trace]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 flow="in_port=1,udp,nw_src=1.1.1.1,nw_dst=1.1.1.2,udp_src=100,udp_dst=200" AT_DATA([flows.txt], [dnl table=0,priority=100,ip,nw_src=1.1.1.1,ct_state=-trk,action=ct(commit,nat(src=10.0.0.1-10.0.0.42:1000-1042),table=0) table=0,priority=100,udp,ct_state=+trk,nw_src=10.0.0.1,nw_dst=1.1.1.2,tp_src=1000,tp_dst=200,action=ct(commit,nat(dst=20.0.0.1-20.0.0.42:2000-2042),table=0) table=0,priority=100,udp,ct_state=+trk,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_src=1000,tp_dst=2000,action=3 table=0,priority=90,ip,ct_state=+trk,action=2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3 ]) flow="in_port=1,udp6,ipv6_src=1::1,ipv6_dst=1::2,udp_src=100,udp_dst=200" AT_DATA([flows.txt], [dnl table=0,priority=100,ip6,ipv6_src=1::1,ct_state=-trk,action=ct(commit,nat(src=[[10::1]]-[[10::42]]:1000-1042),table=0) table=0,priority=100,udp6,ct_state=+trk,ipv6_src=10::1,ipv6_dst=1::2,tp_src=1000,tp_dst=200,action=ct(commit,nat(dst=[[20::1]]-[[20::42]]:2000-2042),table=0) table=0,priority=100,udp6,ct_state=+trk,ipv6_src=10::1,ipv6_dst=20::1,tp_src=1000,tp_dst=2000,action=3 table=0,priority=90,ip6,ct_state=+trk,action=2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Checks the get/set sweep interval AT_SETUP([ofproto-dpif - conntrack - change sweep interval]) OVS_VSWITCHD_START # Check the default value. AT_CHECK([ovs-appctl dpctl/ct-get-sweep-interval], [0], [dnl 20000 ]) # Set the interval to 5s. AT_CHECK([ovs-appctl dpctl/ct-set-sweep-interval 5000], [0], [dnl setting sweep interval successful ]) # Verify that the previous value has been applied. AT_CHECK([ovs-appctl dpctl/ct-get-sweep-interval], [0], [dnl 5000 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - set mtu]) OVS_VSWITCHD_START add_of_ports br0 1 # Check that initial MTU is 1500 for 'br0' and 'p1'. AT_CHECK([ovs-vsctl get Interface br0 mtu], [0], [dnl 1500 ]) AT_CHECK([ovs-vsctl get Interface p1 mtu], [0], [dnl 1500 ]) # Request new MTU for 'p1' AT_CHECK([ovs-vsctl set Interface p1 mtu_request=1600]) # Check that the new MTU is applied AT_CHECK([ovs-vsctl wait-until Interface p1 mtu=1600]) # The internal port 'br0' should have the same MTU value as p1, becase it's # the new bridge minimum. AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1600]) AT_CHECK([ovs-vsctl del-port br0 p1]) # When 'p1' is deleted, the internal port should return to the default MTU AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1500]) # New port with 'mtu_request' in the same transaction. AT_CHECK([ovs-vsctl add-port br0 p2 -- set int p2 type=dummy mtu_request=1600]) AT_CHECK([ovs-vsctl wait-until Interface p2 mtu=1600]) AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1600]) # Explicitly set mtu_request on the internal member. This should prevent # the MTU from being overriden. AT_CHECK([ovs-vsctl set int br0 mtu_request=1700]) AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1700]) # The new MTU on p2 should not affect br0. AT_CHECK([ovs-vsctl set int p2 mtu_request=1400]) AT_CHECK([ovs-vsctl wait-until Interface p2 mtu=1400]) AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1700]) # Remove explicit mtu_request from br0. Now it should track the bridge # minimum again. AT_CHECK([ovs-vsctl set int br0 mtu_request=[[]]]) AT_CHECK([ovs-vsctl wait-until Interface br0 mtu=1400]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - fragment prerequisites]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) add_of_ports br0 1 AT_DATA([flows.txt], [dnl priority=10,in_port=1,udp,tp_src=67,tp_dst=68,action=drop priority=1,in_port=1,udp,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:max-idle=10000]) ovs-appctl time/stop AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth_type(0x0800),ipv4(proto=17,frag=later)']) ovs-appctl time/warp 5000 AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=later), actions:drop ]) dnl Change the flow table. This will trigger revalidation of all the flows. AT_CHECK([ovs-ofctl add-flow br0 priority=5,in_port=1,action=drop]) AT_CHECK([ovs-appctl revalidator/wait], [0]) dnl We don't want revalidators to delete any flow. If the flow has been dnl deleted it means that there's some inconsistency with the revalidation. AT_CHECK([grep flow_del ovs-vswitchd.log], [1]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - check_pkt_larger action]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 4 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,in_port=1,reg0=0x1/0x1 actions=output:2,resubmit(,2) table=1,in_port=1,actions=output:3,resubmit(,2) table=2,in_port=1,actions=mod_dl_dst:82:82:82:82:82:82,output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: check_pkt_len(size=200,gt(2,set(eth(dst=82:82:82:82:82:82)),4),le(3,set(eth(dst=82:82:82:82:82:82)),4)) ]) dnl Test flow xlate check_pkt_large clone action without using datapath check_pkt_len action. AT_CHECK([ovs-appctl dpif/set-dp-features br0 check_pkt_len false], [0], [ignore]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [dnl Datapath actions: 3,set(eth(dst=82:82:82:82:82:82)),4 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) dnl Enable datapath check_pkt_len action AT_CHECK([ovs-appctl dpif/set-dp-features br0 check_pkt_len true], [0], [ignore]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,in_port=1,priority=200,reg0=0x1/0x1 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: check_pkt_len(size=200,gt(2),le(drop)) ]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]] ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: check_pkt_len(size=200,gt(drop),le(drop)) ]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,in_port=1,priority=200,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(mod_dl_src:80:81:81:81:81:81,set_field:192.168.5.5->ip_dst,output:3),output:4 table=1,in_port=1,priority=0,ip actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(ct(commit),output:3),output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: check_pkt_len(size=200,gt(set(ipv4(dst=192.168.4.4)),2,set(eth(src=80:81:81:81:81:81)),set(ipv4(dst=192.168.5.5)),3,set(eth(src=50:54:00:00:00:09)),set(ipv4(dst=10.10.10.1)),4),le(set(ipv4(dst=192.168.4.4)),2,set(ipv4(dst=10.10.10.1)),clone(ct(commit),3),4)) ]) AT_DATA([flows.txt], [dnl table=0,priority=0 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,in_port=1,priority=200,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src, resubmit(,0)) table=1,in_port=1,priority=0,ip actions=clone(set_field:192.168.3.4->ip_src, resubmit(,0)) ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) ovs-ofctl dump-flows br0 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [dnl Megaflow: recirc_id=0,eth,icmp,reg0=0/0x1,in_port=1,nw_src=10.10.10.2,nw_frag=no Datapath actions: drop Translation failed (Recursion too deep), packet is dropped. ]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,priority=0 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,ip,nw_src=192.168.3.3 actions=output:3 table=1,ip,nw_src=192.168.3.4 actions=output:4 table=1,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src, resubmit(,0)) table=1,ip actions=clone(set_field:192.168.3.4->ip_src, resubmit(,0)) ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) ovs-ofctl dump-flows br0 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: check_pkt_len(size=200,gt(set(ipv4(src=192.168.3.3)),check_pkt_len(size=200,gt(3),le(3))),le(set(ipv4(src=192.168.3.4)),check_pkt_len(size=200,gt(4),le(4)))) ]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) table=1,in_port=1,reg0=0x1/0x1 actions=mod_dl_dst:82:82:82:82:82:82,controller(),resubmit(,2) table=1,in_port=1 actions=resubmit(,2) table=2,ip,dl_dst=82:82:82:82:82:82 actions=ct(table=3) table=2,ip,dl_dst=50:54:00:00:00:0a actions=ct(table=3) table=3,ip,reg0=0x1/0x1 actions=output:2 table=3,ip actions=output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([cat stdout | grep Datapath -B1], [0], [dnl Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=50:54:00:00:00:0a,nw_frag=no Datapath actions: check_pkt_len(size=200,gt(set(eth(dst=82:82:82:82:82:82)),userspace(pid=0,controller(reason=1,dont_send=1,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)),ct,recirc(0x2)),le(ct,recirc(0x3))) -- Megaflow: recirc_id=0x2,eth,ip,in_port=1,nw_frag=no Datapath actions: 2 -- Megaflow: recirc_id=0x3,eth,ip,in_port=1,nw_frag=no Datapath actions: 4 ]) ovs-ofctl del-flows br0 AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1),load:0x3->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=1,in_port=1,reg1=0x3 actions=output:4 table=4,in_port=1 actions=output:3 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([cat stdout | grep Datapath -B1], [0], [dnl Megaflow: recirc_id=0,eth,ip,in_port=1,nw_frag=no Datapath actions: check_pkt_len(size=200,gt(3),le(3)),2,4 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - check_pkt_larger with continuation and ct]) OVS_VSWITCHD_START add_of_ports --pcap br0 `seq 1 4` AT_CAPTURE_FILE([ofctl_monitor0.log]) AT_CHECK([ovs-ofctl monitor br0 resume --detach --no-chdir --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log]) AT_DATA([flows.txt], [dnl table=0,in_port=1 actions=check_pkt_larger(150)->NXM_NX_REG0[[0]],resubmit(,1) table=1,ip,reg0=0x1/0x1 actions=mod_dl_dst:82:82:82:82:82:82,controller(pause),resubmit(,2) table=1,ip,reg0=0 actions=mod_dl_dst:83:83:83:83:83:83,controller(pause),resubmit(,2) table=2,ip,dl_dst=82:82:82:82:82:82 actions=ct(table=3) table=2,ip,dl_dst=83:83:83:83:83:83 actions=ct(table=3) table=3,ip,reg0=0x1/0x1 actions=ct(commit),output:2 table=3,ip actions=ct(commit),output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) flow="in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) OVS_WAIT_UNTIL([test 1 = `ovs-ofctl parse-pcap p4-tx.pcap \ | grep dl_dst=83:83:83:83:83:83 | wc -l`]) AT_CHECK([test 0 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow" --len 200], [0], [stdout]) OVS_WAIT_UNTIL([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap \ | grep dl_dst=82:82:82:82:82:82 | wc -l`]) AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto-dpif - Local sampling - not supported]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ local-group-id=12], [0], [ignore]) m4_define([NOT_SUPPORTED_WARN], [dnl ignoring local sampling configuration: not supported by this datapath]) AT_CHECK([grep -q "NOT_SUPPORTED_WARN" ovs-vswitchd.log ]) AT_DATA([flows.txt], [dnl in_port=1 actions=sample(probability=32767,obs_domain_id=100,obs_point_id=200),2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) m4_define([TRACE_PKT], [m4_join([,], [in_port(1)], [eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) OVS_VSWITCHD_STOP(["/NOT_SUPPORTED_WARN/d"]) AT_CLEANUP AT_SETUP([ofproto-dpif - Local sampling - sanity check]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 dnl Enabling an usupported feature is dangerous but we are not sending traffic. AT_CHECK([ovs-appctl dpif/set-dp-features --force br0 psample true], [0], [ignore]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=42], [0], [ignore]) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200),3 in_port=2, actions=sample(probability=32767,collector_set_id=20,obs_domain_id=100,obs_point_id=200),3 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) m4_define([TRACE_PKT], [m4_join([,], [eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) dnl collector_set_id does not match. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 3 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: sample(sample=50.0%,actions(psample(group=42,cookie=0x64000000c8))),3 ]) OVS_VSWITCHD_STOP("/Enabling an unsupported feature is very dangerous/d") AT_CLEANUP AT_SETUP([ofproto-dpif - Local sampling - with IPFIX]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Enabling an usupported feature is dangerous but we are not sending traffic. AT_CHECK([ovs-appctl dpif/set-dp-features --force br0 psample true], [0], [ignore]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@i create ipfix targets=\"127.0.0.1:4739\" \ -- create Flow_Sample_Collector_Set ipfix=@i id=1 \ bridge=@br0 local-group-id=42], [0], [ignore]) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200),2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) m4_define([TRACE_PKT], [m4_join([,], [eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) m4_define([EXPECTED_ACT], [m4_join([], [sample(sample=50.0%,actions(], [psample(group=42,cookie=0x64000000c8),], [userspace(pid=0,], [flow_sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200,output_port=4294967295)], [))),], [2], )]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: EXPECTED_ACT ]) OVS_VSWITCHD_STOP("/Enabling an unsupported feature is very dangerous/d") AT_CLEANUP AT_SETUP([ofproto-dpif - Local sampling - with metered IPFIX]) OVS_VSWITCHD_START add_of_ports br0 1 2 dnl Enabling an usupported feature is dangerous but we are not sending traffic. AT_CHECK([ovs-appctl dpif/set-dp-features --force br0 psample true], [0], [ignore]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@i create ipfix targets=\"127.0.0.1:4739\" \ -- create Flow_Sample_Collector_Set ipfix=@i id=1 \ bridge=@br0 local-group-id=42], [0], [ignore]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=slowpath pktps stats bands=type=drop rate=2']) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200),2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) m4_define([TRACE_PKT], [m4_join([,], [eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) m4_define([EXPECTED_ACT], [m4_join([], [sample(sample=50.0%,actions(], [psample(group=42,cookie=0x64000000c8),], [meter(0),], [userspace(pid=0,], [flow_sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200,output_port=4294967295)], [))),], [2], )]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: EXPECTED_ACT ]) OVS_VSWITCHD_STOP("/Enabling an unsupported feature is very dangerous/d") AT_CLEANUP AT_SETUP([ofproto-dpif - Local sampling - drop]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-appctl dpif/set-dp-features --force br0 psample true], [0], [ignore]) AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 local-group-id=42], [0], [ignore]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=slowpath pktps stats bands=type=drop rate=2']) AT_DATA([flows.txt], [dnl in_port=1, actions=sample(probability=32767,collector_set_id=1,obs_domain_id=100,obs_point_id=200) in_port=2, actions=sample(probability=65535,collector_set_id=1,obs_domain_id=100,obs_point_id=200) ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) m4_define([TRACE_PKT], [m4_join([,], [eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: sample(sample=50.0%,actions(psample(group=42,cookie=0x64000000c8))) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: psample(group=42,cookie=0x64000000c8) ]) AT_CHECK([ovs-vsctl set Open_vSwitch . other-config:explicit-sampled-drops=true]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: sample(sample=50.0%,actions(psample(group=42,cookie=0x64000000c8))),drop ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2) TRACE_PKT'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: psample(group=42,cookie=0x64000000c8),drop ]) OVS_VSWITCHD_STOP("/Enabling an unsupported feature is very dangerous/d") AT_CLEANUP AT_SETUP([ofproto-dpif - Cleanup missing datapath flows]) OVS_VSWITCHD_START add_of_ports br0 1 2 m4_define([ICMP_PKT], [m4_join([,], [eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800)], [ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no)], [icmp(type=8,code=0)])]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 'actions=normal' ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ICMP_PKT']) AT_CHECK([ovs-appctl dpctl/dump-flows --names | strip_used | strip_stats | dnl strip_duration | strip_dp_hash | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:br0,p2 ]) dnl Make sure the ukey exists. AT_CHECK([ovs-appctl upcall/show | grep '(keys' | awk '{print $3}' | \ grep -q '1)'], [0]) dnl Delete all datapath flows, and make sure they are gone. AT_CHECK([ovs-appctl dpctl/del-flows]) AT_CHECK([ovs-appctl dpctl/dump-flows --names ], [0], []) dnl Move forward in time and make sure we have at least 4 * 500ms. AT_CHECK([ovs-appctl time/warp 3000 300], [0], [ignore]) dnl Make sure no more ukeys exists. AT_CHECK([ovs-appctl upcall/show | grep '(keys' | awk '{print $3}' | \ grep -qv '0)'], [1]) dnl Verify coverage counter was hit. AT_CHECK([ovs-appctl coverage/read-counter revalidate_missing_dp_flow], [0], [dnl 1 ]) OVS_VSWITCHD_STOP(["/failed to flow_del (No such file or directory)/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofproto-macros.at000066400000000000000000000317601514270232600234310ustar00rootroot00000000000000m4_divert_push([PREPARE_TESTS]) [ # Strips out uninteresting parts of ovs-ofctl output, as well as parts # that vary from one run to another. ofctl_strip () { sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ duration=[0-9.]*s,// s/ cookie=0x0,// s/ table=0,// s/ n_packets=0,// s/ n_offload_packets=[0-9]*,// s/ n_bytes=0,// s/ n_offload_bytes=[0-9]*,// s/ idle_age=[0-9]*,// s/ hard_age=[0-9]*,// s/dp_hash=0x[0-9a-f]*\//dp_hash=0x0\// s/recirc_id=0x[0-9a-f]*,/recirc_id=0x0,/ s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z|// s/dir\/[0-9]*\/br0.mgmt/dir\/XXXX\/br0.mgmt/ ' } # Strips out byte counters from ovs-ofctl output ofctl_strip_bytes () { sed 's/ n_bytes=[0-9]*,//' } # Filter (multiline) vconn debug messages from ovs-vswitchd.log. # Use with vconn_sub() and ofctl_strip() print_vconn_debug () { awk -F\| < ovs-vswitchd.log ' BEGIN { prt=0 } /\|vconn\|DBG\|/ { sub(/[ \t]*$/, ""); print $3 "|" $4 "|" $5; prt=1; next } $4 != "" { prt=0; next } prt==1 { sub(/[ \t]*$/, ""); print $0 } ' } vconn_sub() { sed ' s/tcp:127.0.0.1:[0-9][0-9]*:/unix:/ s/unix#[0-9]*:/unix:/ ' } ] # PARSE_LISTENING_PORT LOGFILE VARIABLE # # Parses the TCP or SSL/TLS port on which a server is listening from # LOGFILE, given that the server was told to listen on a kernel-chosen # port, and assigns the port number to shell VARIABLE. You should # specify the listening remote as ptcp:0:127.0.0.1 or # pssl:0:127.0.0.1, or the equivalent with [::1] instead of 127.0.0.1. # # Here's an example of how to use this with ovsdb-server: # # ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ... # PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) # # Now $TCP_PORT holds the listening port. m4_define([PARSE_LISTENING_PORT], [OVS_WAIT_UNTIL([$2=`sed -n 's/.*0:.*: listening on port \([[0-9]]*\)$/\1/p' "$1"` && test X != X"[$]$2"])]) start_daemon () { "$@" -vconsole:off --detach --no-chdir --pidfile --log-file pidfile="$OVS_RUNDIR"/$1.pid on_exit "test -e \"$pidfile\" && kill \`cat \"$pidfile\"\`" } # sim_add SANDBOX # # Starts a new simulated Open vSwitch instance named SANDBOX. Files related to # the instance, such as logs, databases, sockets, and pidfiles, are created in # a subdirectory of the main test directory also named SANDBOX. Afterward, the # "as" command (see below) can be used to run Open vSwitch utilities in the # context of the new sandbox. # # The new sandbox starts out without any bridges. Use ovs-vsctl in the context # of the new sandbox to create a bridge, e.g.: # # sim_add hv0 # Create sandbox hv0. # as hv0 # Set hv0 as default sandbox. # ovs-vsctl add-br br0 # Add bridge br0 inside hv0. # # or: # # sim_add hv0 # as hv0 ovs-vsctl add-br br0 sims= sim_add () { echo "adding simulator '$1'" sims="$sims $1" # Create sandbox. local d="$ovs_base"/$1 mkdir "$d" || return 1 ovs_setenv $1 # Create database and start ovsdb-server. : > "$d"/.conf.db.~lock~ as $1 ovsdb-tool create "$d"/conf.db "$abs_top_srcdir"/vswitchd/vswitch.ovsschema || return 1 as $1 start_daemon ovsdb-server --remote=punix:"$d"/db.sock || return 1 # Initialize database. as $1 ovs-vsctl --no-wait -- init || return 1 # Start ovs-vswitchd as $1 start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif -vunixctl } # "as $1" sets the OVS_*DIR environment variables to point to $ovs_base/$1. # # "as $1 COMMAND..." sets those variables in a subshell and invokes COMMAND # there. as() { if test "X$2" != X; then (ovs_setenv $1; shift; "$@") else ovs_setenv $1 fi } # Strips 'xid=0x1234' from ovs-ofctl output. strip_xids () { sed 's/ (xid=0x[[0-9a-fA-F]]*)//' } # Changes all 'used:...' to say 'used:0.0', to make output easier to compare. strip_used () { sed -E 's/used:[[0-9]]+\.[[0-9]]*/used:0.0/' } # Removes all 'duration=...' to make output easier to compare. strip_duration () { sed 's/duration=[[0-9.]]*s,//' } # Strips 'ufid:...' from output, to make it easier to compare. # (ufids are random.) strip_ufid () { sed 's/mega_ufid:[[-0-9a-f]]* // s/ufid:[[-0-9a-f]]* //' } parse_ufid () { grep -o 'ufid:[[-0-9a-f]]*' } # Strips packets: and bytes: from output strip_stats () { sed 's/packets:[[0-9]]*/packets:0/ s/bytes:[[0-9]]*/bytes:0/' } # Strips key32 field from output. strip_key32 () { sed 's/key32([[0-9 \/]]*),//' } # Strips packet-type from output. strip_ptype () { sed 's/packet_type(ns=[[0-9]]*,id=[[0-9]]*),//' } # Strips bare eth from output. strip_eth () { sed 's/eth(),//' } # Changes all 'recirc(...)' and 'recirc=...' to say 'recirc()' and # 'recirc=' respectively. This should make output easier to # compare. strip_recirc() { sed 's/recirc_id([[x0-9a-f]]*)/recirc_id()/ s/recirc_id=[[x0-9a-f]]*/recirc_id=/ s/recirc([[x0-9a-f]]*)/recirc()/' } # Strips dp_hash from output. strip_dp_hash() { sed 's/dp_hash([[0-9a-fx/]]*),//' } m4_divert_pop([PREPARE_TESTS]) m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m']) # _OVS_VSWITCHD_START([vswitchd-aux-args] [dbinit-aux-args]) # # Creates an empty database and starts ovsdb-server. # Starts ovs-vswitchd, with additional arguments 'vswitchd-aux-args'. # 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init' # before starting ovs-vswitchd. # m4_define([_OVS_VSWITCHD_START], [dnl Create database. touch .conf.db.~lock~ AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) dnl Start ovsdb-server. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr]) on_exit "kill `cat ovsdb-server.pid`" AT_CHECK([[sed < stderr ' /vlog|INFO|opened log file/d /ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Initialize database. AT_CHECK([ovs-vsctl --no-wait init $2]) dnl Start ovs-vswitchd. AT_CHECK([ovs-vswitchd $1 --detach --no-chdir --pidfile --log-file -vvconn -vofproto_dpif -vunixctl], [0], [], [stderr]) AT_CAPTURE_FILE([ovs-vswitchd.log]) on_exit "kill_ovs_vswitchd `cat ovs-vswitchd.pid`" AT_CHECK([[sed < stderr ' /ovs_numa|INFO|Discovered /d /odp_execute_impl|INFO|Action implementation /d /vlog|INFO|opened log file/d /vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d /reconnect|INFO|/d /dpif_netlink|INFO|Generic Netlink family .ovs_datapath. does not exist/d /ofproto|INFO|using datapath ID/d /netdev_linux|INFO|.*device has unknown hardware address family/d /ofproto|INFO|datapath ID changed to fedcba9876543210/d /dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d /netlink_socket|INFO|netlink: could not enable listening to all nsid/d /dpif_offload|INFO|Flow HW offload is enabled/d /probe tc:/d /setting extended ack support failed/d /tc: Using policy/d /hw-offload-priority configuration has an unknown type;/d']]) ]) # OVS_VSWITCHD_START([vsctl-args], [vsctl-output], [=override], # [vswitchd-aux-args] [dbinit-aux-args]) # # Creates a database and starts ovsdb-server, starts ovs-vswitchd # connected to that database, calls ovs-vsctl to create a bridge named # br0 with predictable settings, passing 'vsctl-args' as additional # commands to ovs-vsctl. If 'vsctl-args' causes ovs-vsctl to provide # output (e.g. because it includes "create" commands) then 'vsctl-output' # specifies the expected output after filtering through uuidfilt. # # If a test needs to use "system" devices (as dummies), then specify # =override (literally) as the third argument. Otherwise, system devices # won't work at all (which makes sense because tests should not access a # system's real Ethernet devices). # # 'vswitchd-aux-args' provides a way to pass extra command line arguments # to ovs-vswitchd m4_define([OVS_VSWITCHD_START], [_OVS_VSWITCHD_START( [--enable-dummy$3 --disable-system --disable-system-route $4], [$5]) AT_CHECK([add_of_br 0 $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) ]) # check_logs scans through all *.log files (except '*.log' and testsuite.log) # and reports all WARN, ERR, EMER log entries. User can add custom sed filters # in $1. m4_divert_push([PREPARE_TESTS]) check_logs () { local logs for log in *.log; do case ${log} in # ( '*.log'|testsuite.log) ;; # ( *) logs="${logs} ${log}" ;; esac done # We most notably ignore 'Broken pipe' warnings. These often and # intermittently appear in ovsdb-server.log, because *ctl commands # (e.g. ovs-vsctl) exit right after committing a change to the # database. However, in reaction, some daemon may immediately update the # database, and this later update may cause database sending update back to # *ctl command if *ctl has not exited yet. If *ctl command exits before # the database calls send, the send fails with 'Broken pipe' or # 'not connected' depending on system. Also removes all 'connection reset' # warning logs for similar reasons (EPIPE, ENOTCONN or ECONNRESET can be # returned on a send depending on whether the peer had unconsumed data # when it closed the socket). # # We also ignore "Dropped # log messages..." messages. Otherwise, even if # we ignore the messages that were rate-limited, we can end up failing just # because of the announcement that rate-limiting happened (and in a racy, # timing-dependent way, too). # # We also ignore the "Spent an unreasonably long XXms dumping flows" as # they can appear when large time/warps are used during tests. sed -n "$1 /reset by peer/d /Broken pipe/d /is not connected/d /timeval.*Unreasonably long [[0-9]]*ms poll interval/d /timeval.*faults: [[0-9]]* minor, [[0-9]]* major/d /timeval.*disk: [[0-9]]* reads, [[0-9]]* writes/d /timeval.*context switches: [[0-9]]* voluntary, [[0-9]]* involuntary/d /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d /Dropped [[0-9]]* log messages/d /setting extended ack support failed/d /ETHTOOL_GSSET_INFO/d /Spent an unreasonably long .*ms dumping flows/d /|WARN|/p /|ERR|/p /|EMER|/p" ${logs} } # Gets the last line number in ovs-vswitchd.log +1. This can be used to # help ensure that an output in the log is newly written as the result of # a test command and it is not just matching an earlier log line. get_log_next_line_num () { LINENUM=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) } # add_of_br BRNUM [ARG...] add_of_br () { local brnum=$1; shift local br=br$brnum local dpid=fedcba987654321$brnum local mac=aa:55:aa:55:00:0$brnum ovs-vsctl \ -- add-br $br \ -- set bridge $br datapath-type=dummy \ fail-mode=secure \ other-config:datapath-id=$dpid \ other-config:hwaddr=$mac \ protocols="[[OpenFlow10,OpenFlow11,OpenFlow12,\ OpenFlow13,OpenFlow14,OpenFlow15]]" \ -- "$@" } # add_of_ports__ PORT_TYPE [--pcap] BRIDGE PNUM... # # Creates PORT_TYPE interfaces in BRIDGE named pPNUM, OpenFlow port number # PNUM, and datapath port number PNUM (the latter is a consequence of # the dummy implementation, which tries to assign datapath port # numbers based on port names). # # If --pcap is supplied then packets received from the interface will # be written to $port-rx.pcap and those sent to it to $port-tx.pcap. add_of_ports__ () { local args local pcap=false local ptype=$1 shift if test "$1" = --pcap; then pcap=: shift fi local br=$1; shift for pnum; do AS_VAR_APPEND([args], [" -- add-port $br p$pnum -- set Interface p$pnum type=$ptype ofport_request=$pnum"]) if $pcap; then AS_VAR_APPEND([args], [" -- set Interface p$pnum options:rxq_pcap=p$pnum-rx.pcap options:tx_pcap=p$pnum-tx.pcap"]) fi done echo ovs-vsctl $args ovs-vsctl $args } # add_of_ports [--pcap] BRIDGE PNUM... # add_of_ports () { add_of_ports__ dummy $@ } # add_pmd_of_ports [--pcap] BRIDGE PNUM... # add_pmd_of_ports () { add_of_ports__ dummy-pmd $@ } m4_divert_pop([PREPARE_TESTS]) # OVS_VSWITCHD_STOP([ALLOWLIST]) # # Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files # for messages with severity WARN or higher and signaling an error if any # is present. The optional ALLOWLIST may contain shell-quoted "sed" # commands to delete any warnings that are actually expected, e.g.: # # OVS_VSWITCHD_STOP(["/expected error/d"]) m4_define([OVS_VSWITCHD_STOP], [AT_CHECK([check_logs $1]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server])]) m4_define([OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP], [AT_CHECK([ovs-appctl dpif/set-dp-features br0 tnl_push_pop false], [0]) ]) # WAIT_FOR_DUMMY_PORTS(NETDEV_DUMMY_PORT[, NETDEV_DUMMY_PORT...]) # # Wait until the netdev dummy ports are connected to each other m4_define([WAIT_FOR_DUMMY_PORTS], \ [m4_foreach([dummy_port], [$@], [ \ OVS_WAIT_WHILE([ovs-appctl netdev-dummy/conn-state dummy_port \ | grep 'unknown\|disconnected'])])]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ofproto.at000066400000000000000000010714671514270232600221600ustar00rootroot00000000000000AT_BANNER([ofproto]) AT_SETUP([ofproto - echo request]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -vwarn probe br0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - handling messages with bad version]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor running OpenFlow 1.0, then send the switch an OF1.1 features # request AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/send 0205000801234567 ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)// /ECHO/d' monitor.log], [0], [dnl send: OFPT_FEATURES_REQUEST (OF1.1): OFPT_ERROR (OF1.1): OFPBRC_BAD_VERSION OFPT_FEATURES_REQUEST (OF1.1): OFPT_BARRIER_REPLY: ]) OVS_VSWITCHD_STOP(["/received OpenFlow version 0x02 != expected 01/d"]) AT_CLEANUP AT_SETUP([ofproto - feature request, config request]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPT_FEATURES_REPLY: dpid:fedcba9876543210 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst LOCAL(br0): addr:aa:55:aa:55:00:00 config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - set OpenFlow port number]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy --\ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=99]) AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout]) AT_CHECK([[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/00:0.$/00:0x/' < stdout]], [0], [dnl OFPT_FEATURES_REPLY: dpid:fedcba9876543210 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(p1): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max 99(p2): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max LOCAL(br0): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - port stats - (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -vwarn dump-ports br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_PORT reply: 1 ports port LOCAL: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=0, bytes=0, drop=?, errs=?, coll=? ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - port stats - (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-ports br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_PORT reply (OF1.2): 1 ports port LOCAL: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=0, bytes=0, drop=?, errs=?, coll=? ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - port stats - (OpenFlow 1.4)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-ports br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/'], [0], [dnl OFPST_PORT reply (OF1.4): 1 ports port LOCAL: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=0, bytes=0, drop=?, errs=?, coll=? duration=?s CUSTOM Statistics rx_custom_packets_1=0, rx_custom_packets_2=0, rx_q0_bytes=0, rx_q0_packets=0, tx_q0_bytes=0, tx_q0_packets=0, ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - port-desc stats (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -vwarn dump-ports-desc br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_PORT_DESC reply: LOCAL(br0): addr:aa:55:aa:55:00:00 config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - port-desc stats (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-ports-desc br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_PORT_DESC reply (OF1.2): LOCAL(br0): addr:aa:55:aa:55:00:00 config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - port-desc stats (OpenFlow 1.5)]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-ports-desc br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/00:0./00:0x/'], [0], [dnl OFPST_PORT_DESC reply (OF1.5): 1(p1): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max 2(p2): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max 3(p3): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max LOCAL(br0): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max ]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-ports-desc br0 2], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/00:0./00:0x/'], [0], [dnl OFPST_PORT_DESC reply (OF1.5): 2(p2): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl CHECK_QUEUE_STATS(label, option, format) m4_define([CHECK_QUEUE_STATS], [ AT_SETUP([ofproto - queue stats - (OpenFlow $1)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O $2 queue-stats br0 | strip_xids], [0], [OFPST_QUEUE reply$3: 1 queues port LOCAL queue 0: bytes=?, pkts=?, errors=?, duration=? ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 LOCAL | strip_xids], [0], [OFPST_QUEUE reply$3: 1 queues port LOCAL queue 0: bytes=?, pkts=?, errors=?, duration=? ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 LOCAL 0 | strip_xids], [0], [OFPST_QUEUE reply$3: 1 queues port LOCAL queue 0: bytes=?, pkts=?, errors=?, duration=? ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 ANY 0 | strip_xids], [0], [OFPST_QUEUE reply$3: 1 queues port LOCAL queue 0: bytes=?, pkts=?, errors=?, duration=? ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 LOCAL 5 | strip_xids], [0], [OFPT_ERROR$3: OFPQOFC_BAD_QUEUE OFPST_QUEUE request$3: port=LOCAL queue=5 ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 ANY 5 | strip_xids], [0], [OFPT_ERROR$3: OFPQOFC_BAD_QUEUE OFPST_QUEUE request$3: port=ANY queue=5 ]) AT_CHECK([ovs-ofctl -O $2 queue-stats br0 10 | strip_xids], [0], [OFPT_ERROR$3: OFPQOFC_BAD_PORT OFPST_QUEUE request$3: port=10 queue=ALL ]) OVS_VSWITCHD_STOP AT_CLEANUP ]) CHECK_QUEUE_STATS([1.0], [OpenFlow10], []) CHECK_QUEUE_STATS([1.1], [OpenFlow11], [ (OF1.1)]) CHECK_QUEUE_STATS([1.2], [OpenFlow12], [ (OF1.2)]) CHECK_QUEUE_STATS([1.3], [OpenFlow13], [ (OF1.3)]) CHECK_QUEUE_STATS([1.4], [OpenFlow14], [ (OF1.4)]) dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - queue configuration - (OpenFlow 1.0)]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl queue-get-config br0 1], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY: port=1 queue 0: ]) AT_CHECK([ovs-ofctl queue-get-config br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY: port=1 OFPT_QUEUE_GET_CONFIG_REPLY: port=2 queue 0: queue 0: ]) AT_CHECK([ovs-ofctl queue-get-config br0 10], [0], [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_PORT OFPT_QUEUE_GET_CONFIG_REQUEST (xid=0x2): port=10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - queue configuration - (OpenFlow 1.1)]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow11 queue-get-config br0 1], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.1): port=1 queue 0: ]) AT_CHECK([ovs-ofctl -O OpenFlow11 queue-get-config br0 10 | strip_xids], [0], [OFPT_ERROR (OF1.1): OFPQOFC_BAD_PORT OFPT_QUEUE_GET_CONFIG_REQUEST (OF1.1): port=10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - queue configuration - (OpenFlow 1.2)]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow12 queue-get-config br0 1], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.2): port=1 queue 0: ]) AT_CHECK([ovs-ofctl -O OpenFlow12 queue-get-config br0 ANY], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPT_QUEUE_GET_CONFIG_REPLY (OF1.2): port=ANY queue 0: queue 0: queue 0: ]) AT_CHECK([ovs-ofctl -O OpenFlow12 queue-get-config br0 10 | strip_xids], [0], [OFPT_ERROR (OF1.2): OFPQOFC_BAD_PORT OFPT_QUEUE_GET_CONFIG_REQUEST (OF1.2): port=10 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - queue configuration - (OpenFlow 1.4)]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_CHECK([ovs-ofctl -O OpenFlow14 queue-get-config br0 any | strip_xids], [0], [OFPST_QUEUE_DESC reply (OF1.4): port=1 queue 0: port=2 queue 0: port=LOCAL queue 0: ]) AT_CHECK([ovs-ofctl -O OpenFlow14 queue-get-config br0 1 | strip_xids], [0], [OFPST_QUEUE_DESC reply (OF1.4): port=1 queue 0: ]) AT_CHECK([ovs-ofctl -O OpenFlow14 queue-get-config br0 10 | strip_xids], [0], [OFPT_ERROR (OF1.4): OFPQOFC_BAD_PORT OFPST_QUEUE_DESC request (OF1.4): port=10 ]) AT_CHECK([ovs-ofctl -O OpenFlow14 queue-get-config br0 1 2 | strip_xids], [0], [OFPT_ERROR (OF1.4): OFPQOFC_BAD_QUEUE OFPST_QUEUE_DESC request (OF1.4): port=1 queue=2 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - del group (OpenFlow 1.0 extension)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1233,type=select,selection_method=hash,bucket=output:10,bucket=output:11 group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11 group_id=1235,type=all,bucket=actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=output:10,bucket=output:11 ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl NXST_GROUP_DESC reply: group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 NXST_GROUP_DESC reply: ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 NXST_GROUP_DESC reply: ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn del-groups br0], [0]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl NXST_GROUP_DESC reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - del group (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0 ], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,type=all,bucket=actions=output:10 group_id=1235,type=all,bucket=actions=output:10 OFPST_GROUP_DESC reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.1): group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.1): group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0], [0]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.1): ]) # Negative test. AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=0xfffffff0], [1], [], [ovs-ofctl: invalid group id 4294967280 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - add indirect group]) OVS_VSWITCHD_START dnl indirect group must have exactly one bucket AT_DATA([stderr], [dnl OFPT_ERROR (OF1.1) (xid=0x2): OFPGMFC_INVALID_GROUP OFPT_GROUP_MOD (OF1.1) (xid=0x2): ***decode error: OFPGMFC_INVALID_GROUP*** ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 'group_id=1234,type=indirect'], [1], , [stderr]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 'group_id=1235,type=indirect,bucket=output:10']) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 'group_id=1236,type=indirect,bucket=output:10,bucket=output:11'], [1], , [stderr]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - group mod with mod and add_or_mod command]) OVS_VSWITCHD_START dnl Check that mod-group for non-existing group fails without --may-create AT_DATA([stderr], [dnl OFPT_ERROR (OF1.3) (xid=0x2): OFPGMFC_UNKNOWN_GROUP OFPT_GROUP_MOD (OF1.3) (xid=0x2): MOD group_id=1234,type=indirect,bucket=actions=output:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn mod-group br0 'group_id=1234,type=indirect,bucket=actions=2'], [1], , [stderr]) dnl Check that mod-group for non-existing group succeeds with --may-create AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn --may-create mod-group br0 'group_id=1234,type=indirect,bucket=actions=2']) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.3): group_id=1234,type=indirect,bucket=actions=output:2 ]) dnl Check that mod-group for existing group succeeds with --may-create AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn --may-create mod-group br0 'group_id=1234,type=indirect,bucket=actions=3']) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.3): group_id=1234,type=indirect,bucket=actions=output:3 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - del group (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1233,type=select,selection_method=hash,bucket=output:10,bucket=output:11 group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11 group_id=1235,type=all,bucket=actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=output:10,bucket=output:11 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 OFPST_GROUP_DESC reply (OF1.5): ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1236,type=select,selection_method=dp_hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 OFPST_GROUP_DESC reply (OF1.5): ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn del-groups br0], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - del group deletes flows]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt]) AT_DATA([flows.txt], [dnl tcp actions=group:1234 table=2 udp actions=group:1235 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 tcp actions=group:1234 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - insert group buckets]) OVS_VSWITCHD_START # Add group with no buckets. AT_DATA([groups.txt], [dnl group_id=1234,type=all ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all ]) # Add two buckets, using "last". AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Start over again, then add two buckets using "first". AT_CHECK([ovs-ofctl -O OpenFlow15 --strict del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-group br0 group_id=1234,type=all]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Add two more buckets before the existing ones. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Add another bucket at the end. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Verify that duplicate bucket IDs are rejected. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/truncated/,$d'], [0], [dnl OFPT_ERROR (OF1.5): OFPGMFC_BUCKET_EXISTS OFPT_GROUP_MOD (OF1.5): INSERT_BUCKET command_bucket_id:last,group_id=1234,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Add another bucket just before bucket 15. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=15]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Add two more buckets just before bucket 11, # getting the command from a file. AT_DATA([buckets.txt], [dnl group_id=1234,command_bucket_id=11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 - < buckets.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Add yet two more buckets. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=15,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21 ]) # Delete groups. AT_CHECK([ovs-ofctl -O OpenFlow15 del-groups br0]) # Add "fast_failover" group, then insert a bucket into it and make # sure that the type of the group doesn't change. (There was a bug # that caused this to happen.) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-group br0 group_id=1234,type=ff]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=ff ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=ff,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21 ]) # Negative tests. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=123,type=indirect,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: type is not needed ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=123,selection_method=dp_hash,type=indirect,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: selection method is not needed ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=0xffffff01,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: invalid command bucket id 4294967041 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: insert-bucket needs OpenFlow 1.5 or later ('-O OpenFlow15') ]) # Verify insert-buckets command to insert bucket with weight value for select group. AT_CHECK([ovs-ofctl -O OpenFlow15 --strict del-groups br0 group_id=1234]) AT_DATA([groups.txt], [dnl group_id=1234,type=select,selection_method=hash,bucket=bucket_id=1,weight:100,output:11 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id=2,weight=100,actions=output:11]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=select,selection_method=hash,bucket=bucket_id:1,weight:100,actions=output:11,bucket=bucket_id:2,weight:100,actions=output:11 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - remove group buckets]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=first]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=13]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) AT_DATA([buckets.txt], [dnl group_id=1234 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 - < buckets.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=first]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=all]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=1], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [dnl OFPT_ERROR (OF1.5): OFPGMFC_UNKNOWN_BUCKET OFPT_GROUP_MOD (OF1.5): REMOVE_BUCKET command_bucket_id:1,group_id=1234 ]) # Negative test. AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last], [1], [], [ovs-ofctl: none of the usable flow formats (OpenFlow10,NXM,OXM-OpenFlow15) is among the allowed flow formats (OpenFlow11) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle del group (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow13 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0 ], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,type=all,bucket=actions=output:10 group_id=1235,type=all,bucket=actions=output:10 OFPST_GROUP_DESC reply (OF1.3): ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow13 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.3): group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow13 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.3): group_id=1235,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow13 -vwarn del-groups br0], [0]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.3): ]) # Negative test. AT_CHECK([ovs-ofctl --bundle -O OpenFlow13 -vwarn del-groups br0 group_id=0xfffffff0], [1], [], [ovs-ofctl: invalid group id 4294967280 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle add indirect group]) OVS_VSWITCHD_START dnl indirect group must have exactly one bucket AT_DATA([stderr], [dnl OFPT_ERROR (OF1.4) (xid=0x2): OFPGMFC_INVALID_GROUP OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x2): bundle_id=0 flags=atomic ordered OFPT_GROUP_MOD (OF1.4) (xid=0x2): ***decode error: OFPGMFC_INVALID_GROUP*** ]) AT_CHECK([ovs-ofctl --bundle -vwarn add-group br0 'group_id=1234,type=indirect'], [1], , [stderr]) AT_CHECK([ovs-ofctl --bundle -vwarn add-group br0 'group_id=1235,type=indirect,bucket=output:10']) AT_CHECK([ovs-ofctl --bundle -vwarn add-group br0 'group_id=1236,type=indirect,bucket=output:10,bucket=output:11'], [1], , [stderr]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle group mod with mod and add_or_mod command]) OVS_VSWITCHD_START dnl Check that mod-group for non-existing group fails without --may-create AT_DATA([stderr], [dnl OFPT_ERROR (OF1.4) (xid=0x2): OFPGMFC_UNKNOWN_GROUP OFPT_GROUP_MOD (OF1.4) (xid=0x2): MOD group_id=1234,type=indirect,bucket=actions=output:2 ]) AT_CHECK([ovs-ofctl --bundle -vwarn mod-group br0 'group_id=1234,type=indirect,bucket=actions=2'], [1], , [stderr]) dnl Check that mod-group for non-existing group succeeds with --may-create AT_CHECK([ovs-ofctl --bundle -vwarn --may-create mod-group br0 'group_id=1234,type=indirect,bucket=actions=2']) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.4): group_id=1234,type=indirect,bucket=actions=output:2 ]) dnl Check that mod-group for existing group succeeds with --may-create AT_CHECK([ovs-ofctl --bundle -vwarn --may-create mod-group br0 'group_id=1234,type=indirect,bucket=actions=3']) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.4): group_id=1234,type=indirect,bucket=actions=output:3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle del group (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1233,type=select,selection_method=hash,bucket=output:10,bucket=output:11 group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=output:10,bucket=output:11 group_id=1235,type=all,bucket=actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=select,selection_method=hash,fields(eth_dst,ip_dst,tcp_dst),bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 OFPST_GROUP_DESC reply (OF1.5): ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1233,type=select,selection_method=hash,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 group_id=1235,type=all,bucket=bucket_id:2,actions=output:12,bucket=bucket_id:0,actions=output:10,bucket=bucket_id:1,actions=output:11 OFPST_GROUP_DESC reply (OF1.5): ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn del-groups br0], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle del group deletes flows]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl --bundle -vwarn add-groups br0 groups.txt]) AT_DATA([flows.txt], [dnl tcp actions=group:1234 table=2 udp actions=group:1235 ]) AT_CHECK([ovs-ofctl --bundle -vwarn add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 tcp actions=group:1234 OFPST_FLOW reply (OF1.4): ]) AT_CHECK([ovs-ofctl --bundle -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 OFPST_FLOW reply (OF1.4): ]) AT_CHECK([ovs-ofctl --bundle -vwarn del-groups br0 group_id=1234]) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl table=2, udp actions=group:1235 OFPST_FLOW reply (OF1.4): ]) AT_CHECK([ovs-ofctl --bundle -vwarn del-groups br0]) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. dnl Actions definition listed in both supported formats (w/ actions=) AT_SETUP([ofproto - bundle insert group buckets]) OVS_VSWITCHD_START # Add group with no buckets. AT_DATA([groups.txt], [dnl group_id=1234,type=all ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all ]) # Add two buckets, using "last". AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Start over again, then add two buckets using "first". AT_DATA([groups.txt], [dnl delete group_id=1234 add group_id=1234,type=all insert_bucket group_id=1234,command_bucket_id=first,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Add two more buckets before the existing ones. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11 ]) # Add another bucket at the end. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Verify that duplicate bucket IDs are rejected. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=last,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/talking to/,$d'], [0], [dnl Error OFPGMFC_BUCKET_EXISTS for: OFPT_GROUP_MOD (OF1.5): INSERT_BUCKET command_bucket_id:last,group_id=1234,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.5): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) # Add another bucket just before bucket 15. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=15]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Add two more buckets just before bucket 11, # getting the command from a file. AT_DATA([buckets.txt], [dnl group_id=1234,command_bucket_id=11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 - < buckets.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) # Add yet two more buckets. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=15,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:20,actions=output:20,bucket=bucket_id:21,actions=output:21 ]) # Negative tests. AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn insert-buckets br0 group_id=1234,command_bucket_id=0xffffff01,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: invalid command bucket id 4294967041 ]) AT_CHECK([ovs-ofctl --bundle -vwarn insert-buckets br0 group_id=1234,command_bucket_id=first,bucket=bucket_id:0,actions=output:0,bucket=bucket_id:1,actions=output:1], [1], [], [ovs-ofctl: insert-bucket needs OpenFlow 1.5 or later ('-O OpenFlow15') ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle remove group buckets]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -F OXM-OpenFlow15 -O OpenFlow15 -vwarn dump-groups br0 1234], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:10,actions=output:10,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=first]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15,bucket=bucket_id:16,actions=output:16 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:13,actions=output:13,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=13]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all,bucket=bucket_id:11,actions=output:11,bucket=bucket_id:12,actions=output:12,bucket=bucket_id:14,actions=output:14,bucket=bucket_id:15,actions=output:15 ]) AT_DATA([buckets.txt], [dnl group_id=1234 ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 - < buckets.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.5): group_id=1234,type=all ]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=first]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=all]) AT_CHECK([ovs-ofctl --bundle -O OpenFlow15 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=1], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr | sed '/talking to/,$d'], [0], [dnl Error OFPGMFC_UNKNOWN_BUCKET for: OFPT_GROUP_MOD (OF1.5): REMOVE_BUCKET command_bucket_id:1,group_id=1234 Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.5): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) # Negative test. AT_CHECK([ovs-ofctl --bundle -O OpenFlow11 -vwarn remove-buckets br0 group_id=1234,command_bucket_id=last], [1], [], [ovs-ofctl: none of the usable flow formats (OXM-OpenFlow15) is among the allowed flow formats (OXM-OpenFlow14) ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - flow mod checks group availability]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 group_id=1234,type=all,bucket=output:10]) AT_DATA([flows.txt], [dnl tcp actions=group:1234 udp actions=group:1235 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1234']) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1235'], [1], [], [stderr]) AT_CHECK([strip_xids < stderr], [0], [OFPT_ERROR (OF1.1): OFPBAC_BAD_OUT_GROUP OFPT_FLOW_MOD (OF1.1): ADD tcp actions=group:1235 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle flow mod checks group availability]) OVS_VSWITCHD_START AT_DATA([bundle.txt], [dnl group add group_id=1234,type=all,bucket=output:10 flow add tcp actions=group:1234 flow add udp actions=group:1235 ]) AT_CHECK([ovs-ofctl -vwarn bundle br0 bundle.txt], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr | sed '/talking to/,$d'], [0], [dnl Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.4): ADD udp actions=group:1235 Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group description]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 group_id=1234,type=all,bucket=output:10], [0], [stdout]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_DESC reply (OF1.1): group_id=1234,type=all,bucket=actions=output:10 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group features (OpenFlow 1.0 extension)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-group-features br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl NXST_GROUP_FEATURES reply: Group table: Types: 0xf Capabilities: 0x7 all group: max_groups=0xffffff00 actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst select group: max_groups=0xffffff00 actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst indirect group: max_groups=0xffffff00 actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst fast failover group: max_groups=0xffffff00 actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group features (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-group-features br0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_GROUP_FEATURES reply (OF1.2): Group table: Types: 0xf Capabilities: 0x7 all group: max_groups=0xffffff00 actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue select group: max_groups=0xffffff00 actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue indirect group: max_groups=0xffffff00 actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue fast failover group: max_groups=0xffffff00 actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group stats (OpenFlow 1.0 extension)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn add-flow br0 'tcp actions=group:1234']) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 NXST_GROUP reply: ]) AT_CHECK([ovs-ofctl -O OpenFlow10 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 group_id=1235,duration=?s,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 NXST_GROUP reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group stats (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1234']) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sort], [0], [dnl group_id=1234,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 group_id=1235,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group stats (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn add-flow br0 'tcp actions=group:1234']) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.3): ]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 group_id=1235,duration=?s,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is really bare-bones. dnl It at least checks request and reply serialization and deserialization. AT_SETUP([ofproto - group stats (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-flow br0 'tcp actions=group:1234']) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.5): ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-group-stats br0], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 group_id=1235,duration=?s,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is used to find that the bucket counter is not updated. AT_SETUP([ofproto - group stats after insert a new bucket (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=select,selection_method=hash bucket=bucket_id=1,weight:100,actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn insert-buckets br0 'group_id=1234, command_bucket_id=last, bucket=bucket_id=2,weight:100,actions=output:10']) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout]) AT_CHECK([strip_xids < stdout | sed 's/duration=[[0-9.]]*s/duration=?s/' | sort], [0], [dnl group_id=1234,duration=?s,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0,bucket1:packet_count=0,byte_count=0 OFPST_GROUP reply (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This found a use-after-free error in bridge destruction in the dnl presence of groups. AT_SETUP([ofproto - group add then bridge delete (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1234,type=all,bucket=output:10 group_id=1235,type=all,bucket=output:10 dnl This checks for regression against a parser bug such that dnl "actions=resbmit(,1)" etc. was rejected as a syntax error. group_id=2345,type=select,bucket=weight:10,actions=resubmit(,1),bucket=weight:10,actions=resubmit(,2),bucket=weight:1,actions=resubmit(,3) ]) AT_CHECK([ovs-ofctl -O OpenFlow13 -vwarn add-groups br0 groups.txt]) AT_CHECK([ovs-vsctl del-br br0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod-port (OpenFlow 1.0)]) OVS_VSWITCHD_START for command_config_state in \ 'up 0 0' \ 'noflood NO_FLOOD 0' \ 'flood 0 0' \ 'no-receive NO_RECV 0' \ 'no-forward NO_RECV,NO_FWD 0' \ 'no-packet-in NO_RECV,NO_FWD,NO_PACKET_IN 0' \ 'forward NO_RECV,NO_PACKET_IN 0' \ 'packet-in NO_RECV 0' \ 'up NO_RECV 0' \ 'receive 0 0' \ 'down PORT_DOWN LINK_DOWN' do printf '\n--- %s --- \n\n' "$command_config_state" set $command_config_state command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3] AT_CHECK([ovs-ofctl -vwarn mod-port br0 br0 $command]) AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout]) AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl OFPT_FEATURES_REPLY: dpid:fedcba9876543210 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst LOCAL(br0): addr:aa:55:aa:55:00:00 config: $config state: $state speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY: frags=normal miss_send_len=0 ]) done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod-port (OpenFlow 1.2)]) OVS_VSWITCHD_START for command_config_state in \ 'up 0 LIVE' \ 'no-receive NO_RECV LIVE' \ 'no-forward NO_RECV,NO_FWD LIVE' \ 'no-packet-in NO_RECV,NO_FWD,NO_PACKET_IN LIVE' \ 'forward NO_RECV,NO_PACKET_IN LIVE' \ 'packet-in NO_RECV LIVE' \ 'up NO_RECV LIVE' \ 'receive 0 LIVE' \ 'down PORT_DOWN LINK_DOWN' do printf '\n--- %s --- \n\n' "$command_config_state" set $command_config_state command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3] AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn mod-port br0 br0 $command]) AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn show br0], [0], [stdout]) AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl OFPT_FEATURES_REPLY (OF1.2): dpid:fedcba9876543210 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS LOCAL(br0): addr:aa:55:aa:55:00:00 config: $config state: $state speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (OF1.2): frags=normal miss_send_len=0 ]) done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod-port (OpenFlow 1.4)]) OVS_VSWITCHD_START for command_config_state in \ 'up 0 LIVE' \ 'no-receive NO_RECV LIVE' \ 'no-forward NO_RECV,NO_FWD LIVE' \ 'no-packet-in NO_RECV,NO_FWD,NO_PACKET_IN LIVE' \ 'forward NO_RECV,NO_PACKET_IN LIVE' \ 'packet-in NO_RECV LIVE' \ 'up NO_RECV LIVE' \ 'receive 0 LIVE' \ 'down PORT_DOWN LINK_DOWN' do printf '\n--- %s --- \n\n' "$command_config_state" set $command_config_state command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3] AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn mod-port br0 br0 $command]) AT_CHECK([ovs-ofctl -O OpenFlow14 -vwarn show br0], [0], [stdout]) AT_CHECK_UNQUOTED([strip_xids < stdout], [0], [dnl OFPT_FEATURES_REPLY (OF1.4): dpid:fedcba9876543210 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS BUNDLES OFPST_PORT_DESC reply (OF1.4): LOCAL(br0): addr:aa:55:aa:55:00:00 config: $config state: $state speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (OF1.4): frags=normal miss_send_len=0 ]) done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - basic flow_mod commands (NXM)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: ]) AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl add-flows br0 -]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=2]) AT_CHECK([ovs-ofctl -F nxm add-flow br0 table=1,in_port=4,actions=3]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 table=1, in_port=4 actions=output:3 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | strip_xids], [0], [dnl NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - basic flow_mod commands (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply: ]) AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl -F openflow10 add-flows br0 -]) AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 in_port=1,actions=2]) AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 table=1,in_port=4,actions=3]) AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 table=1, in_port=4 actions=output:3 OFPST_FLOW reply: ]) AT_CHECK([ovs-ofctl -F openflow10 dump-aggregate br0 table=0 | strip_xids], [0], [dnl OFPST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=2 ]) AT_CHECK([ovs-ofctl -F openflow10 del-flows br0]) AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP # It's really dumb that check_overlap and reset_counts are considered # part of flow state, but OpenFlow implies that it is, and OFTest and # some users insist on it. AT_SETUP([ofproto - add-flow and flags]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 check_overlap,in_port=1,actions=drop]) # Prior to OF1.3, flow dumps didn't include a "flags" field. AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply: in_port=1 actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.1): in_port=1 actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.2): in_port=1 actions=drop ]) # OF1.3 makes the flags visible. AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.3): check_overlap reset_counts in_port=1 actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.4): check_overlap reset_counts in_port=1 actions=drop ]) # OF1.5 makes the flags invisible. AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.5): check_overlap reset_counts in_port=1 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - basic flow_mod commands (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.1): ]) AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl -O OpenFlow11 add-flows br0 -]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 in_port=1,actions=2]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 table=1,in_port=4,actions=3]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 table=1, in_port=4 actions=output:3 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-aggregate br0 table=0 | strip_xids], [0], [dnl OFPST_AGGREGATE reply (OF1.1): packet_count=0 byte_count=0 flow_count=2 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.1): table=1, in_port=4 actions=output:3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow_mod negative test (OpenFlow 1.1)]) OVS_VSWITCHD_START( [set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]) AT_CHECK([ovs-ofctl add-flow -O OpenFlow11 br0 table=1,action=goto_table:2]) # The error message here actually comes from ovs-ofctl, not from ovs-vswitchd, # but at least it's the same code in ofpacts_check() that issues the error. AT_CHECK([ovs-ofctl add-flow -O OpenFlow11 br0 table=1,action=goto_table:1], [1], [], [ovs-ofctl: actions are invalid with specified match (OFPBIC_BAD_TABLE_ID) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - set-field flow_mod commands (NXM)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop]) AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src]) AT_CHECK([ovs-ofctl add-flow br0 icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl table=1, icmp6,in_port=3,icmp_type=136 actions=load:0xa6badbfff00d59fa->NXM_NX_ND_TARGET[[0..63]],load:0xfe8086753097890a->NXM_NX_ND_TARGET[[64..127]],load:0xccddeeff0011->NXM_NX_ND_TLL[[]] table=1, ipv6,in_port=3 actions=load:0xa6badbfffefe59fa->NXM_NX_IPV6_SRC[[0..63]],load:0xfe8001234567890a->NXM_NX_IPV6_SRC[[64..127]] NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - basic flow_mod commands (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.2): ]) AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl -O OpenFlow12 add-flows br0 -]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=1,actions=2]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 table=1,in_port=4,actions=3]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1 table=1, in_port=4 actions=output:3 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - set-field flow_mod commands (OF1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 ipv6,table=1,in_port=3,actions=drop]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl table=1, icmp6,in_port=3,icmp_type=136 actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa->nd_target,set_field:cc:dd:ee:ff:00:11->nd_tll table=1, ipv6,in_port=3 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - dump flows with cookie]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, in_port=2 actions=output:1 cookie=0x3, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | strip_xids], [0], [dnl NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3 ]) AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/-1 | ofctl_strip | sort], [0], [dnl cookie=0x3, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/-1 | strip_xids], [0], [dnl NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie change (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 OFPST_FLOW reply: ]) AT_CHECK([ovs-ofctl -F openflow10 mod-flows br0 cookie=0x2,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, in_port=1 actions=output:1 OFPST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie change (NXM)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F nxm add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=0x2,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, in_port=1 actions=output:1 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - no mod flow with cookie change (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 mod-flows br0 cookie=0x2,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 OFPST_FLOW reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl The OpenFlow 1.2 spec states that the cookie may not be modified AT_SETUP([ofproto - no mod flow with cookie change (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 cookie=0x2,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x1, in_port=2 actions=output:1 cookie=0x2, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=0x1/0xff,actions=4]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:4 cookie=0x1, in_port=2 actions=output:4 cookie=0x2, in_port=3 actions=output:1 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=2,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=3,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x1, in_port=2 actions=output:1 cookie=0x2, in_port=3 actions=output:1 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 mod-flows br0 cookie=0x1/0xff,actions=4]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:4 cookie=0x1, in_port=2 actions=output:4 cookie=0x2, in_port=3 actions=output:1 OFPST_FLOW reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=2,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=3,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x1, in_port=2 actions=output:1 cookie=0x2, in_port=3 actions=output:1 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 cookie=0x1/0xff,actions=4]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:4 cookie=0x1, in_port=2 actions=output:4 cookie=0x2, in_port=3 actions=output:1 OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl The OpenFlow 1.2 spec states that the cookie may not be modified AT_SETUP([ofproto - mod flows based on cookie mask with cookie change]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x1, in_port=2 actions=output:1 cookie=0x2, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=1/-1,cookie=4,actions=4]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, in_port=3 actions=output:1 cookie=0x4, in_port=1 actions=output:4 cookie=0x4, in_port=2 actions=output:4 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - NXM]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F nxm mod-flows br0 in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:1 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - OF1.1]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O openflow11 mod-flows br0 in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O openflow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=output:1 OFPST_FLOW reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - OF1.2]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O openflow12 mod-flows br0 in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O openflow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - NXM]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=1/1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - OF1.1]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O openflow11 mod-flows br0 cookie=1/1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O openflow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - OF1.2]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O openflow12 mod-flows br0 cookie=1/1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O openflow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows with cookies]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, in_port=2 actions=output:1 cookie=0x3, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows based on cookie]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, in_port=2 actions=output:1 cookie=0x3, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3/-1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, in_port=2 actions=output:1 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows based on cookie mask]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, in_port=2 actions=output:1 cookie=0x3, in_port=3 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3/0x1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, in_port=2 actions=output:1 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows based on table id (NXM)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0 table=0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, table=1, in_port=2 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0 table=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows based on table id (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 table=0]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 table=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.1): ]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply (OF1.1): cookie=0x2, table=1, in_port=2 actions=output:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - del flows based on table id (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0 table=0]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0 table=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x1, in_port=1 actions=output:1 cookie=0x2, table=1, in_port=2 actions=output:1 OFPST_FLOW reply (OF1.2): ]) AT_CHECK([ovs-ofctl -O OpenFlow12 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow_mod with out_port matching (OpenFlow 1.0)]) OVS_VSWITCHD_START AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1,output:2,output:3 in_port=3 actions=output:3,output:1,output:2 in_port=4 actions=drop in_port=5 actions=output:3 in_port=6 actions=output:1 ]) AT_CHECK([ovs-ofctl -F openflow10 add-flows br0 flows.txt]) (cat flows.txt; echo 'OFPST_FLOW reply:') > expout AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [expout]) (grep 'output:2' flows.txt; echo 'OFPST_FLOW reply:') > expout AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 out_port=2 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -F openflow10 del-flows br0 out_port=2]) (grep -v 'output:2' flows.txt; echo 'OFPST_FLOW reply:') > expout AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -F openflow10 del-flows br0 out_port=3]) (grep -v 'output:[[23]]' flows.txt; echo 'OFPST_FLOW reply:') > expout AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -F openflow10 del-flows br0 out_port=1]) (grep -v 'output:[[123]]' flows.txt; echo 'OFPST_FLOW reply:') > expout AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow_mod with out_port matching (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_DATA([flows.txt], [dnl in_port=1 actions=output:2 in_port=2 actions=output:1,write_actions(output:2,output:3) in_port=3 actions=output:3,output:1,write_actions(output:2) in_port=4 actions=drop in_port=5 actions=write_actions(output:3) in_port=6 actions=output:1 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flows br0 flows.txt]) (cat flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) (grep 'output:2' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 out_port=2 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_port=2]) (grep -v 'output:2' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_port=3]) (grep -v 'output:[[23]]' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_port=1]) (grep -v 'output:[[123]]' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow_mod with out_group matching (OpenFlow 1.1)]) OVS_VSWITCHD_START AT_DATA([groups.txt], [dnl group_id=1,type=all,bucket=output:10 group_id=2,type=all,bucket=output:10 group_id=3,type=all,bucket=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-groups br0 groups.txt]) AT_DATA([flows.txt], [dnl in_port=1 actions=group:2,output:5 in_port=2 actions=group:1,write_actions(group:2,group:3,output:6) in_port=3 actions=group:3,group:1,write_actions(group:2,output:3) in_port=4 actions=output:4 in_port=5 actions=write_actions(output:4,group:3) in_port=6 actions=group:1 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-flows br0 flows.txt]) (cat flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) (grep 'output:3' flows.txt | grep 'group:2'; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 out_port=3,out_group=2 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_group=2]) (grep -v 'group:2' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_group=3]) (grep -v 'group:[[23]]' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 out_group=1]) (grep -v 'group:[[123]]' flows.txt; echo 'OFPST_FLOW reply (OF1.1):') > expout AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle flow_mod with out group matching (OpenFlow 1.4)]) OVS_VSWITCHD_START AT_DATA([bundle.txt], [dnl group group_id=1,type=all,bucket=output:10 group group_id=2,type=all,bucket=output:10 group group_id=3,type=all,bucket=output:10 flow in_port=1 actions=group:2,output:5 flow in_port=2 actions=group:1,write_actions(group:2,group:3,output:6) flow in_port=3 actions=group:3,group:1,write_actions(group:2,output:3) flow in_port=4 actions=output:4 flow in_port=5 actions=write_actions(output:4,group:3) flow in_port=6 actions=group:1 ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) # for checking AT_DATA([flows.txt], [dnl in_port=1 actions=group:2,output:5 in_port=2 actions=group:1,write_actions(group:2,group:3,output:6) in_port=3 actions=group:3,group:1,write_actions(group:2,output:3) in_port=4 actions=output:4 in_port=5 actions=write_actions(output:4,group:3) in_port=6 actions=group:1 ]) (cat flows.txt; echo 'OFPST_FLOW reply (OF1.4):') > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [expout]) (grep 'output:3' flows.txt | grep 'group:2'; echo 'OFPST_FLOW reply (OF1.4):') > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 out_port=3,out_group=2 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0 out_group=2]) (grep -v 'group:2' flows.txt; echo 'OFPST_FLOW reply (OF1.4):') > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0 out_group=3]) (grep -v 'group:[[23]]' flows.txt; echo 'OFPST_FLOW reply (OF1.4):') > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [expout]) AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0 out_group=1]) (grep -v 'group:[[123]]' flows.txt; echo 'OFPST_FLOW reply (OF1.4):') > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle packet-out (OpenFlow 1.4)]) OVS_VSWITCHD_START ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 priority=0,actions=drop # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # This bundle adds a group, a flow using that group and then a # packet-out that needs them both. Finally the bundle deletes all # groups, which also deletes the flow, leaving only the drop flow in # the table. If this works properly, the packet-out should get # translated and processed before the flow disappears. Also, if the # bundle were to fail, the packet-in should not get executed. AT_DATA([bundle.txt], [dnl group group_id=1,type=all,bucket=output:controller flow in_port=6 actions=group:1 packet-out in_port=6, packet=0001020304050010203040501234 actions=table group delete ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) # Verify that only the drop flow remains. AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [ reset_counts priority=0 actions=drop OFPST_FLOW reply (OF1.4): ]) # Verify that the packet-in was received via controller action. AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [OFPT_PACKET_IN (xid=0x0): total_len=14 in_port=6 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle packet-out, failing bundle commit (OpenFlow 1.4)]) OVS_VSWITCHD_START ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 priority=0,actions=drop # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # This bundle adds a flow using that group and then a packet-out that # needs them both. Finally the bundle adds another flow that referes # to a non-existing group, causing the bundle to fail, and the # packet-in should not get executed. AT_DATA([bundle.txt], [dnl group group_id=1,type=all,bucket=output:controller flow in_port=6 actions=group:1 packet-out in_port=6, packet=0001020304050010203040501234 actions=table flow in_port=7 actions=group:2 ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d' | strip_xids], [], [dnl Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.4): ADD in_port=7 actions=group:2 Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) # Verify that only the drop flow remains. AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [ reset_counts priority=0 actions=drop OFPST_FLOW reply (OF1.4): ]) # Verify that the packet-in was NOT received via controller action. AT_CHECK([strip_xids < monitor.log], [0], []) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle packet-out makes bundle commit to fail(OpenFlow 1.4)]) OVS_VSWITCHD_START ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 priority=0,actions=drop # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # This bundle adds a flow using that group and then a packet-out that # needs them both. Finally the bundle adds another flow that referes # to a non-existing group, causing the bundle to fail, and the # packet-in should not get executed. AT_DATA([bundle.txt], [dnl group group_id=1,type=all,bucket=output:controller flow in_port=6 actions=group:1 packet-out in_port=6, packet=0001020304050010203040501234 actions=table packet-out in_port=6, packet=0001020304050010203040501234 actions=group:2 ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d' | strip_xids], [], [dnl Error OFPBAC_BAD_OUT_GROUP for: OFPT_PACKET_OUT (OF1.4): in_port=6 actions=group:2 data_len=14 vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) # Verify that only the drop flow remains. AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0], [ reset_counts priority=0 actions=drop OFPST_FLOW reply (OF1.4): ]) # Verify that the packet-in was NOT received via controller action. AT_CHECK([strip_xids < monitor.log], [0], []) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow table configuration (OpenFlow 1.0)]) OVS_VSWITCHD_START # Check the default configuration. head_table() { printf 'OFPST_TABLE reply (xid=0x2): table 0%s: active=0, lookup=0, matched=0 max_entries=1000000 matching: exact match or wildcard: in_port eth_{src,dst,type} vlan_{vid,pcp} ip_{src,dst} nw_{proto,tos} tcp_{src,dst} ' "$1" } (head_table; echo ' tables 1...253: ditto') > expout AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout]) # Change the configuration. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=main \ -- --id=@t1 create Flow_Table flow-limit=1024 \ -- set bridge br0 'flow_tables={1=@t1,0=@t0}' \ | uuidfilt], [0], [<0> <1> ]) # Check that the configuration was updated. (head_table ' ("main")'; echo ' table 1: active=0, lookup=0, matched=0 max_entries=1024 (same matching) table 2: active=0, lookup=0, matched=0 max_entries=1000000 (same matching) '; echo ' tables 3...253: ditto') > expout AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP dnl In-band and fail-open add "hidden rules" to table 0. These rules shouldn't dnl be visible to OpenFlow. This test checks that "dump-flows" and dnl "dump-tables" don't make them visible. AT_SETUP([ofproto - hidden rules not in table stats]) # Use an IP address for a controller that won't actually exist: we # want to create in-band rules but we do not want to actually connect # to a controller (because that could mess about with our test). The # Class E range 240.0.0.0 - 255.255.255.255 seems like a good choice. OVS_VSWITCHD_START([set-controller br0 tcp:240.0.0.1:6653]) for i in 1 2 3 4 5; do ovs-appctl time/warp 1000; done # Check that no hidden flows are visible in OpenFlow. AT_CHECK([ovs-ofctl dump-flows br0 | strip_xids], [0], [NXST_FLOW reply: ]) # Check that some hidden flows related to 240.0.0.1 are actually in table 0. # # We discard flows that mention table_id because we only want table 0 flows, # which in OVS is implied by the absence of a table_id. AT_CHECK([ovs-appctl bridge/dump-flows br0], [0], [stdout]) AT_CHECK([test `grep '240\.0\.0\.1' stdout | grep -v table_id= | wc -l` -gt 0]) # Check that dump-tables doesn't count the hidden flows. head_table() { printf 'OFPST_TABLE reply: table 0: active=0, lookup=0, matched=0 max_entries=1000000 matching: exact match or wildcard: in_port eth_{src,dst,type} vlan_{vid,pcp} ip_{src,dst} nw_{proto,tos} tcp_{src,dst} ' } (head_table; echo ' tables 1...253: ditto') > expout AT_CHECK([ovs-ofctl dump-tables br0 | strip_xids], [0], [expout]) OVS_VSWITCHD_STOP(["/240\.0\.0\.1/d"]) AT_CLEANUP AT_SETUP([ofproto - flow table configuration (OpenFlow 1.2)]) OVS_VSWITCHD_START # Check the default configuration. head_table() { printf 'OFPST_TABLE reply (OF1.2) (xid=0x2): table 0%s: active=0, lookup=0, matched=0 metadata: match=0xffffffffffffffff write=0xffffffffffffffff config=controller max_entries=1000000 instructions (table miss and others): instructions: apply_actions clear_actions write_actions write_metadata goto_table Write-Actions and Apply-Actions features: actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue supported on Set-Field: metadata in_port_oxm eth_{src,dst} vlan_{vid,pcp} mpls_{label,tc} ip_{src,dst} ipv6_{src,dst,label} ip_dscp nw_ecn arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll} matching: exact match or wildcard: metadata in_port_oxm eth_{src,dst,type} vlan_{vid,pcp} mpls_{label,tc} ip_{src,dst} ipv6_{src,dst,label} nw_proto ip_dscp nw_ecn arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll} ' "$1" } tail_table() { printf ' table 253: active=0, lookup=0, matched=0 metadata: match=0xffffffffffffffff write=0xffffffffffffffff config=controller max_entries=1000000 instructions (table miss and others): instructions: apply_actions clear_actions write_actions write_metadata (same actions) (same matching) ' } (head_table; printf ' tables 1...252: ditto\n\n'; tail_table) > expout AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout]) # Change the configuration. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=main \ -- --id=@t1 create Flow_Table flow-limit=1024 \ -- set bridge br0 'flow_tables={1=@t1,0=@t0}' \ | uuidfilt], [0], [<0> <1> ]) # Check that the configuration was updated. (head_table ' ("main")'; echo ' table 1: active=0, lookup=0, matched=0 metadata: match=0xffffffffffffffff write=0xffffffffffffffff config=controller max_entries=1024 (same instructions) (same matching) table 2: active=0, lookup=0, matched=0 metadata: match=0xffffffffffffffff write=0xffffffffffffffff config=controller max_entries=1000000 (same instructions) (same matching) '; printf ' tables 3...252: ditto\n\n'; tail_table) > expout AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - table features (OpenFlow 1.3)]) OVS_VSWITCHD_START head_table () { printf ' table 0%s: metadata: match=0xffffffffffffffff write=0xffffffffffffffff max_entries=1000000 instructions (table miss and others): next tables: 1-253 instructions: meter apply_actions clear_actions write_actions write_metadata goto_table Write-Actions and Apply-Actions features: actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue supported on Set-Field: tun_{id,src,dst,ipv6_{src,dst},flags,gbp_{id,flags},erspan_{idx,ver,dir,hwid},metadata0...metadata63} metadata in_{port,port_oxm} pkt_mark ct_{mark,label} reg0...reg31 xreg0...xreg15 xxreg0...xxreg7 eth_{src,dst} vlan_{tci,vid,pcp} mpls_{label,tc,ttl} ip_{src,dst} ipv6_{src,dst,label} nw_tos ip_dscp nw_{ecn,ttl} arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst} icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll,reserved,options_type} nsh_{flags,spi,si,c1...c4,ttl} matching: arbitrary mask: dp_hash tun_{id,src,dst,ipv6_{src,dst},flags,gbp_{id,flags},erspan_{idx,ver,dir,hwid},gtpu_{flags,msgtype},metadata0...metadata63} metadata pkt_mark ct_{state,mark,label,nw_{src,dst},ipv6_{src,dst},tp_{src,dst}} reg0...reg31 xreg0...xreg15 xxreg0...xxreg7 eth_{src,dst} vlan_{tci,vid} ip_{src,dst} ipv6_{src,dst,label} ip_frag arp_{spa,tpa,sha,tha} tcp_{src,dst,flags} udp_{src,dst} sctp_{src,dst} nd_{target,sll,tll} nsh_{flags,c1...c4} exact match or wildcard: recirc_id packet_type conj_id in_{port,port_oxm} actset_output ct_{zone,nw_proto} eth_type vlan_pcp mpls_{label,tc,bos,ttl} nw_{proto,tos} ip_dscp nw_{ecn,ttl} arp_op icmp_{type,code} icmpv6_{type,code} nd_{reserved,options_type} nsh_{mdtype,np,spi,si,ttl} ' "$1" } tail_tables() { echo ' table 253: metadata: match=0xffffffffffffffff write=0xffffffffffffffff max_entries=1000000 instructions (table miss and others): instructions: meter apply_actions clear_actions write_actions write_metadata (same actions) (same matching) ' } (head_table printf ' tables 1...252: ditto\n\n' tail_tables) > expout AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout]) # Change the configuration. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=main \ -- --id=@t1 create Flow_Table flow-limit=1024 \ -- set bridge br0 'flow_tables={1=@t1,0=@t0}' \ | uuidfilt], [0], [<0> <1> ]) # Check that the configuration was updated. (head_table ' ("main")' echo ' table 1: metadata: match=0xffffffffffffffff write=0xffffffffffffffff max_entries=1024 (same instructions) (same matching) table 2: metadata: match=0xffffffffffffffff write=0xffffffffffffffff max_entries=1000000 (same instructions) (same matching) tables 3...252: ditto ' tail_tables) > expout AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow table names]) OVS_VSWITCHD_START add_of_ports br0 1 2 # Set a table name via OpenFlow 1.3 and one via OpenFlow 1.5. AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:xyzzy]) AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:quux]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("xyzzy"): table 1 ("quux"): ditto tables 2...252: ditto table 253: ]) # Set some table names via OVSDB. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=zero \ -- --id=@t1 create Flow_Table name=one \ -- --id=@t2 create Flow_Table name=two \ -- set bridge br0 'flow_tables={0=@t0,1=@t1,2=@t2}' \ | uuidfilt], [0], [<0> <1> <2> ]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2 ("two"): ditto tables 3...252: ditto table 253: ]) # Check that flow table parsing and dumping uses the names. AT_DATA([flows.txt], [dnl table=zero in_port=p2 actions=p1,resubmit(,one) table=one,in_port=p1,ip,actions=ct(table=two) table=one,in_port=p1,arp,actions=goto_table(two) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl --names --no-stats dump-flows br0], [0], [dnl table=zero, in_port=p2 actions=output:p1,resubmit(,one) table=one, ip,in_port=p1 actions=ct(table=two) table=one, arp,in_port=p1 actions=resubmit(,two) ]) AT_CHECK([ovs-ofctl --no-names --no-stats dump-flows br0], [0], [dnl in_port=2 actions=output:1,resubmit(,1) table=1, ip,in_port=1 actions=ct(table=2) table=1, arp,in_port=1 actions=resubmit(,2) ]) # Setting the same table names via OpenFlow 1.3 or OpenFlow 1.5 is a no-op. AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:zero]) AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:one]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2 ("two"): ditto tables 3...252: ditto table 253: ]) # Setting different tables names via OpenFlow 1.3 or OpenFlow 1.5 yield errors. AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:xyzzy], 1, [], [stderr]) AT_CHECK([head -1 stderr], [0], [OFPT_ERROR (OF1.3) (xid=0x5): OFPTFFC_EPERM ]) AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:quux], 1, [], [stderr]) AT_CHECK([head -1 stderr], [0], [OFPT_ERROR (OF1.5) (xid=0x5): OFPTFFC_EPERM ]) # But we can still set table names for those not set via OVSDB. AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 3 name:three]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2 ("two"): ditto table 3 ("three"): ditto tables 4...252: ditto table 253: ]) # Unsetting names via OVSDB then setting them via OpenFlow works too. AT_CHECK([ovs-vsctl remove bridge br0 Flow_Table 2]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2: ditto table 3 ("three"): ditto tables 4...252: ditto table 253: ]) AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 2 name:foobar]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2 ("foobar"): ditto table 3 ("three"): ditto tables 4...252: ditto table 253: ]) # We can clear names via OpenFlow, at least if they were set that way. AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 2 name:]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'], [0], [dnl table 0 ("zero"): table 1 ("one"): ditto table 2: ditto table 3 ("three"): ditto tables 4...252: ditto table 253: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - table description (OpenFlow 1.4)]) OVS_VSWITCHD_START (x=0 while test $x -lt 254; do y=`expr $x + 1` echo " table $x: eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME vacancy=off" x=$y done) > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d /^OFPST_TABLE_DESC/d'], [0], [expout]) # Change the configuration. AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 evict]) # Check that the configuration was updated. mv expout orig-expout sed -e '2s/eviction=off/eviction=on/' expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d /^OFPST_TABLE_DESC/d'], [0], [expout]) AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 vacancy:20,80]) # Check that the configuration was updated. mv expout orig-expout sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=100%/' expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d /^OFPST_TABLE_DESC/d'], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - hard limits on flow table size (OpenFlow 1.0)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table flow-limit=4 \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. for in_port in 1 2 3 4; do ovs-ofctl add-flow br0 in_port=$in_port,actions=drop done AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=drop in_port=2 actions=drop in_port=3 actions=drop in_port=4 actions=drop NXST_FLOW reply: ]) # Adding another flow will be refused. AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR: OFPFMFC_TABLE_FULL OFPT_FLOW_MOD: ADD in_port=5 actions=drop ]) # Also a mod-flow that would add a flow will be refused. AT_CHECK([ovs-ofctl mod-flows br0 in_port=5,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR: OFPFMFC_TABLE_FULL OFPT_FLOW_MOD: MOD in_port=5 actions=drop ]) # Replacing or modifying an existing flow is allowed. AT_CHECK([ovs-ofctl add-flow br0 in_port=4,actions=normal]) AT_CHECK([ovs-ofctl mod-flows br0 in_port=3,actions=output:1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=drop in_port=2 actions=drop in_port=3 actions=output:1 in_port=4 actions=NORMAL NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - hard limits on flow table size (OpenFlow 1.2)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table flow-limit=4 \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. for in_port in 1 2 3 4; do ovs-ofctl -O OpenFlow12 add-flow br0 in_port=$in_port,actions=drop done AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=drop in_port=2 actions=drop in_port=3 actions=drop in_port=4 actions=drop OFPST_FLOW reply (OF1.2): ]) # Adding another flow will be refused. AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=5,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR (OF1.2): OFPFMFC_TABLE_FULL OFPT_FLOW_MOD (OF1.2): ADD in_port=5 actions=drop ]) # Replacing or modifying an existing flow is allowed. AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=4,actions=normal]) AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=3,actions=output:1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=1 actions=drop in_port=2 actions=drop in_port=3 actions=output:1 in_port=4 actions=NORMAL OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - eviction upon table overflow (OpenFlow 1.0)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table flow-limit=4 overflow-policy=evict \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. for in_port in 4 3 2 1; do ovs-ofctl add-flow br0 idle_timeout=${in_port}0,in_port=$in_port,actions=drop done AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=10, in_port=1 actions=drop idle_timeout=20, in_port=2 actions=drop idle_timeout=30, in_port=3 actions=drop idle_timeout=40, in_port=4 actions=drop NXST_FLOW reply: ]) # Adding another flow will cause the one that expires soonest to be evicted. AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=20, in_port=2 actions=drop idle_timeout=30, in_port=3 actions=drop idle_timeout=40, in_port=4 actions=drop in_port=5 actions=drop NXST_FLOW reply: ]) # A mod-flow that adds a flow also causes eviction, but replacing or # modifying an existing flow doesn't. AT_CHECK([ovs-ofctl mod-flows br0 in_port=6,actions=drop]) AT_CHECK([ovs-ofctl add-flow br0 in_port=4,actions=normal]) AT_CHECK([ovs-ofctl mod-flows br0 in_port=3,actions=output:1]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=30, in_port=3 actions=output:1 in_port=4 actions=NORMAL in_port=5 actions=drop in_port=6 actions=drop NXST_FLOW reply: ]) # Flows with no timeouts at all cannot be evicted. AT_CHECK([ovs-ofctl add-flow br0 in_port=7,actions=normal]) AT_CHECK([ovs-ofctl add-flow br0 in_port=8,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR: OFPFMFC_TABLE_FULL OFPT_FLOW_MOD: ADD in_port=8 actions=drop ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=4 actions=NORMAL in_port=5 actions=drop in_port=6 actions=drop in_port=7 actions=NORMAL NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - eviction upon table overflow (OpenFlow 1.2)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table flow-limit=4 overflow-policy=evict \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. for in_port in 4 3 2 1; do ovs-ofctl -O OpenFlow12 add-flow br0 idle_timeout=${in_port}0,in_port=$in_port,actions=drop done AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=10, in_port=1 actions=drop idle_timeout=20, in_port=2 actions=drop idle_timeout=30, in_port=3 actions=drop idle_timeout=40, in_port=4 actions=drop OFPST_FLOW reply (OF1.2): ]) # Adding another flow will cause the one that expires soonest to be evicted. AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=5,actions=drop]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=20, in_port=2 actions=drop idle_timeout=30, in_port=3 actions=drop idle_timeout=40, in_port=4 actions=drop in_port=5 actions=drop OFPST_FLOW reply (OF1.2): ]) # In OpenFlow 1.2 a mod-flow does not ever add a flow and thus # has no effect on eviction AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=6,actions=drop]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=4,actions=normal]) AT_CHECK([ovs-ofctl -O OpenFlow12 mod-flows br0 in_port=3,actions=output:1]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=20, in_port=2 actions=drop idle_timeout=30, in_port=3 actions=output:1 in_port=4 actions=NORMAL in_port=5 actions=drop OFPST_FLOW reply (OF1.2): ]) # Flows with no timeouts at all cannot be evicted. AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=6,actions=drop]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=7,actions=normal]) AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 in_port=8,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR (OF1.2): OFPFMFC_TABLE_FULL OFPT_FLOW_MOD (OF1.2): ADD in_port=8 actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=4 actions=NORMAL in_port=5 actions=drop in_port=6 actions=drop in_port=7 actions=NORMAL OFPST_FLOW reply (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - eviction using importance upon table overflow (OpenFlow 1.4)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=evict flow-limit=4 \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Use mod-table to turn on eviction just to demonstrate that it works. AT_CHECK([ovs-ofctl -O OpenFlow14 mod-table br0 0 evict]) # Add 4 flows. for in_port in 4 3 2 1; do ovs-ofctl -O Openflow14 add-flow br0 importance=$((in_port + 30)),priority=$((in_port + 5)),hard_timeout=$((in_port + 500)),actions=drop done AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=501, importance=31, priority=6 actions=drop hard_timeout=502, importance=32, priority=7 actions=drop hard_timeout=503, importance=33, priority=8 actions=drop hard_timeout=504, importance=34, priority=9 actions=drop OFPST_FLOW reply (OF1.4): ]) # Adding another flow will cause the one with lowest importance to be evicted. AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 hard_timeout=505,importance=35,priority=10,in_port=2,actions=drop]) AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=502, importance=32, priority=7 actions=drop hard_timeout=503, importance=33, priority=8 actions=drop hard_timeout=504, importance=34, priority=9 actions=drop hard_timeout=505, importance=35, priority=10,in_port=2 actions=drop OFPST_FLOW reply (OF1.4): ]) # Disable the Eviction configuration. AT_CHECK([ovs-ofctl -O OpenFlow14 mod-table br0 0 noevict]) # Adding another flow will cause the system to give error for FULL TABLE. AT_CHECK([ovs-ofctl -O Openflow14 add-flow br0 hard_timeout=506,importance=36,priority=11,actions=drop],[1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR (OF1.4): OFPFMFC_TABLE_FULL OFPT_FLOW_MOD (OF1.4): ADD priority=11 hard:506 importance:36 actions=drop ]) #Dump flows. It should show only the old values AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=502, importance=32, priority=7 actions=drop hard_timeout=503, importance=33, priority=8 actions=drop hard_timeout=504, importance=34, priority=9 actions=drop hard_timeout=505, importance=35, priority=10,in_port=2 actions=drop OFPST_FLOW reply (OF1.4): ]) # mod-flow that would modify a flow will be done successfully. AT_CHECK([ovs-ofctl -O Openflow14 mod-flows br0 in_port=2,actions=NORMAL]) AT_CHECK([ovs-ofctl -O Openflow14 dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=502, importance=32, priority=7 actions=drop hard_timeout=503, importance=33, priority=8 actions=drop hard_timeout=504, importance=34, priority=9 actions=drop hard_timeout=505, importance=35, priority=10,in_port=2 actions=NORMAL OFPST_FLOW reply (OF1.4): ]) # Also a mod-flow that would add a flow will be refused. AT_CHECK([ovs-ofctl mod-flows br0 in_port=5,actions=drop], [1], [], [stderr]) AT_CHECK([ofctl_strip < stderr], [0], [OFPT_ERROR: OFPFMFC_TABLE_FULL OFPT_FLOW_MOD: MOD in_port=5 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - eviction upon table overflow, with fairness (OpenFlow 1.0)]) OVS_VSWITCHD_START # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table name=evict flow-limit=4 \ overflow-policy=evict \ groups='"NXM_OF_IN_PORT[[]]"' \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. ovs-ofctl add-flows br0 - < ]) # Add 4 flows. ovs-ofctl -O OpenFlow12 add-flows br0 - < ]) ovs-appctl time/stop # Add 4 flows. for in_port in 4 3 2 1; do ovs-ofctl add-flow br0 hard_timeout=$((10 + in_port * 3)),in_port=$in_port,actions=drop done AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=13, in_port=1 actions=drop hard_timeout=16, in_port=2 actions=drop hard_timeout=19, in_port=3 actions=drop hard_timeout=22, in_port=4 actions=drop NXST_FLOW reply: ]) # Sleep and modify the one that expires soonest ovs-appctl time/warp 5000 AT_CHECK([ovs-ofctl mod-flows br0 in_port=1,actions=drop]) # At this point the table would looks like: # in_port seconds to expire # 1 13 # 2 11 # 3 14 # 4 17 ovs-appctl time/warp 2000 # Adding another flow will cause the one that expires soonest to be evicted. AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl hard_timeout=13, in_port=1 actions=drop hard_timeout=19, in_port=3 actions=drop hard_timeout=22, in_port=4 actions=drop in_port=5 actions=drop NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - eviction upon table overflow, with modified idle timeout]) OVS_VSWITCHD_START([add-port br0 p1 -- set interface p1 type=dummy ofport_request=1]) # Configure a maximum of 4 flows. AT_CHECK( [ovs-vsctl \ -- --id=@t0 create Flow_Table flow-limit=4 overflow-policy=evict \ -- set bridge br0 flow_tables:0=@t0 \ | uuidfilt], [0], [<0> ]) # Add 4 flows. for in_port in 4 3 2 1; do ovs-ofctl add-flow br0 idle_timeout=$((10 + in_port * 3)),in_port=$in_port,actions=drop done ovs-appctl time/stop AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=13, in_port=1 actions=drop idle_timeout=16, in_port=2 actions=drop idle_timeout=19, in_port=3 actions=drop idle_timeout=22, in_port=4 actions=drop NXST_FLOW reply: ]) # Sleep and receive on the flow that expires soonest ovs-appctl time/warp 5000 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1)']) # At this point the table would looks like: # in_port seconds to expire # 1 13 # 2 11 # 3 14 # 4 17 ovs-appctl time/warp 2000 # Adding another flow will cause the one that expires soonest to be evicted. AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=19, in_port=3 actions=drop idle_timeout=22, in_port=4 actions=drop in_port=5 actions=drop n_packets=1, n_bytes=14, idle_timeout=13, in_port=1 actions=drop NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.0)]) OVS_VSWITCHD_START([set bridge br0 other_config:hwaddr=00:01:02:03:04:05]) AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) check_async () { printf '\n\n--- check_async %d ---\n\n\n' $1 shift ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log : > expout # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0) ovs-ofctl -v packet-out br0 controller controller '0001020304050010203040501234' if test X"$1" = X"OFPR_ACTION"; then shift; echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123) ovs-ofctl -v packet-out br0 controller 'controller(reason=no_match,id=123)' '0001020304050010203040501234' if test X"$1" = X"OFPR_NO_MATCH"; then shift; echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0) ovs-ofctl packet-out br0 "in_port=controller packet=002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00 actions=dec_ttl" if test X"$1" = X"OFPR_INVALID_TTL"; then shift; echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=CONTROLLER (via invalid_ttl) data_len=76 (unbuffered) udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=55155,tp_dst=53 udp_csum:8f6d" fi # OFPT_PORT_STATUS, OFPPR_ADD ovs-vsctl add-port br0 test -- set Interface test type=dummy ofport_request=1 if test X"$1" = X"OFPPR_ADD"; then shift; echo >>expout "OFPT_PORT_STATUS: ADD: 1(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_DELETE ovs-vsctl del-port br0 test if test X"$1" = X"OFPPR_DELETE"; then shift; echo >>expout "OFPT_PORT_STATUS: DEL: 1(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max" fi # OFPT_FLOW_REMOVED, OFPRR_DELETE ovs-ofctl add-flow br0 send_flow_rem,actions=drop ovs-ofctl --strict del-flows br0 '' if test X"$1" = X"OFPRR_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED: reason=delete" fi AT_FAIL_IF([test X"$1" != X]) OVS_WAIT_UNTIL([test `wc -l < "monitor.log"` -ge `wc -l < "expout"`]) AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/00:0.$/00:0x/' < monitor.log]], [0], [expout]) } # It's a service connection so initially there should be no async messages. check_async 1 # Set miss_send_len to 128, turning on packet-ins for our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE # Set miss_send_len to 128 and enable invalid_ttl. ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700040080 check_async 3 OFPR_ACTION OFPR_INVALID_TTL OFPPR_ADD OFPPR_DELETE OFPRR_DELETE # Become secondary, which should disable everything except port status. ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000002 check_async 4 OFPPR_ADD OFPPR_DELETE # Use NXT_SET_ASYNC_CONFIG to enable a patchwork of asynchronous messages. ovs-appctl -t ovs-ofctl ofctl/send 01040028000000020000232000000013000000020000000500000005000000020000000200000005 check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE # Set controller ID 123. ovs-appctl -t ovs-ofctl ofctl/send 01040018000000030000232000000014000000000000007b check_async 6 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE # Restore controller ID 0. ovs-appctl -t ovs-ofctl ofctl/send 010400180000000300002320000000140000000000000000 # Become primary. ovs-appctl -t ovs-ofctl ofctl/send 0104001400000002000023200000000a00000001 check_async 7 OFPR_ACTION OFPPR_ADD OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.2)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow12 -P standard monitor br0 --detach --no-chdir --pidfile]) check_async () { printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log : > expout # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0) ovs-ofctl -O OpenFlow12 -v packet-out br0 none controller '0001020304050010203040501234' if test X"$1" = X"OFPR_ACTION"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123) ovs-ofctl -O OpenFlow12 -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234' if test X"$1" = X"OFPR_NO_MATCH"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0) ovs-ofctl -O OpenFlow12 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00' if test X"$1" = X"OFPR_INVALID_TTL"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.2): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered) udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=55155,tp_dst=53 udp_csum:8f6d" fi # OFPT_PORT_STATUS, OFPPR_ADD ovs-vsctl add-port br0 test -- set Interface test type=dummy if test X"$1" = X"OFPPR_ADD"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.2): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.2): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_DELETE ovs-vsctl del-port br0 test if test X"$1" = X"OFPPR_DELETE"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.2): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_FLOW_REMOVED, OFPRR_DELETE ovs-ofctl -O OpenFlow12 add-flow br0 send_flow_rem,actions=drop ovs-ofctl -O OpenFlow12 --strict del-flows br0 '' if test X"$1" = X"OFPRR_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED (OF1.2): reason=delete table_id=0" fi AT_FAIL_IF([test X"$1" != X]) OVS_WAIT_UNTIL([test `wc -l < "monitor.log"` -ge `wc -l < "expout"`]) AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/00:0.$/00:0x/' < monitor.log]], [0], [expout]) } # It's a service connection so initially there should be no async messages. check_async 1 # Set miss_send_len to 128, turning on packet-ins for our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080 check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE # Set miss_send_len to 128 and enable invalid_ttl. ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700040080 check_async 3 OFPR_ACTION OFPR_INVALID_TTL OFPPR_ADD OFPPR_DELETE OFPRR_DELETE # Become secondary (OF 1.2), which should disable everything except port status. ovs-appctl -t ovs-ofctl ofctl/send 031800180000000200000003000000000000000000000001 check_async 4 OFPPR_ADD OFPPR_DELETE # Use NXT_SET_ASYNC_CONFIG to enable a patchwork of asynchronous messages. ovs-appctl -t ovs-ofctl ofctl/send 03040028000000020000232000000013000000020000000500000005000000020000000200000005 check_async 5 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE # Set controller ID 123. ovs-appctl -t ovs-ofctl ofctl/send 03040018000000030000232000000014000000000000007b check_async 6 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE # Restore controller ID 0. ovs-appctl -t ovs-ofctl ofctl/send 030400180000000300002320000000140000000000000000 # Become primary (OF 1.2). ovs-appctl -t ovs-ofctl ofctl/send 031800180000000400000002000000000000000000000002 check_async 7 OFPR_ACTION OFPPR_ADD OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.3)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ofport_request=10 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 -P standard monitor br0 --detach --no-chdir --pidfile]) check_async () { printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log : > expout # OFPT_PACKET_IN, OFPR_ACTION (controller_id=0) # OFPR_ACTION_SET is treated as OFPR_ACTION in OpenFlow 1.3 ovs-ofctl -O OpenFlow13 -v packet-out br0 none controller '0001020304050010203040501234' ovs-ofctl -O OpenFlow13 add-flow br0 'in_port=10 actions=write_actions(output(CONTROLLER))' ovs-appctl netdev-dummy/receive p1 'in_port(10),eth(src=00:10:20:30:40:50,dst=00:01:02:03:04:05),eth_type(0x1234)' if test X"$1" = X"OFPR_ACTION"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" echo >>expout "OFPT_PACKET_IN (OF1.3): cookie=0x0 total_len=14 in_port=10 (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123) ovs-ofctl -O OpenFlow13 -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234' if test X"$1" = X"OFPR_NO_MATCH"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0) ovs-ofctl -O OpenFlow13 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00' if test X"$1" = X"OFPR_INVALID_TTL"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.3): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered) udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=55155,tp_dst=53 udp_csum:8f6d" fi # OFPT_PORT_STATUS, OFPPR_ADD ovs-vsctl add-port br0 test -- set Interface test type=dummy if test X"$1" = X"OFPPR_ADD"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.3): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.3): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_DELETE ovs-vsctl del-port br0 test if test X"$1" = X"OFPPR_DELETE"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.3): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_FLOW_REMOVED, OFPRR_DELETE ovs-ofctl -O OpenFlow13 add-flow br0 send_flow_rem,actions=drop ovs-ofctl -O OpenFlow13 --strict del-flows br0 '' if test X"$1" = X"OFPRR_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED (OF1.3): reason=delete table_id=0" fi # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE ovs-ofctl -O OpenFlow13 add-group br0 group_id=1234,type=all,bucket=output:10 ovs-ofctl -O OpenFlow13 add-flow br0 send_flow_rem,actions=group:1234 ovs-ofctl -O OpenFlow13 --strict del-groups br0 group_id=1234 if test X"$1" = X"OFPRR_GROUP_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED (OF1.3): reason=group_delete table_id=0" fi AT_FAIL_IF([test X"$1" != X]) OVS_WAIT_UNTIL([test `wc -l < "monitor.log"` -ge `wc -l < "expout"`]) AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/00:0.$/00:0x/' < monitor.log]], [0], [expout]) } # It's a service connection so initially there should be no async messages. check_async 1 # Set miss_send_len to 128, turning on packet-ins for our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE # Become secondary (OF 1.3), which should disable everything except port status. ovs-appctl -t ovs-ofctl ofctl/send 041800180000000200000003000000000000000000000001 check_async 3 OFPPR_ADD OFPPR_DELETE # Use OF 1.3 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages. ovs-appctl -t ovs-ofctl ofctl/send 041c00200000000200000002000000050000000500000002000000020000000d check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE # Set controller ID 123. ovs-appctl -t ovs-ofctl ofctl/send 04040018000000030000232000000014000000000000007b check_async 5 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE # Restore controller ID 0. ovs-appctl -t ovs-ofctl ofctl/send 040400180000000300002320000000140000000000000000 # Become primary (OF 1.3). ovs-appctl -t ovs-ofctl ofctl/send 041800180000000400000002000000000000000000000002 check_async 6 OFPR_ACTION OFPPR_ADD OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.4)]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy ofport_request=10 ]) AT_CHECK([ovs-ofctl -O OpenFlow14 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs_appctl () { echo ovs-appctl "$@" AT_CHECK([ovs-appctl "$@"], [0], [ignore], [ignore]) } ovs_ofctl () { echo ovs-ofctl --no-names "$@" AT_CHECK([ovs-ofctl --no-names "$@"]) } ovs_vsctl () { echo ovs-vsctl "$@" AT_CHECK([ovs-vsctl "$@"]) } check_async () { printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift ovs_appctl -t ovs-ofctl ofctl/barrier ovs_appctl -t ovs-ofctl ofctl/set-output-file monitor.log : > expout # OFPT_PACKET_IN, OFPR_PACKET_OUT (controller_id=0) ovs_ofctl -O OpenFlow14 packet-out br0 none controller '0001020304050010203040501234' if test X"$1" = X"OFPR_PACKET_OUT"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=14 in_port=ANY (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_ACTION_SET (controller_id=0) ovs_ofctl -O OpenFlow14 add-flow br0 'in_port=10 actions=write_actions(output(CONTROLLER))' ovs_appctl netdev-dummy/receive p1 'in_port(10),eth(src=00:10:20:30:40:50,dst=00:01:02:03:04:05),eth_type(0x1234)' if test X"$1" = X"OFPR_ACTION_SET"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.4): cookie=0x0 total_len=14 in_port=10 (via action_set) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123) ovs_ofctl -O OpenFlow14 packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234' if test X"$1" = X"OFPR_NO_MATCH"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=14 in_port=ANY (via no_match) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234" fi # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0) ovs_ofctl -O OpenFlow14 packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00' if test X"$1" = X"OFPR_INVALID_TTL"; then shift; echo >>expout "OFPT_PACKET_IN (OF1.4): total_len=76 in_port=ANY (via invalid_ttl) data_len=76 (unbuffered) udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=55155,tp_dst=53 udp_csum:8f6d" fi # OFPT_PORT_STATUS, OFPPR_ADD ovs_vsctl add-port br0 test -- set Interface test type=dummy if test X"$1" = X"OFPPR_ADD"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.4): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_MODIFY ovs_ofctl -O OpenFlow14 -vwarn mod-port br0 test down if test X"$1" = X"OFPPR_MODIFY"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_DELETE ovs_vsctl del-port br0 test if test X"$1" = X"OFPPR_DELETE"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.4): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max" fi # OFPT_FLOW_REMOVED, OFPRR_DELETE ovs_ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=drop ovs-ofctl -O OpenFlow14 dump-flows br0 ovs_ofctl -O OpenFlow14 --strict del-flows br0 '' ovs-ofctl -O OpenFlow14 dump-flows br0 if test X"$1" = X"OFPRR_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=delete table_id=0" fi # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE ovs_ofctl -O OpenFlow14 add-group br0 group_id=1234,type=all,bucket=output:10 ovs_ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=group:1234 ovs_ofctl -O OpenFlow14 --strict del-groups br0 group_id=1234 if test X"$1" = X"OFPRR_GROUP_DELETE"; then shift; echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=group_delete table_id=0" fi # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP if test X"$1" = X"OFPTR_VACANCY_UP"; then shift; AT_CHECK([ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1], [0], [ignore], []) # Turn on vacancy events, then add flows until we're full. # With initial vacancy of 100% and vacancy_up of 80%, so that # vacancy >= vacancy_up, this enables VACANY_DOWN events, so # we get a single such message when vacancy dips below 20%. ovs_ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=6,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=7,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=8,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=9,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=10,actions=2 echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_DOWN table_desc:- table 1: eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%" # Then delete flows until we're empty. Sending the # VACANCY_DOWN message enabled VACANCY_UP events, so we get a # single such message when vacancy rises above 80%. ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=1 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=2 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=3 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=4 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=5 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=6 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=7 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=8 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=9 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=10 echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP table_desc:- table 1: eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME vacancy=on vacancy_down=20% vacancy_up=80% vacancy=90%" # Now approach vacancy from the other direction. First # disable vacancy events. With initial vacancy of 70%, so # that vacancy < vacancy_up, this enables VACANCY_UP events. # That means that filling up the table generates no message, # but deleting all the flows generates VACANCY_UP at the point # vacancy rises above 80%. ovs_ofctl -O OpenFlow14 mod-table br0 1 novacancy ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2 ovs_ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=6,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=7,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=8,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=9,actions=2 ovs_ofctl -O OpenFlow14 add-flow br0 table=1,in_port=10,actions=2 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=1 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=2 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=3 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=4 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=5 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=6 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=7 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=8 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=9 ovs_ofctl -O OpenFlow14 del-flows br0 table=1,in_port=10 echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP table_desc:- table 1: eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME vacancy=on vacancy_down=20% vacancy_up=80% vacancy=90%" fi AT_FAIL_IF([test X"$1" != X]) normalize_log () { sed ' s/ (xid=0x[[0-9a-fA-F]]*)// s/ *duration.*// s/00:0.$/00:0x/' < monitor.log } OVS_WAIT_UNTIL([test `wc -l < "monitor.log"` -ge `wc -l < "expout"`], [normalize_log | diff -u - expout]) AT_CHECK( [normalize_log], [0], [expout]) } # It's a service connection so initially there should be no async messages. check_async 1 # Set miss_send_len to 128, turning on packet-ins for our service connection. ovs_appctl -t ovs-ofctl ofctl/send 0509000c0123456700000080 check_async 2 OFPR_PACKET_OUT OFPR_ACTION_SET OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE # Become secondary (OF 1.4), which should disable everything except port status. ovs_appctl -t ovs-ofctl ofctl/send 051800180000000200000003000000000000000000000001 check_async 3 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE # Use OF 1.4 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages. ovs_appctl -t ovs-ofctl ofctl/send 051c0040000000020000000800000005000100080000002000020008000000020003000800000005000400080000001c00050008000000050008000800000018 check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE OFPTR_VACANCY_UP # Set controller ID 123. ovs_appctl -t ovs-ofctl ofctl/send 05040018000000030000232000000014000000000000007b check_async 5 OFPR_NO_MATCH OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE # Restore controller ID 0. ovs_appctl -t ovs-ofctl ofctl/send 050400180000000300002320000000140000000000000000 # Become primary (OF 1.4). ovs_appctl -t ovs-ofctl ofctl/send 051800180000000400000002000000000000000000000002 check_async 6 OFPR_PACKET_OUT OFPPR_ADD OFPPR_MODIFY OFPRR_DELETE OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - asynchronous message control (OpenFlow 1.5)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) check_async () { printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log : > expout # Other tests are not working with OF 1.5, and message # format may change, so leave them out. # OFPT_PORT_STATUS, OFPPR_ADD ovs-vsctl add-port br0 test -- set Interface test type=dummy if test X"$1" = X"OFPPR_ADD"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.5): ADD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.5): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: 0 state: LIVE speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_MODIFY ovs-ofctl -O OpenFlow15 -vwarn mod-port br0 test down if test X"$1" = X"OFPPR_MODIFY"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.5): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: 0 speed: 0 Mbps now, 0 Mbps max OFPT_PORT_STATUS (OF1.5): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max" fi # OFPT_PORT_STATUS, OFPPR_DELETE ovs-vsctl del-port br0 test if test X"$1" = X"OFPPR_DELETE"; then shift; echo >>expout "OFPT_PORT_STATUS (OF1.5): DEL: ${INDEX}(test): addr:aa:55:aa:55:00:0x config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max" fi AT_FAIL_IF([test X"$1" != X]) OVS_WAIT_UNTIL([test `wc -l < "monitor.log"` -ge `wc -l < "expout"`]) AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)// s/ *duration.*// s/00:0.$/00:0x/' < monitor.log]], [0], [expout]) } # It's a service connection so initially there should be no async messages. check_async 1 # If we don't set this, async messages are not received. # Set miss_send_len to 128, turning on packet-ins for our service connection. ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 check_async 2 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE # Set-async has changed in OF 1.4 and is not yet implemented. OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that the role request/response messaging works dnl and that generation_id is handled properly. AT_SETUP([ofproto - controller role (OpenFlow 1.2)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([experr1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([experr2]) for i in 1 2; do AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile=c$i.pid --unixctl=c$i]) ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i : > experr$i # find out current role ovs-appctl -t `pwd`/c$i ofctl/send 031800180000000200000000000000000000000000000000 echo >>experr$i "send: OFPT_ROLE_REQUEST (OF1.2): role=nochange" echo >>expout$i "OFPT_ROLE_REPLY (OF1.2): role=equal" done # controller 1: Become secondary (generation_id is initially undefined, so # 2^63+2 should not be stale) ovs-appctl -t `pwd`/c1 ofctl/send 031800180000000300000003000000008000000000000002 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.2): role=secondary generation_id=9223372036854775810" echo >>expout1 "OFPT_ROLE_REPLY (OF1.2): role=secondary generation_id=9223372036854775810" # controller 2: Become primary. ovs-appctl -t `pwd`/c2 ofctl/send 031800180000000300000002000000008000000000000003 echo >>experr2 "send: OFPT_ROLE_REQUEST (OF1.2): role=primary generation_id=9223372036854775811" echo >>expout2 "OFPT_ROLE_REPLY (OF1.2): role=primary generation_id=9223372036854775811" # controller 1: Try to become the primary using a stale generation ID ovs-appctl -t `pwd`/c1 ofctl/send 031800180000000400000002000000000000000000000003 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.2): role=primary generation_id=3" echo >>expout1 "OFPT_ERROR (OF1.2): OFPRRFC_STALE" echo >>expout1 "OFPT_ROLE_REQUEST (OF1.2): role=primary generation_id=3" # controller 1: Become primary using a valid generation ID ovs-appctl -t `pwd`/c1 ofctl/send 031800180000000500000002000000000000000000000001 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.2): role=primary generation_id=1" echo >>expout1 "OFPT_ROLE_REPLY (OF1.2): role=primary generation_id=1" for i in 1 2; do ovs-appctl -t `pwd`/c$i ofctl/barrier echo >>expout$i "OFPT_BARRIER_REPLY (OF1.2):" done # Check output. for i in 1 2; do cp expout$i expout AT_CHECK([grep -v '^send:' monitor$i.log | strip_xids], [0], [expout]) cp experr$i expout AT_CHECK([grep '^send:' monitor$i.log | strip_xids], [0], [expout]) done OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that the role request/response messaging works, dnl that generation_id is handled properly, and that role status update dnl messages are sent when a controller's role gets changed from primary dnl to secondary. AT_SETUP([ofproto - controller role (OpenFlow 1.4)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([experr1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([experr2]) for i in 1 2; do AT_CHECK([ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile=c$i.pid --unixctl=c$i]) ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i : > experr$i # find out current role ovs-appctl -t `pwd`/c$i ofctl/send 051800180000000200000000000000000000000000000000 echo >>experr$i "send: OFPT_ROLE_REQUEST (OF1.4): role=nochange" echo >>expout$i "OFPT_ROLE_REPLY (OF1.4): role=equal" done # controller 1: Become secondary (generation_id is initially undefined, so # 2^63+2 should not be stale) ovs-appctl -t `pwd`/c1 ofctl/send 051800180000000300000003000000008000000000000002 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.4): role=secondary generation_id=9223372036854775810" echo >>expout1 "OFPT_ROLE_REPLY (OF1.4): role=secondary generation_id=9223372036854775810" # controller 2: Become primary. ovs-appctl -t `pwd`/c2 ofctl/send 051800180000000300000002000000008000000000000003 echo >>experr2 "send: OFPT_ROLE_REQUEST (OF1.4): role=primary generation_id=9223372036854775811" echo >>expout2 "OFPT_ROLE_REPLY (OF1.4): role=primary generation_id=9223372036854775811" # controller 1: Try to become the primary using a stale generation ID ovs-appctl -t `pwd`/c1 ofctl/send 051800180000000400000002000000000000000000000003 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.4): role=primary generation_id=3" echo >>expout1 "OFPT_ERROR (OF1.4): OFPRRFC_STALE" echo >>expout1 "OFPT_ROLE_REQUEST (OF1.4): role=primary generation_id=3" # controller 1: Become primary using a valid generation ID ovs-appctl -t `pwd`/c1 ofctl/send 051800180000000500000002000000000000000000000001 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.4): role=primary generation_id=1" echo >>expout1 "OFPT_ROLE_REPLY (OF1.4): role=primary generation_id=1" echo >>expout2 "OFPT_ROLE_STATUS (OF1.4): role=secondary generation_id=1 reason=primary_request" for i in 1 2; do ovs-appctl -t `pwd`/c$i ofctl/barrier echo >>expout$i "OFPT_BARRIER_REPLY (OF1.4):" done # Check output. for i in 1 2; do cp expout$i expout AT_CHECK([grep -v '^send:' monitor$i.log | strip_xids], [0], [expout]) cp experr$i expout AT_CHECK([grep '^send:' monitor$i.log | strip_xids], [0], [expout]) done OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that the role request/response messaging works, dnl that generation_id is handled properly, and that role status update dnl messages are sent when a controller's role gets changed from primary dnl to secondary. AT_SETUP([ofproto - controller role (OpenFlow 1.3)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([experr1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([experr2]) for i in 1 2; do AT_CHECK([ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile=c$i.pid --unixctl=c$i]) ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i : > experr$i # find out current role ovs-appctl -t `pwd`/c$i ofctl/send 041800180000000200000000000000000000000000000000 echo >>experr$i "send: OFPT_ROLE_REQUEST (OF1.3): role=nochange" echo >>expout$i "OFPT_ROLE_REPLY (OF1.3): role=equal" done # controller 1: Become secondary (generation_id is initially undefined, so # 2^63+2 should not be stale) ovs-appctl -t `pwd`/c1 ofctl/send 041800180000000300000003000000008000000000000002 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.3): role=secondary generation_id=9223372036854775810" echo >>expout1 "OFPT_ROLE_REPLY (OF1.3): role=secondary generation_id=9223372036854775810" # controller 2: Become primary. ovs-appctl -t `pwd`/c2 ofctl/send 041800180000000300000002000000008000000000000003 echo >>experr2 "send: OFPT_ROLE_REQUEST (OF1.3): role=primary generation_id=9223372036854775811" echo >>expout2 "OFPT_ROLE_REPLY (OF1.3): role=primary generation_id=9223372036854775811" # controller 1: Try to become the primary using a stale generation ID ovs-appctl -t `pwd`/c1 ofctl/send 041800180000000400000002000000000000000000000003 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.3): role=primary generation_id=3" echo >>expout1 "OFPT_ERROR (OF1.3): OFPRRFC_STALE" echo >>expout1 "OFPT_ROLE_REQUEST (OF1.3): role=primary generation_id=3" # controller 1: Become primary using a valid generation ID ovs-appctl -t `pwd`/c1 ofctl/send 041800180000000500000002000000000000000000000001 echo >>experr1 "send: OFPT_ROLE_REQUEST (OF1.3): role=primary generation_id=1" echo >>expout1 "OFPT_ROLE_REPLY (OF1.3): role=primary generation_id=1" echo >>expout2 "ONFT_ROLE_STATUS (OF1.3): role=secondary generation_id=1 reason=primary_request" for i in 1 2; do ovs-appctl -t `pwd`/c$i ofctl/barrier echo >>expout$i "OFPT_BARRIER_REPLY (OF1.3):" done # Check output. for i in 1 2; do cp expout$i expout AT_CHECK([grep -v '^send:' monitor$i.log | strip_xids], [0], [expout]) cp experr$i expout AT_CHECK([grep '^send:' monitor$i.log | strip_xids], [0], [expout]) done OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks the Group and meter notifications when a group mod dnl command is sent from one controller and the reply is received by dnl other controllers. AT_SETUP([ofproto - requestforward (OpenFlow 1.4)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid c3.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([monitor3.log]) AT_CAPTURE_FILE([expout3]) ovs-ofctl -O OpenFlow15 monitor br0 --detach --no-chdir --pidfile=c1.pid --unixctl=c1 ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile=c2.pid --unixctl=c2 ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile=c3.pid --unixctl=c3 check_async () { for i in 1 3; do ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i done printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift # OFPGC_ADD ovs-appctl -t `pwd`/c2 ofctl/send "050f0020000000020000000000000001 00100000 ffffffffffffffff 00000000" if test X"$1" = X"OFPGC_ADD"; then shift; echo >>expout2 "send: OFPT_GROUP_MOD (OF1.4): ADD group_id=1,type=all,bucket=actions=drop" echo >>expout1 "OFPT_REQUESTFORWARD (OF1.5): reason=group_mod ADD group_id=1,type=all,bucket=bucket_id:0,actions=drop" echo >>expout3 "OFPT_REQUESTFORWARD (OF1.4): reason=group_mod ADD group_id=1,type=all,bucket=actions=drop" fi # OFPGC_MODIFY ovs-appctl -t `pwd`/c2 ofctl/send "050f0020000000020001010000000001 00100000 ffffffffffffffff 00000000" if test X"$1" = X"OFPGC_MODIFY"; then shift; echo >>expout2 "send: OFPT_GROUP_MOD (OF1.4): MOD group_id=1,type=select,bucket=weight:0,actions=drop" echo >>expout1 "OFPT_REQUESTFORWARD (OF1.5): reason=group_mod MOD group_id=1,type=select,bucket=bucket_id:0,weight:0,actions=drop" echo >>expout3 "OFPT_REQUESTFORWARD (OF1.4): reason=group_mod MOD group_id=1,type=select,bucket=weight:0,actions=drop" fi ovs-appctl -t `pwd`/c1 ofctl/barrier echo >>expout1 "OFPT_BARRIER_REPLY (OF1.5):" ovs-appctl -t `pwd`/c2 ofctl/barrier echo >>expout2 "OFPT_BARRIER_REPLY (OF1.4):" ovs-appctl -t `pwd`/c3 ofctl/barrier echo >>expout3 "OFPT_BARRIER_REPLY (OF1.4):" # Check output. for i in 1 3; do cp expout$i expout AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)//'< monitor$i.log]], [0], [expout]) done } # controller 1: Become secondary ovs-appctl -t `pwd`/c1 ofctl/send 061800180000000300000003000000008000000000000002 # controller 2: Become primary ovs-appctl -t `pwd`/c2 ofctl/send 051800180000000300000002000000008000000000000003 # controller 1: Become secondary ovs-appctl -t `pwd`/c3 ofctl/send 051800180000000300000003000000008000000000000004 # controller 1: Enabled requestforward using set Asynchronous message ovs-appctl -t `pwd`/c1 ofctl/send 061c00280000000200000008000000050002000800000002000400080000001a000a000800000003 # controller 2: Enabled requestforward using set Asynchronous message ovs-appctl -t `pwd`/c2 ofctl/send 051c002800000002000100080000000200030008000000050005000800000005000b000800000003 # controller 1: Enabled requestforward using set Asynchronous message ovs-appctl -t `pwd`/c3 ofctl/send 051c00280000000200000008000000050002000800000002000400080000001a000a000800000003 check_async 1 OFPGC_ADD OFPGC_MODIFY OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks the backwards compatibility of the NXT_REQUESTFORWARD dnl message type to OpenFlow 1.0, also relying on the Nicira Extensions: dnl NXT_GROUP_MOD, NXT_ROLE_REQUEST, and OFPRAW_NXT_SET_ASYNC_CONFIG2, dnl while also testing the functionality of the previous test. AT_SETUP([ofproto - NXT requestforward (OpenFlow 1.0)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid c3.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([monitor3.log]) AT_CAPTURE_FILE([expout3]) ovs-ofctl -O OpenFlow10 monitor br0 --detach --no-chdir --pidfile=c1.pid --unixctl=c1 ovs-ofctl -O OpenFlow10 monitor br0 --detach --no-chdir --pidfile=c2.pid --unixctl=c2 ovs-ofctl -O OpenFlow10 monitor br0 --detach --no-chdir --pidfile=c3.pid --unixctl=c3 check_async () { for i in 1 3; do ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i done printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift # OFPGC_ADD # NXT_GROUP_MOD (xid=0x2): # ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3 ovs-appctl -t `pwd`/c2 ofctl/send "01 04 00 a8 00 00 00 02 00 00 23 20 00 00 00 1f 00 00 01 00 87 65 43 21 00 60 00 00 ff ff ff ff 00 20 00 08 00 00 00 00 00 00 00 08 00 01 00 00 00 00 00 08 00 64 00 00 00 01 00 08 00 00 00 01 00 20 00 08 00 00 00 01 00 00 00 08 00 02 00 00 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 02 00 20 00 08 00 00 00 02 00 00 00 08 00 03 00 00 00 00 00 08 00 c8 00 00 00 01 00 08 00 00 00 03 ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07" if test X"$1" = X"OFPGC_ADD"; then shift; echo >>expout2 "send: NXT_GROUP_MOD: ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3" echo >>expout1 "NXT_REQUESTFORWARD: reason=group_mod ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3" echo >>expout3 "NXT_REQUESTFORWARD: reason=group_mod ADD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:100,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:200,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:200,watch_port:3,actions=output:3" fi # OFPGC_MODIFY # NXT_GROUP_MOD (xid=0x2): # MOD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:150,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:150,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:150,watch_port:3,actions=output:3 ovs-appctl -t `pwd`/c2 ofctl/send "01 04 00 a8 00 00 00 02 00 00 23 20 00 00 00 1f 00 01 01 00 87 65 43 21 00 60 00 00 ff ff ff ff 00 20 00 08 00 00 00 00 00 00 00 08 00 01 00 00 00 00 00 08 00 96 00 00 00 01 00 08 00 00 00 01 00 20 00 08 00 00 00 01 00 00 00 08 00 02 00 00 00 00 00 08 00 96 00 00 00 01 00 08 00 00 00 02 00 20 00 08 00 00 00 02 00 00 00 08 00 03 00 00 00 00 00 08 00 96 00 00 00 01 00 08 00 00 00 03 ff ff 00 28 00 00 15 40 00 00 00 01 00 00 00 00 68 61 73 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07" if test X"$1" = X"OFPGC_MODIFY"; then shift; echo >>expout2 "send: NXT_GROUP_MOD: MOD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:150,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:150,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:150,watch_port:3,actions=output:3" echo >>expout1 "NXT_REQUESTFORWARD: reason=group_mod MOD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:150,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:150,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:150,watch_port:3,actions=output:3" echo >>expout3 "NXT_REQUESTFORWARD: reason=group_mod MOD group_id=2271560481,type=select,selection_method=hash,selection_method_param=7,bucket=bucket_id:0,weight:150,watch_port:1,actions=output:1,bucket=bucket_id:1,weight:150,watch_port:2,actions=output:2,bucket=bucket_id:2,weight:150,watch_port:3,actions=output:3" fi ovs-appctl -t `pwd`/c1 ofctl/barrier echo >>expout1 "OFPT_BARRIER_REPLY:" ovs-appctl -t `pwd`/c2 ofctl/barrier echo >>expout2 "OFPT_BARRIER_REPLY:" ovs-appctl -t `pwd`/c3 ofctl/barrier echo >>expout3 "OFPT_BARRIER_REPLY:" # Check output. for i in 1 3; do cp expout$i expout AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)//'< monitor$i.log]], [0], [expout]) done } # controller 1: Become secondary # NXT_ROLE_REQUEST (xid=0x3): role=secondary ovs-appctl -t `pwd`/c1 ofctl/send 0104001400000003000023200000000a00000002 # controller 2: Become primary # NXT_ROLE_REQUEST (xid=0x3): role=primary ovs-appctl -t `pwd`/c2 ofctl/send 0104001400000003000023200000000a00000001 # controller 1: Become secondary # NXT_ROLE_REQUEST (xid=0x3): role=secondary ovs-appctl -t `pwd`/c3 ofctl/send 0104001400000003000023200000000a00000002 # controller 1: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 ovs-appctl -t `pwd`/c1 ofctl/send 0104003000000002000023200000001b00000008000000050002000800000002000400080000001a000a000800000003 # controller 2: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 ovs-appctl -t `pwd`/c2 ofctl/send 0104003000000002000023200000001b000100080000000200030008000000050005000800000005000b000800000003 # controller 1: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 ovs-appctl -t `pwd`/c3 ofctl/send 0104003000000002000023200000001b00000008000000050002000800000002000400080000001a000a000800000003 check_async 1 OFPGC_ADD OFPGC_MODIFY OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks the Group and meter notifications when a group mod dnl command is sent from one controller and the reply is received by dnl other controllers, using the ONF Extension for OF 1.3. AT_SETUP([ofproto - ONF requestforward (OpenFlow 1.3)]) OVS_VSWITCHD_START on_exit 'kill `cat c1.pid c2.pid c3.pid`' # Start two ovs-ofctl controller processes. AT_CAPTURE_FILE([monitor1.log]) AT_CAPTURE_FILE([expout1]) AT_CAPTURE_FILE([monitor2.log]) AT_CAPTURE_FILE([expout2]) AT_CAPTURE_FILE([monitor3.log]) AT_CAPTURE_FILE([expout3]) ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile=c1.pid --unixctl=c1 ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile=c2.pid --unixctl=c2 ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile=c3.pid --unixctl=c3 check_async () { for i in 1 3; do ovs-appctl -t `pwd`/c$i ofctl/barrier ovs-appctl -t `pwd`/c$i ofctl/set-output-file monitor$i.log : > expout$i done printf '\n\n--- check_async %d ---\n\n\n' $1 INDEX=$1 shift # OFPGC_ADD # OFPT_GROUP_MOD (OF1.3) (xid=0x2): # ADD group_id=1,type=all,bucket=actions=drop ovs-appctl -t `pwd`/c2 ofctl/send "040f0020000000020000000000000001 00100000 ffffffffffffffff 00000000" if test X"$1" = X"OFPGC_ADD"; then shift; echo >>expout2 "send: OFPT_GROUP_MOD (OF1.3): ADD group_id=1,type=all,bucket=actions=drop" echo >>expout1 "ONFT_REQUESTFORWARD (OF1.3): reason=group_mod ADD group_id=1,type=all,bucket=actions=drop" echo >>expout3 "ONFT_REQUESTFORWARD (OF1.3): reason=group_mod ADD group_id=1,type=all,bucket=actions=drop" fi # OFPGC_MODIFY # OFPT_GROUP_MOD (OF1.3) (xid=0x2): # MOD group_id=1,type=select,bucket=weight:0,actions=drop ovs-appctl -t `pwd`/c2 ofctl/send "040f0020000000020001010000000001 00100000 ffffffffffffffff 00000000" if test X"$1" = X"OFPGC_MODIFY"; then shift; echo >>expout2 "send: OFPT_GROUP_MOD (OF1.3): MOD group_id=1,type=select,bucket=weight:0,actions=drop" echo >>expout1 "ONFT_REQUESTFORWARD (OF1.3): reason=group_mod MOD group_id=1,type=select,bucket=weight:0,actions=drop" echo >>expout3 "ONFT_REQUESTFORWARD (OF1.3): reason=group_mod MOD group_id=1,type=select,bucket=weight:0,actions=drop" fi ovs-appctl -t `pwd`/c1 ofctl/barrier echo >>expout1 "OFPT_BARRIER_REPLY (OF1.3):" ovs-appctl -t `pwd`/c2 ofctl/barrier echo >>expout2 "OFPT_BARRIER_REPLY (OF1.3):" ovs-appctl -t `pwd`/c3 ofctl/barrier echo >>expout3 "OFPT_BARRIER_REPLY (OF1.3):" # Check output. for i in 1 3; do cp expout$i expout AT_CHECK( [[sed ' s/ (xid=0x[0-9a-fA-F]*)//'< monitor$i.log]], [0], [expout]) done } # controller 1: Become secondary # OFPT_ROLE_REQUEST (OF1.3) (xid=0x3): role=secondary ovs-appctl -t `pwd`/c1 ofctl/send 041800180000000300000003000000008000000000000002 # controller 2: Become primary # OFPT_ROLE_REQUEST (OF1.3) (xid=0x3): role=primary ovs-appctl -t `pwd`/c2 ofctl/send 041800180000000300000002000000008000000000000003 # controller 1: Become secondary # OFPT_ROLE_REQUEST (OF1.3) (xid=0x3): role=secondary ovs-appctl -t `pwd`/c3 ofctl/send 041800180000000300000003000000008000000000000004 # controller 1: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 (necessary for OF1.3) ovs-appctl -t `pwd`/c1 ofctl/send 0404003000000002000023200000001b00000008000000050002000800000002000400080000001a000a000800000003 # controller 2: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 (necessary for OF1.3) ovs-appctl -t `pwd`/c2 ofctl/send 0404003000000002000023200000001b000100080000000200030008000000050005000800000005000b000800000003 # controller 1: Enabled requestforward using OFPRAW_NXT_SET_ASYNC_CONFIG2 (necessary for OF1.3) ovs-appctl -t `pwd`/c3 ofctl/send 0404003000000002000023200000001b00000008000000050002000800000002000400080000001a000a000800000003 check_async 1 OFPGC_ADD OFPGC_MODIFY OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as dnl specified by OpenFlow 1.0) and OFPP_CONTROLLER (used by some dnl controllers despite the spec) as meaning a packet that was generated dnl by the controller. AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.0)]) OVS_VSWITCHD_START add_of_ports br0 1 # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port. AT_CHECK([ovs-ofctl packet-out br0 "in_port=none packet=0001020304050010203040501234 actions=controller,1"]) AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040505678 actions=controller,1"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) ovs-ofctl dump-ports br0 AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN: total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678 OFPT_BARRIER_REPLY: ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as dnl specified by OpenFlow 1.2) and OFPP_CONTROLLER (used by some dnl controllers despite the spec) as meaning a packet that was generated dnl by the controller. AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.2)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow12 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port. AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none controller '0001020304050010203040501234']) AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 4294967293 controller '0001020304050010203040505678']) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_PACKET_IN (OF1.2): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678 OFPT_BARRIER_REPLY (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as dnl specified by OpenFlow 1.1) and OFPP_CONTROLLER (used by some dnl controllers despite the spec) as meaning a packet that was generated dnl by the controller. AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.1)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow11 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0209000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER as in_port. AT_CHECK([ovs-appctl -t ovs-ofctl ofctl/packet-out "in_port=none, packet=0001020304050010203040501234 actions=controller"]) AT_CHECK([ovs-appctl -t ovs-ofctl ofctl/packet-out "in_port=controller packet=0001020304050010203040505678 actions=controller"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)// /PACKET_OUT/d' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.1): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_PACKET_IN (OF1.1): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678 OFPT_BARRIER_REPLY (OF1.1): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.5)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow15 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0609000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port. AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=none tun_id=0x11 metadata=0x22 packet=0001020304050010203040501234 actions=controller"]) AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller tun_id=0x11 metadata=0x33 packet=0001020304050010203040505678 actions=controller"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.5): total_len=14 tun_id=0x11,metadata=0x22,in_port=ANY (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_PACKET_IN (OF1.5): total_len=14 tun_id=0x11,metadata=0x33,in_port=CONTROLLER (via packet_out) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x5678 OFPT_BARRIER_REPLY (OF1.5): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that metadata and userdata are encoded in NXT_PACKET_IN2. AT_SETUP([ofproto - packet-out with metadata and userdata (NXT_PACKET_IN2)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P nxt_packet_in2 monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a load action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040501234 actions=load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]),load(0xaa->NXM_NX_PKT_MARK[[]]),controller(userdata=01.02.03.04.05)"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl NXT_PACKET_IN2: total_len=14 pkt_mark=0xaa,metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered) userdata=01.02.03.04.05 vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY: ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that 1.5 packet_out is properly encoded/decoded. AT_SETUP([ofproto - packet-out with set_field metadata (OpenFlow 1.5)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a couple of set-field action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 CONTROLLER 'set_field:0xfafafafa5a5a5a5a->metadata, controller' '0001020304050010203040501234']) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl exit AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3): total_len=14 metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that packet_type PT_ETH is properly encoded/decoded in 1.5 packet_out. AT_SETUP([ofproto - packet-out with set_field metadata with packet_type PT_ETH (OpenFlow 1.5)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a couple of set-field action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=0001020304050010203040501234 packet_type(0,0x0) actions=set_field:0xfafafafa5a5a5a5a->metadata,controller"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl exit AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3): total_len=14 metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that packet_type PT_IPV4 is properly encoded/decoded in 1.5 packet_out. AT_SETUP([ofproto - packet-out with set_field metadata with packet_type PT_IPV4 on PTAP bridge (OpenFlow 1.5)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a couple of set-field action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=4500002012344000ff1155670a0000140a00001e006400c8000cea78ffffffff packet_type(1,0x800) actions=set_field:0xfafafafa5a5a5a5a->metadata,controller"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl exit AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3): total_len=32 packet_type=(1,0x800),metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=32 (unbuffered) packet_type=(1,0x800),nw_src=10.0.0.20,nw_dst=10.0.0.30,nw_proto=17,nw_tos=0,nw_ecn=0,nw_ttl=255,nw_frag=no,tp_src=100,tp_dst=200 udp_csum:ea78 OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that metadata is encoded in packet_in structures, dnl supported by NXAST. AT_SETUP([ofproto - packet-out with metadata (NXM)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -P nxt_packet_in monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a load action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040501234 actions=load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]),load(0xaa->NXM_NX_PKT_MARK[[]]),controller"]) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl NXT_PACKET_IN: total_len=14 pkt_mark=0xaa,metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY: ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that metadata is encoded in packet_in structures, dnl supported by NXAST. AT_SETUP([ofproto - packet-out with metadata (OpenFlow 1.2)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow12 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a set-field action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none 'set_field:0xfafafafa5a5a5a5a->metadata, controller' '0001020304050010203040501234']) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=14 metadata=0xfafafafa5a5a5a5a,in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that metadata is encoded in packet_in structures, dnl supported by NXAST. AT_SETUP([ofproto - packet-out with metadata and dual set_field (OpenFlow 1.3)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow13 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with a couple of set-field action to set some metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 none 'set_field:0xfafafafa5a5a5a5a->metadata, set_field:0x6b->metadata, controller' '0001020304050010203040501234']) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.3): total_len=14 metadata=0x6b,in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test checks that tunnel metadata is encoded in packet_in structures. AT_SETUP([ofproto - packet-out with tunnel metadata (OpenFlow 1.2)]) OVS_VSWITCHD_START # Start a monitor listening for packet-ins. AT_CHECK([ovs-ofctl -O OpenFlow12 -P standard monitor br0 --detach --no-chdir --pidfile]) ovs-appctl -t ovs-ofctl ofctl/send 0309000c0123456700000080 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log AT_CAPTURE_FILE([monitor.log]) # Send a packet-out with set field actions to set some tunnel metadata, and forward to controller AT_CHECK([ovs-ofctl -O OpenFlow12 packet-out br0 none 'set_field:127.0.0.1->tun_src,set_field:0x01020304->tun_id,set_field:192.168.0.1->tun_dst, controller' '0001020304050010203040501234']) # Stop the monitor and check its output. ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl OFPT_PACKET_IN (OF1.2): total_len=14 tun_id=0x1020304,tun_src=127.0.0.1,tun_dst=192.168.0.1,in_port=ANY (via action) data_len=14 (unbuffered) vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234 OFPT_BARRIER_REPLY (OF1.2): ]) OVS_VSWITCHD_STOP AT_CLEANUP m4_divert_push([PREPARE_TESTS]) # Sorts groups of lines that start with a space, without moving them # past the nearest line that does not start with a space. [ multiline_sort () { $PYTHON3 -c ' import sys buffer = [] while True: line = sys.stdin.readline() if not line: break if line.startswith(" "): buffer.append(line) else: sys.stdout.write("".join(sorted(buffer))) sys.stdout.write(line) buffer = [] sys.stdout.write("".join(sorted(buffer))) ' } ] m4_divert_pop([PREPARE_TESTS]) dnl Flow monitoring tests verified across all supported protocols dnl CHECK_FLOW_MONITORING(label, option, format) m4_define([CHECK_FLOW_MONITORING], [ AT_SETUP([ofproto - flow monitoring - (OpenFlow $1)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Get packet data to used as part of ofctl/send operation before doing anything else send_buf=$(ovs-ofctl -O $2 del-flows br0 table=0 -mmmmmm | sed 's/0x/0/' | sed -E 's/^(.{8})(.{8})/\112345678/' ) ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1 # Start a monitor watching the flow table and check the initial reply. ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/barrier # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [$4 reply$3: event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1 OFPT_BARRIER_REPLY$3: ]) # Add, delete, and modify some flows and check the updates. ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log ovs-ofctl add-flow br0 in_port=0,dl_vlan=124,actions=output:2 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:5 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=0,actions=output:6 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8 ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9 ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10 ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=3,actions=output:11 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13 ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:15 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:16 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:17 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:18 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:19 ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:20 ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=0,actions=output:21 ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=1,actions=output:22 ovs-ofctl add-flow br0 in_port=0,actions=output:23 ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:3 ovs-ofctl mod-flows br0 cookie=5,dl_vlan=123,actions=output:3 ovs-ofctl del-flows br0 dl_vlan=123 ovs-ofctl del-flows br0 ovs-appctl -t ovs-ofctl ofctl/barrier # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | multiline_sort], [0], [$4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:6 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:7 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:9 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:10 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:15 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:16 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=0 actions=output:23 $4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:3 event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3 event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3 $4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3 event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3 event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3 $4 reply$3 (xid=0x0): event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3 event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3 event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3 $4 reply$3 (xid=0x0): event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=3 actions=output:11 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21 event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22 OFPT_BARRIER_REPLY$3: ]) # Check that our own changes are reported as full updates. ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log ovs-ofctl add-flow br0 in_port=1,actions=output:2 ovs-ofctl add-flow br0 in_port=2,actions=output:1 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send $send_buf ovs-appctl -t ovs-ofctl ofctl/barrier AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: ]) AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | multiline_sort], [0], [$4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=1 actions=output:2 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=2 actions=output:1 OFPT_BARRIER_REPLY$3: send: OFPT_FLOW_MOD$3: DEL actions=drop $4 reply$3 (xid=0x0): event=DELETED reason=delete table=0 cookie=0 in_port=1 actions=output:2 event=DELETED reason=delete table=0 cookie=0 in_port=2 actions=output:1 OFPT_BARRIER_REPLY$3: ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow monitoring with !own - (OpenFlow $1)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Get packet data to used as part of ofctl/send operation before doing anything else send_buf=$(ovs-ofctl -O $2 del-flows br0 table=0 -mmmmmm | sed 's/0x/0/' | sed -E 's/^(.{8})(.{8})/\112345678/' ) ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1 # Start a monitor watching the flow table and check the initial reply. ovs-ofctl -O $2 monitor br0 watch:\!own --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/barrier # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [$4 reply$3: event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1 OFPT_BARRIER_REPLY$3: ]) # Check that our own changes are reported as abbreviations. ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log ovs-ofctl add-flow br0 in_port=1,actions=output:2 ovs-ofctl add-flow br0 in_port=2,actions=output:1 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send $send_buf ovs-appctl -t ovs-ofctl ofctl/barrier AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply: ]) # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [$4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=1 actions=output:2 $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 in_port=2 actions=output:1 OFPT_BARRIER_REPLY$3: send: OFPT_FLOW_MOD$3: DEL actions=drop $4 reply$3 (xid=0x0): event=ABBREV xid=0x12345678 OFPT_BARRIER_REPLY$3: ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow monitoring with out_port - (OpenFlow $1)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START ovs-ofctl add-flow br0 in_port=0,dl_vlan=121,actions=output:1 ovs-ofctl add-flow br0 in_port=0,dl_vlan=122,actions=output:1 ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:2 # Start a monitor watching the flow table and check the initial reply. ovs-ofctl -O $2 monitor br0 watch:out_port=2 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/barrier # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [$4 reply$3: event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2 OFPT_BARRIER_REPLY$3: ]) ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Add, modify flows and check the updates. ovs-ofctl mod-flows br0 dl_vlan=121,actions=drop ovs-ofctl mod-flows br0 dl_vlan=122,actions=output:1,output:2 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:1,output:2 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-ofctl mod-flows br0 dl_vlan=122,actions=output:1 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-ofctl mod-flows br0 dl_vlan=123,actions=output:2 ovs-appctl -t ovs-ofctl ofctl/barrier # For OF(1.4+), replace INITIAL to ADDED sed -i'.raw' -e 's|event=INITIAL|event=ADDED|' monitor.log AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [$4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1,output:2 OFPT_BARRIER_REPLY$3: $4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1,output:2 OFPT_BARRIER_REPLY$3: $4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=output:1 OFPT_BARRIER_REPLY$3: $4 reply$3 (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:2 OFPT_BARRIER_REPLY$3: ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow monitoring pause and resume - (OpenFlow $1)]) AT_KEYWORDS([monitor]) # The maximum socket receive buffer size is important for this test, which # tests behavior when the receive buffer overflows. if test -e /proc/sys/net/core/rmem_max; then # Linux rmem_max=`cat /proc/sys/net/core/rmem_max` elif rmem_max=`sysctl -n net.inet.tcp.recvbuf_max 2>/dev/null`; then : # FreeBSD, NetBSD else # Don't know how to get maximum socket receive buffer on this OS AT_SKIP_IF([:]) fi # Calculate the total amount of queuing: rmem_max in the kernel, 128 kB # in ofproto sending userspace (see ofmonitor_flush() in connmgr.c). queue_size=`expr $rmem_max + 128 \* 1024` echo rmem_max=$rmem_max queue_size=$queue_size # If there's too much queuing skip the test to avoid timing out. AT_SKIP_IF([test $rmem_max -gt 1048576]) # Each flow update message takes up at least 48 bytes of space in queues # and in practice more than that. n_msgs=`expr $queue_size / 48` echo n_msgs=$n_msgs OVS_VSWITCHD_START # Start a monitor watching the flow table, then make it block. on_exit 'kill `cat ovs-ofctl.pid`' ovs-ofctl -O $2 monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/block # Add $n_msgs flows. (echo "in_port=2,actions=output:2" $PYTHON3 -c ' for i in range('$n_msgs'): print("cookie=1,reg1=%d,actions=drop" % i) ') > flows.txt AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) # Check that multipart flow dumps work properly: AT_CHECK([ovs-ofctl diff-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br0 in_port=1,cookie=3,actions=drop]) AT_CHECK([ovs-ofctl mod-flows br0 in_port=2,cookie=2,actions=output:2]) AT_CHECK([ovs-ofctl del-flows br0 cookie=1/-1]) ovs-appctl -t ovs-ofctl ofctl/unblock # Wait for the connection resumed. # A barrier doesn't work for this purpose. # https://www.mail-archive.com/dev@openvswitch.org/msg27013.html # https://www.mail-archive.com/dev@openvswitch.org/msg27675.html OVS_WAIT_UNTIL([grep RESUMED monitor.log]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) # Check that the flow monitor reported the same number of flows # added and deleted, but fewer than we actually added and deleted. adds=`grep -c 'ADDED.*reg1=' monitor.log` deletes=`grep -c 'DELETED.*reg1=' monitor.log` echo adds=$adds deletes=$deletes AT_CHECK([test $adds -gt 100 && test $adds -lt $n_msgs]) AT_CHECK([test $adds = $deletes]) # Check that the flow monitor reported everything in the expected order: # # event=ADDED table=0 cookie=0x1 reg1=0x22 # ... # NXT_FLOW_MONITOR_PAUSED: # ... # event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22 # ... # event=ADDED table=0 cookie=0x3 in_port=1 # event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2 # NXT_FLOW_MONITOR_RESUMED: # # except that, between the PAUSED and RESUMED, the order of the ADDED # and MODIFIED lines lines depends on hash order, that is, it varies # as we change the hash function or change architecture. Therefore, # we use a couple of tests below to accept both orders. # Rename all version specific strings to a generic one sed -i'.raw' -e 's|NXT_FLOW_MONITOR|FLOW_MONITOR|' -e 's|ONFT_FLOW_MONITOR|FLOW_MONITOR|' monitor.log sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n event=PAUSED|FLOW_MONITOR_PAUSED$3:|' monitor.log sed -i -z 's|OFPST_FLOW_MONITOR reply$3 (xid=0x0):\n event=RESUMED|FLOW_MONITOR_RESUMED$3:|' monitor.log AT_CHECK([ofctl_strip < monitor.log | sed -n -e ' /reg1=0x22$/p /cookie=0x[[23]]/p /FLOW_MONITOR_PAUSED$3:/p /FLOW_MONITOR_RESUMED$3:/p ' > monitor.log.subset]) AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl event=ADDED table=0 cookie=0x1 reg1=0x22 FLOW_MONITOR_PAUSED$3: event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22 event=ADDED table=0 cookie=0x3 in_port=1 FLOW_MONITOR_RESUMED$3: ]) AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl FLOW_MONITOR_PAUSED$3: event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22 event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2 FLOW_MONITOR_RESUMED$3: ]) OVS_VSWITCHD_STOP AT_CLEANUP # Test to show flow monitoring support on different OpenFlow protocols AT_SETUP([ofproto - flow monitoring usable protocols (OpenFlow $1)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START on_exit 'kill `cat ovs-ofctl.pid`' ovs-ofctl -O $2 monitor br0 watch:udp,udp_dst=8 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Wait till reply comes backs with OF Version OVS_WAIT_UNTIL([grep "$4 reply$3" monitor.log]) ovs-appctl -t ovs-ofctl exit # Make sure protocol type in messages from vswitchd, matches that of requested protocol ovs-ofctl -O $2 monitor br0 watch:sctp,sctp_dst=9 --detach --no-chdir --pidfile >monitor.log 2>&1 ovs-ofctl add-flow br0 sctp,sctp_dst=9,action=normal OVS_WAIT_UNTIL([grep "event=ADDED " monitor.log]) AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [dnl $4 reply$3: $4 reply$3 (xid=0x0): event=ADDED table=0 cookie=0 sctp,tp_dst=9 actions=NORMAL ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP ]) CHECK_FLOW_MONITORING([1.0], [OpenFlow10], [], NXST_FLOW_MONITOR) CHECK_FLOW_MONITORING([1.1], [OpenFlow11], [ (OF1.1)], NXST_FLOW_MONITOR) CHECK_FLOW_MONITORING([1.2], [OpenFlow12], [ (OF1.2)], NXST_FLOW_MONITOR) CHECK_FLOW_MONITORING([1.3], [OpenFlow13], [ (OF1.3)], ONFST_FLOW_MONITOR) CHECK_FLOW_MONITORING([1.4], [OpenFlow14], [ (OF1.4)], OFPST_FLOW_MONITOR) CHECK_FLOW_MONITORING([1.5], [OpenFlow15], [ (OF1.5)], OFPST_FLOW_MONITOR) AT_SETUP([ofproto - OpenFlow14 flow monitoring with out_group]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0 group_id=1,type=all,bucket=output:1]) AT_CHECK([ovs-ofctl -O OpenFlow14 add-group br0 group_id=2,type=all,bucket=output:2]) ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=121,actions=output:1 ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=122,actions=group:1 ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=123,actions=group:2 # Start a monitor watching the flow table and check the initial reply. ovs-ofctl -OOpenFlow14 monitor br0 watch:out_group=2 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/barrier AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [OFPST_FLOW_MONITOR reply (OF1.4): event=INITIAL table=0 cookie=0 in_port=0,dl_vlan=123 actions=group:2 OFPT_BARRIER_REPLY (OF1.4): ]) ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log # Add, modify flows and check the updates. ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=121,actions=group:2 ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=122,actions=group:2 ovs-ofctl -OOpenFlow14 mod-flows br0 dl_vlan=123,actions=group:1 ovs-appctl -t ovs-ofctl ofctl/barrier ovs-ofctl -OOpenFlow14 add-flow br0 in_port=0,dl_vlan=124,actions=group:2 AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=121 actions=group:2 OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=MODIFIED table=0 cookie=0 in_port=0,dl_vlan=122 actions=group:2 OFPT_BARRIER_REPLY (OF1.4): OFPST_FLOW_MONITOR reply (OF1.4) (xid=0x0): event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=group:2 ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - event filtering (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Send an OpenFlow13 message (04), OFPT_GET_ASYNC_REQUEST (1a), length (8), xid (0a) ovs-appctl -t ovs-ofctl ofctl/send 041a00080000000a ovs-appctl -t ovs-ofctl ofctl/barrier # Check default setting read -r -d '' expected <<'EOF' EOF AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: OFPT_GET_ASYNC_REQUEST (OF1.3): OFPT_GET_ASYNC_REPLY (OF1.3): primary: PACKET_IN: no_match action PORT_STATUS: add delete modify FLOW_REMOVED: idle hard delete group_delete ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) secondary: PACKET_IN: (off) PORT_STATUS: add delete modify FLOW_REMOVED: (off) ROLE_STATUS: (off) TABLE_STATUS: (off) REQUESTFORWARD: (off) OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - ofport_request]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 set_and_check_specific_ofports () { ovs-vsctl set Interface p1 ofport_request="$1" -- \ set Interface p2 ofport_request="$2" -- \ set Interface p3 ofport_request="$3" ofports=`ovs-vsctl get Interface p1 ofport -- \ get Interface p2 ofport -- \ get Interface p3 ofport` AT_CHECK_UNQUOTED([echo $ofports], [0], [$1 $2 $3 ]) } for pre in '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do for post in '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do echo ----------------------------------------------------------- echo "Check changing port numbers from $pre to $post" set_and_check_specific_ofports $pre set_and_check_specific_ofports $post done done ovs-vsctl del-port p3 set_and_check_poorly_specified_ofports () { ovs-vsctl set Interface p1 ofport_request="$1" -- \ set Interface p2 ofport_request="$2" p1=`ovs-vsctl get Interface p1 ofport` p2=`ovs-vsctl get Interface p2 ofport` echo $p1 $p2 AT_CHECK([test "$p1" != "$p2"]) if test "$1" = "$2" && test "$1" != '[[]]'; then # One port number must be the requested one. AT_CHECK([test "$p1" = "$1" || test "$p2" = "$1"]) # The other port number must be different (already tested above). else AT_CHECK([test "$1" = '[[]]' || test "$p1" = "$1"]) AT_CHECK([test "$2" = '[[]]' || test "$p2" = "$2"]) fi } for pre in '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \ '1 1' '2 2'; do for post in '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \ '1 1' '2 2'; do echo ----------------------------------------------------------- echo "Check changing port numbers from $pre to $post" set_and_check_poorly_specified_ofports $pre set_and_check_poorly_specified_ofports $post done done OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle open (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Send an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (0a) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle double open (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Send twice an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (0a) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle close without open (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle double close (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Close, Close ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_ERROR (OF1.4): OFPBFC_BUNDLE_CLOSED OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle close, different flags (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Close ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 02 00 01" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic OFPT_ERROR (OF1.4): OFPBFC_BAD_FLAGS OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle commit without open (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Commit ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 04 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle commit, different flags (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Commit ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 04 00 01" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.4): send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic OFPT_ERROR (OF1.4): OFPBFC_BAD_FLAGS OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle discard without open (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Discard ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 0a 00 00 00 01 00 06 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=DISCARD_REQUEST flags=ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=DISCARD_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle with multiple flow mods (OpenFlow 1.4)]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set vconn:dbg]) AT_CHECK([ovs-ofctl --no-names del-flows br0]) AT_DATA([flows.txt], [dnl add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1 add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3 add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4 delete add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5 add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7 delete in_port=2 dl_src=00:88:99:aa:bb:cc ]) AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6 NXST_FLOW reply: ]) AT_DATA([flows.txt], [dnl modify actions=drop modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7 ]) AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 NXST_FLOW reply: ]) # Adding an existing flow acts as a modify, and delete_strict also works. AT_DATA([flows.txt], [dnl add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=8 delete_strict in_port=2 dl_src=00:66:77:88:99:aa add in_port=2 dl_src=00:66:77:88:99:aa actions=drop ]) AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8 in_port=2,dl_src=00:66:77:88:99:aa actions=drop NXST_FLOW reply: ]) dnl Check logs for OpenFlow trace # Prevent race. OVS_WAIT_UNTIL([vconn_sub < ovs-vswitchd.log | test `grep -- "|vconn|DBG|unix: sent (Success): NXST_FLOW reply" | wc -l` -ge 3]) AT_CHECK([print_vconn_debug | vconn_sub | ofctl_strip], [0], [dnl vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: OFPT_FLOW_MOD: DEL actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:1 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:2 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:3 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:4 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL table:255 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:5 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:6 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:7 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL table:255 in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.4): vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): MOD actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): MOD_STRICT in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.4): vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:8 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:255 in_port=2,dl_src=00:66:77:88:99:aa actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.4): vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8 in_port=2,dl_src=00:66:77:88:99:aa actions=drop ]) AT_CHECK([grep " flow_mods in the last " ovs-vswitchd.log | sed -e 's/^.*connmgr|INFO|//' | vconn_sub], [0], [dnl br0<->unix: 1 flow_mods in the last 0 s (1 deletes) br0<->unix: 9 flow_mods in the last 0 s (7 adds, 2 deletes) br0<->unix: 2 flow_mods in the last 0 s (2 modifications) br0<->unix: 3 flow_mods in the last 0 s (2 adds, 1 deletes) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - failing bundle commit (OpenFlow 1.4)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl del-flows br0]) ovs-ofctl add-flows br0 - <&1 | sed '/talking to/,$d' | strip_xids], [0], [dnl Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.4): ADD table:254 actions=drop ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22 idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle timeout (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl time/stop # Send an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (01) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 01 00 00 00 01 00 00 00 03" ovs-appctl time/warp 8000 # Send a bundle flow mod, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 22 00 a0 00 00 00 02 00 00 00 01 00 00 00 03 \ 05 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " ovs-appctl time/warp 8000 # Send a bundle close, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 03 00 00 00 01 00 02 00 03" ovs-appctl time/warp 11000 # Make sure that timeouts are processed after the expiry ovs-appctl time/warp 1000 # Send a Commit, but too late. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 04 00 00 00 01 00 04 00 03" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 send: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0x1 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REPLY flags=0 OFPT_ERROR (OF1.4): OFPBFC_TIMEOUT OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle custom timeout (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:bundle-idle-timeout=4]) # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl time/stop # Send an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (01) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 01 00 00 00 01 00 00 00 03" ovs-appctl time/warp 2000 # Send a bundle flow mod, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 22 00 a0 00 00 00 02 00 00 00 01 00 00 00 03 \ 05 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " ovs-appctl time/warp 2000 # Send a bundle close, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 03 00 00 00 01 00 02 00 03" ovs-appctl time/warp 4000 # Make sure that timeouts are processed after the expiry, but still before the # current timeout of 4s. ovs-appctl time/warp 1000 # Send a Commit, but too late. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 04 00 00 00 01 00 04 00 03" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 send: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0x1 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REPLY flags=0 OFPT_ERROR (OF1.4): OFPBFC_TIMEOUT OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle reset timeout to default (OpenFlow 1.4)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:bundle-idle-timeout=15]) AT_CHECK([ovs-vsctl remove Open_vSwitch . other_config bundle-idle-timeout]) # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow14 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl time/stop # Send an OpenFlow14 message (05), OFPT_BUNDLE_CONTROL (21), length (10), xid (01) ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 01 00 00 00 01 00 00 00 03" ovs-appctl time/warp 8000 # Send a bundle flow mod, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 22 00 a0 00 00 00 02 00 00 00 01 00 00 00 03 \ 05 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \ 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \ 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \ 80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \ 00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \ 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \ 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \ " ovs-appctl time/warp 8000 # Send a bundle close, it should keep the bundle alive. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 03 00 00 00 01 00 02 00 03" ovs-appctl time/warp 11000 # Make sure that timeouts are processed after the expiry ovs-appctl time/warp 1000 # Send a Commit, but too late. ovs-appctl -t ovs-ofctl ofctl/send "05 21 00 10 00 00 00 04 00 00 00 01 00 04 00 03" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REPLY flags=0 send: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0x1 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3 send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic ordered OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=CLOSE_REPLY flags=0 OFPT_ERROR (OF1.4): OFPBFC_TIMEOUT OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=OPEN_REQUEST flags=atomic ordered send: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_ERROR (OF1.4): OFPBFC_BAD_ID OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic ordered OFPT_BARRIER_REPLY (OF1.4): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle open (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Send an OpenFlow13 message (04), OFPT_EXPERIMENTER (04), length (0018), # xid (0000000a), ONF_EXPERIMENTER_ID (4F4E4600), # ONFT_BUNDLE_CONTROL (2300 = 0x08FC), bundle id (00000001), # message type (0000), and flags (0002) ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle double open (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Send twice an OpenFlow13 message (04), OFPT_EXPERIMENTER (04), length (0018), # xid (0000000a), ONF_EXPERIMENTER_ID (4F4E4600), # ONFT_BUNDLE_CONTROL (2300 = 0x08FC), bundle id (00000001), # message type (0000), and flags (0002) ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_ERROR (OF1.3): OFPBFC_BAD_ID ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle close without open (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_ERROR (OF1.3): OFPBFC_BAD_ID ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle double close (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Close, Close ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 02 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_ERROR (OF1.3): OFPBFC_BUNDLE_CLOSED ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle close, different flags (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Close ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 02 00 01" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic OFPT_ERROR (OF1.3): OFPBFC_BAD_FLAGS ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=CLOSE_REQUEST flags=atomic OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle commit without open (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Commit ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 04 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=COMMIT_REQUEST flags=ordered OFPT_ERROR (OF1.3): OFPBFC_BAD_ID ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=COMMIT_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle commit, different flags (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Open, Commit ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 00 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 04 00 01" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REQUEST flags=ordered ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=OPEN_REPLY flags=0 OFPT_BARRIER_REPLY (OF1.3): send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic OFPT_ERROR (OF1.3): OFPBFC_BAD_FLAGS ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=COMMIT_REQUEST flags=atomic OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle discard without open (OpenFlow 1.3)]) AT_KEYWORDS([monitor]) OVS_VSWITCHD_START # Start a monitor, use the required protocol version ovs-ofctl -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile >monitor.log 2>&1 AT_CAPTURE_FILE([monitor.log]) # Discard ovs-appctl -t ovs-ofctl ofctl/send "04 04 00 18 00 00 00 0a 4F 4E 46 00 00 00 08 FC 00 00 00 01 00 06 00 02" ovs-appctl -t ovs-ofctl ofctl/barrier OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([ofctl_strip < monitor.log], [0], [dnl send: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=DISCARD_REQUEST flags=ordered OFPT_ERROR (OF1.3): OFPBFC_BAD_ID ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0x1 type=DISCARD_REQUEST flags=ordered OFPT_BARRIER_REPLY (OF1.3): ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - bundle with multiple flow mods (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set vconn:dbg]) AT_CHECK([ovs-ofctl --no-names del-flows br0]) AT_DATA([flows.txt], [dnl add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1 add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3 add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4 delete add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5 add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7 delete in_port=2 dl_src=00:88:99:aa:bb:cc ]) AT_CHECK([ovs-ofctl -O OpenFlow13 --no-names --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6 NXST_FLOW reply: ]) AT_DATA([flows.txt], [dnl modify actions=drop modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 --no-names --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 NXST_FLOW reply: ]) # Adding an existing flow acts as a modify, and delete_strict also works. AT_DATA([flows.txt], [dnl add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=8 delete_strict in_port=2 dl_src=00:66:77:88:99:aa add in_port=2 dl_src=00:66:77:88:99:aa actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle --no-names add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8 in_port=2,dl_src=00:66:77:88:99:aa actions=drop NXST_FLOW reply: ]) dnl Check logs for OpenFlow trace # Prevent race. OVS_WAIT_UNTIL([vconn_sub < ovs-vswitchd.log | test `grep -- "|vconn|DBG|unix: sent (Success): NXST_FLOW reply" | wc -l` -ge 3]) AT_CHECK([print_vconn_debug | vconn_sub | ofctl_strip], [0], [dnl vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: OFPT_FLOW_MOD: DEL actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.3): version bitmap: 0x04 vconn|DBG|unix: negotiated OpenFlow version 0x04 (we support version 0x06 and earlier, peer supports version 0x04) vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:1 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:2 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:3 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:4 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): DEL table:255 actions=drop vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:5 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:6 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:7 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): DEL table:255 in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.3): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.3): vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.3): version bitmap: 0x04 vconn|DBG|unix: negotiated OpenFlow version 0x04 (we support version 0x06 and earlier, peer supports version 0x04) vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): MOD actions=drop vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): MOD_STRICT in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.3): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.3): vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.3): version bitmap: 0x04 vconn|DBG|unix: negotiated OpenFlow version 0x04 (we support version 0x06 and earlier, peer supports version 0x04) vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:8 vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): DEL_STRICT table:255 in_port=2,dl_src=00:66:77:88:99:aa actions=drop vconn|DBG|unix: received: ONFT_BUNDLE_ADD_MESSAGE (OF1.3): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.3): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.3): vconn|DBG|unix: received: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO: version bitmap: 0x01 vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01) vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm vconn|DBG|unix: received: OFPT_BARRIER_REQUEST: vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY: vconn|DBG|unix: received: NXST_FLOW request: vconn|DBG|unix: sent (Success): NXST_FLOW reply: idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8 in_port=2,dl_src=00:66:77:88:99:aa actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - failing bundle add message (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl del-flows br0]) ovs-ofctl add-flows br0 - <&1 | sed '/talking to/,$d' | strip_xids], [0], [dnl Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.3): ADD table:254 actions=drop ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11 idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22 idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33 NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - failing bundle commit (OpenFlow 1.3)]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl del-flows br0]) # Invalid group numbers are detected at commit time AT_DATA([flows.txt], [dnl add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=group:1 add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=group:2 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=group:3 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d' | strip_xids], [0], [dnl Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=group:1 Error OFPBFC_MSG_FAILED for: ONFT_BUNDLE_CONTROL (OF1.3): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - monitor flows with tun_md]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow br0 tun_metadata0=0x1,actions=drop]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: tun_metadata0=0x1 actions=drop ]) AT_CAPTURE_FILE([ofctl_monitor.log]) dnl Usually ovs-ofctl monitor outputs on stderr, but the first message here dnl is put on stdout, because it is handled by ofctl in dump_transaction() dnl and not in monitor_vconn(). AT_CHECK([ovs-ofctl monitor br0 65534 watch: --detach --no-chdir --pidfile >ofctl_monitor.log 2>&1]) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl NXST_FLOW_MONITOR reply: event=ADDED table=0 cookie=0 tun_metadata0=0x1 ]) AT_CHECK([ovs-ofctl del-flows br0]) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl NXST_FLOW_MONITOR reply: event=ADDED table=0 cookie=0 tun_metadata0=0x1 NXST_FLOW_MONITOR reply: event=DELETED reason=delete table=0 cookie=0 tun_metadata0=0x1 ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) dnl Check that vswitchd hasn't crashed AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - flow mod with tunnel metadata]) AT_KEYWORDS([ofp-actions]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 actions=move:tun_metadata0[[0..31]]->NXM_NX_REG0[[]]"]) dnl Check the length of tun_metadata0 in the replied OXM header. dnl Ignore the first 0x50 bytes of hex dump from the reply msg since the NXM dnl header that describes the tunnel metadata starts at offset 0x50. AT_CHECK([ovs-ofctl dump-flows br0 -mmmm], [0], [stdout]) AT_CHECK([sed -e 's/duration=[[0-9.]]*s/duration=?s/' -e 's/idle_age=[[0-9]]*/idle_age=?/' -e '/^000000[[0-4]]0 / d' stdout | strip_xids], [0], [dnl NXST_FLOW reply: cookie=0x0, duration=?s, table=0, n_packets=0, n_bytes=0, idle_age=?, in_port=1 actions=move:NXM_NX_TUN_METADATA0[[0..31]]->NXM_NX_REG0[[]] 00000050 ff ff 00 18 00 00 23 20-00 06 00 20 00 00 00 00 |......# ... ....| 00000060 00 01 50 04 00 01 00 04- |..P..... | ]) dnl Check actions that may use tun_metadata AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=move:tun_metadata1[[0..31]]->NXM_NX_REG0[[]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=move:tun_metadata0[[32..63]]->NXM_NX_REG0[[]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=push:tun_metadata1[[0..31]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=pop:tun_metadata0[[32..63]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=3, actions=load:0x11223344->tun_metadata1"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=output:tun_metadata1[[0..31]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=output:tun_metadata0[[32..63]]"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=multipath(eth_src,50,modulo_n,1,0,tun_metadata1[[0..31]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=multipath(eth_src,50,modulo_n,1,0,tun_metadata0[[32..63]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=bundle_load(eth_src,50,hrw,ofport,tun_metadata1[[0..31]], members:4,8)"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=bundle_load(eth_src,50,hrw,ofport,tun_metadata0[[32..63]], members:4,8)"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=learn(tun_metadata1[[0..31]]=reg0[[0..31]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=learn(tun_metadata0[[32..63]]=reg0[[0..31]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=clone(move:tun_metadata1[[0..31]]->reg0[[0..31]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=clone(move:tun_metadata0[[32..63]]->reg0[[0..31]])"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=tun_metadata1[[0..15]],exec(set_field:0x01->ct_mark))"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=tun_metadata0[[32..47]],exec(set_field:0x01->ct_mark))"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=1,exec(move:tun_metadata1[[0..31]]->ct_mark))"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=1,exec(move:tun_metadata0[[32..63]]->ct_mark))"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: OFPBAC_BAD_SET_LEN ]) dnl Check match field with tun_metadata AT_CHECK([ovs-ofctl add-flow br0 "tun_metadata0=0x11223344 actions=output:2"], [0], [], [stderr]) AT_CHECK([ovs-ofctl add-flow br0 "tun_metadata1=0x11223344 actions=output:2"], [1], [], [stderr]) AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl OFPT_ERROR: NXFMFC_INVALID_TLV_FIELD ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl) NXST_FLOW reply: in_port=1 actions=move:NXM_NX_TUN_METADATA0[[0..31]]->NXM_NX_REG0[[]] tun_metadata0=0x11223344 actions=output:2 ]) OVS_VSWITCHD_STOP(["/NXFMFC_INVALID_TLV_FIELD/d /tun_metadata0/d /OFPBAC_BAD_SET_LEN/d"]) AT_CLEANUP AT_SETUP([ofproto - flush flows, groups, and meters for controller change]) AT_KEYWORDS([flow flows group group meter]) OVS_VSWITCHD_START add_flow_group_and_meter () { AT_CHECK([ovs-ofctl add-flow br0 in_port=1,actions=2]) AT_CHECK([ovs-ofctl -O OpenFlow11 add-group br0 group_id=1234,type=all,bucket=output:10 AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps burst stats bands=type=drop rate=1 burst_size=1']) ]) } verify_added () { AT_CHECK([ovs-ofctl --no-stats dump-flows br0], [0], [dnl in_port=1 actions=output:2 ]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-groups br0], [0], [dnl OFPST_GROUP_DESC reply (OF1.1) (xid=0x2): group_id=1234,type=all,bucket=actions=output:10 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0], [0], [dnl OFPST_METER_CONFIG reply (OF1.3) (xid=0x2): meter=1 pktps burst stats bands= type=drop rate=1 burst_size=1 ]) } verify_deleted () { AT_CHECK([ovs-ofctl --no-stats dump-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow11 dump-groups br0], [0], [dnl OFPST_GROUP_DESC reply (OF1.1) (xid=0x2): ]) AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0], [0], [dnl OFPST_METER_CONFIG reply (OF1.3) (xid=0x2): ]) } # Add flow, group, meter and check that they're there, without a controller. add_flow_group_and_meter verify_added # Set up a controller and verify that the flow and group were deleted, # then add them back. AT_CHECK([ovs-vsctl set-controller br0 'tcp::6653']) verify_deleted add_flow_group_and_meter verify_added # Change the controller and verify that the flow and group are still there. AT_CHECK([ovs-vsctl set-controller br0 'tcp::6653']) verify_added # Clear the controller and verify that the flow and group were deleted. AT_CHECK([ovs-vsctl del-controller br0]) verify_deleted OVS_VSWITCHD_STOP(["/nw_dst,output=2 table=0 in_port=1 priority=83,ip,nw_dst=192.168.1.15,actions=set_field:192.168.21.26->nw_src,output=2 table=0 in_port=1 priority=82,ip,nw_dst=192.168.1.14,actions=set_field:0x40->nw_tos,output=2 table=0 in_port=1 priority=0,actions=drop ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl send a proto 0 packet to try and poison the DP flow path AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ '5054000000075054000000050800450000548de140004000289fc0a801c4c0a8011408003bf60002001bbf080a640000000032ad010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=0,frag=no), packets:0, bytes:0, used:never, actions:2 ]) dnl Send ICMP for mod nw_src and mod nw_dst AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.21,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.20,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will dec TTL AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.10,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will mod TTL AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.19,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will mod ECN AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.18,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will mod TOS AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.17,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will set DST AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.16,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will set SRC AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.15,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) dnl send ICMP that will set TOS AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.14,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.10,proto=1,ttl=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(ttl=63)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.14,proto=1,tos=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x40/0xfc)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.16,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(dst=192.168.20.26)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.17,proto=1,tos=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x40/0xfc)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.18,proto=1,tos=0/0x3,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x2/0x3)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.19,proto=1,ttl=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(ttl=8)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=0,frag=no), packets:0, bytes:0, used:never, actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(dst=192.168.20.20)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.15,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(src=192.168.21.26)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.21,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(src=192.168.20.21)),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - implicit mask of ipv6 proto with HOPOPT field]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=77,ip6,ipv6_dst=111:db8::3,actions=dec_ttl,output=2 table=0 in_port=1 priority=76,ip6,ipv6_dst=111:db8::4,actions=mod_nw_ttl:8,output=2 table=0 in_port=1 priority=75,ip6,ipv6_dst=111:db8::5,actions=mod_nw_ecn:2,output=2 table=0 in_port=1 priority=74,ip6,ipv6_dst=111:db8::6,actions=mod_nw_tos:0x40,output=2 table=0 in_port=1 priority=73,ip6,ipv6_dst=111:db8::7,actions=set_field:2112:db8::2->ipv6_dst,output=2 table=0 in_port=1 priority=72,ip6,ipv6_dst=111:db8::8,actions=set_field:2112:db8::3->ipv6_src,output=2 table=0 in_port=1 priority=72,ip6,ipv6_dst=111:db8::9,actions=set_field:44->ipv6_label,output=2 table=0 in_port=1 priority=0,actions=drop ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl send a proto 0 packet to try and poison the DP flow path AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::3,proto=0,tclass=0,hlimit=64,frag=no)']) AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=0,hlimit=0,frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ]) dnl Send ICMP for mod nw_src and mod nw_dst AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::3,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::4,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) dnl send ICMP that will dec TTL AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::5,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) dnl send ICMP that will mod TTL AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::6,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) dnl send ICMP that will mod ECN AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::7,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) dnl send ICMP that will mod TOS AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::8,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) dnl send ICMP that will set LABEL AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::9,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=0,hlimit=0,frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=1,hlimit=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(hlimit=63)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::4,proto=1,hlimit=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(hlimit=8)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::5,proto=1,tclass=0/0x3,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(tclass=0x2/0x3)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::6,proto=1,tclass=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(tclass=0x40/0xfc)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::7,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(dst=2112:db8::2)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::9,label=0,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(label=0x2c)),2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::8,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(src=2112:db8::3)),2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - implicit mask of ARP OPer field]) OVS_VSWITCHD_START add_of_ports br0 1 2 AT_DATA([flows.txt], [dnl table=0 in_port=1 priority=77,arp,arp_sha=00:01:02:03:04:06,actions=set_field:0x1->arp_op,2 table=0 in_port=1 priority=76,arp,arp_sha=00:01:02:03:04:07,actions=set_field:00:02:03:04:05:06->arp_sha,2 table=0 in_port=1 priority=75,arp,arp_sha=00:01:02:03:04:08,actions=set_field:ff:00:00:00:00:ff->arp_tha,2 table=0 in_port=1 priority=74,arp,arp_sha=00:01:02:03:04:09,actions=set_field:172.31.110.26->arp_spa,2 table=0 in_port=1 priority=73,arp,arp_sha=00:01:02:03:04:0a,actions=set_field:172.31.110.10->arp_tpa,2 table=0 in_port=1 priority=1,actions=drop ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Send op == 0 packet AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ 'ffffffffffffaa55aa550000080600010800060400000001020304070c0a00010000000000000c0a0002']) AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=0,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:2 ]) dnl Send op 2 -> set op AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=2,sha=00:01:02:03:04:06,tha=ff:ff:ff:ff:ff:ff)']) dnl Send op 1 -> set SHA AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:07,tha=ff:ff:ff:ff:ff:ff)']) dnl Send op 1 -> set THA AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:08,tha=ff:ff:ff:ff:ff:ff)']) dnl Send op 1 -> set SIP AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:09,tha=ff:ff:ff:ff:ff:ff)']) dnl Send op 1 -> set TIP AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:0a,tha=ff:ff:ff:ff:ff:ff)']) AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=0,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=1,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=1,sha=00:01:02:03:04:08,tha=ff:ff:ff:ff:ff:ff), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=2,sha=00:01:02:03:04:06), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(sip=172.31.110.1,op=1,sha=00:01:02:03:04:09), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(tip=172.31.110.25,op=1,sha=00:01:02:03:04:0a), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ofproto - configure inactivity probe interval]) # Set 6 second inactivity probe interval (default is 5 seconds). OVS_VSWITCHD_START([set-controller br0 unix:testcontroller \ -- set Controller br0 inactivity_probe=6000], [], [], [-vfile:rconn:dbg]) # Start test openflow controller. AT_CHECK([ovs-testcontroller -vsyslog:off --detach --no-chdir --pidfile punix:testcontroller], [0], [ignore]) on_exit 'kill `cat ovs-testcontroller.pid`' OVS_WAIT_UNTIL([test -e testcontroller]) # After 6 seconds of inactivity there should be a log message. OVS_WAIT_UNTIL([grep "idle 6 seconds, sending inactivity probe" ovs-vswitchd.log]) # Restart ovs-vswitchd with an empty ovs-vswitchd log file. OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) mv ovs-vswitchd.log ovs-vswitchd_1.log AT_CHECK([ovs-vswitchd --enable-dummy --disable-system --disable-system-route --detach \ --no-chdir --pidfile --log-file -vfile:rconn:dbg -vvconn -vofproto_dpif -vunixctl], [0], [], [stderr]) # After 6 seconds of inactivity there should be a log message. OVS_WAIT_UNTIL([grep "idle 6 seconds, sending inactivity probe" ovs-vswitchd.log]) OVS_VSWITCHD_STOP(["/br0<->unix:testcontroller: connection failed/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/openssl.supp000066400000000000000000000002241514270232600225140ustar00rootroot00000000000000# suppress OpenSSL errors from valgrind { BN_mod_inverse Memcheck:Cond fun:BN_mod_inverse } { BN_div Memcheck:Cond fun:BN_div } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/000077500000000000000000000000001514270232600217225ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/automake.mk000066400000000000000000000042421514270232600240630ustar00rootroot00000000000000OSS_FUZZ_TARGETS = \ tests/oss-fuzz/flow_extract_target \ tests/oss-fuzz/json_parser_target \ tests/oss-fuzz/ofp_print_target \ tests/oss-fuzz/odp_target \ tests/oss-fuzz/miniflow_target \ tests/oss-fuzz/ofctl_parse_target EXTRA_PROGRAMS += $(OSS_FUZZ_TARGETS) oss-fuzz-targets: $(OSS_FUZZ_TARGETS) tests_oss_fuzz_flow_extract_target_SOURCES = \ tests/oss-fuzz/flow_extract_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_flow_extract_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_flow_extract_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ tests_oss_fuzz_json_parser_target_SOURCES = \ tests/oss-fuzz/json_parser_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_json_parser_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_json_parser_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ tests_oss_fuzz_ofp_print_target_SOURCES = \ tests/oss-fuzz/ofp_print_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_ofp_print_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_ofp_print_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ tests_oss_fuzz_odp_target_SOURCES = \ tests/oss-fuzz/odp_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_odp_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_odp_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ tests_oss_fuzz_miniflow_target_SOURCES = \ tests/oss-fuzz/miniflow_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_miniflow_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_miniflow_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ tests_oss_fuzz_ofctl_parse_target_SOURCES = \ tests/oss-fuzz/ofctl_parse_target.c \ tests/oss-fuzz/fuzzer.h tests_oss_fuzz_ofctl_parse_target_LDADD = lib/libopenvswitch.la tests_oss_fuzz_ofctl_parse_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ EXTRA_DIST += \ tests/oss-fuzz/config/flow_extract_target.options \ tests/oss-fuzz/config/json_parser_target.options \ tests/oss-fuzz/config/ofp_print_target.options \ tests/oss-fuzz/config/odp_target.options \ tests/oss-fuzz/config/miniflow_target.options \ tests/oss-fuzz/config/ofctl_parse_target.options \ tests/oss-fuzz/config/ovs.dict \ tests/oss-fuzz/config/odp.dict \ tests/oss-fuzz/config/ofp-flow.dict openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/000077500000000000000000000000001514270232600231675ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/flow_extract_target.options000066400000000000000000000000561514270232600306540ustar00rootroot00000000000000[libfuzzer] dict = ovs.dict close_fd_mask = 3 openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/json_parser_target.options000066400000000000000000000000351514270232600304750ustar00rootroot00000000000000[libfuzzer] dict = json.dict openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/miniflow_target.options000066400000000000000000000000551514270232600277760ustar00rootroot00000000000000[libfuzzer] dict = ovs.dict close_fd_mask = 3openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/odp.dict000066400000000000000000000031041514270232600246140ustar00rootroot00000000000000"(" ")" "," "/0x" "0x" ": " "=" "=r" "action" "arp" "bfd" "bos" "c1" "c2" "c3" "c4" "cc" "cfi" "cfm" "class" "clone" "code" "commit," "crit," "csum" "ct" "ct(error)" "ct_clear" "ct_label" "ct_mark" "ct_state" "ct_tuple4" "ct_tuple6" "ct_zone" "dir" "dl_type" "dnat" "dp_hash" "drop" "dst" "dst_port" "egress" "encap" "error" "erspan" "erspan(ver=1,sid=0x,dir=,idx=0x)" "est" "eth" "eth_type" "eth_type(0/)" "first" "flags" "force_commit," "frag" "gbp(" "geneve" "gre((flags=0x,proto=0x)" "hash," "helper" "hlimit" "hwid" "icmp" "icmpv6" "id" "idx=" "in_port" "ingress" "inv" "ip6erspan" "ipfix(output_port=" "ipv4" "ipv6" "ipv6_dst" "ipv6_src" "l4()" "label" "lacp" "later" "len" "lldp" "mark=/," "match" "md2" "mdtype" "meter()" "mpls" "nat" "nd" "new" "no" "np" "ns" "nsh" "oam," "op" "options(" "out_port(" "packet_type" "pcp=" "persistent," "pop_eth" "pop_mpls(eth_type=0x)" "pop_nsh()" "pop_vlan" "proto" "push_eth(src=:::::,dst=:::::,type=)" "push_mpls(" "push_nsh(" "push_vlan(" "push_vlan(tpid=,vid=,pcp=,cfi=)" "random," "recirc()" "recirc_id" "rel" "rpl" "sFlow(vid=,pcp=,output=)" "sample" "sctp" "seq=0x" "set(" "set(nsh(" "sha" "si" "sip" "skb_mark" "skb_priority" "sll" "slow_path" "snat" "spi" "src" "src_port" "stp" "sym_l4()" "target" "tc=" "tclass" "tcp" "tcp_flags" "tha" "tip" "tll" "tnl_pop(" "tnl_push(tnl_port(" "too_little" "too_much" "tos" "tp_dst" "tp_src" "tpid=0x" "trk" "trunc()" "ttl" "tun_id" "tunnel" "tunnel_out_port" "type" "udp" "unspec" "userdata" "userspace(" "userspace(error)" "userspace(pid" "ver" "vid" "vlan" "vxlan" "vxlan(flags=0x,vni=0x)" "vxlan(gbp(" "zone" openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/odp_target.options000066400000000000000000000000561514270232600267350ustar00rootroot00000000000000[libfuzzer] close_fd_mask = 3 dict = odp.dict openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/ofctl_parse_target.options000066400000000000000000000000631514270232600304520ustar00rootroot00000000000000[libfuzzer] close_fd_mask = 3 dict = ofp-flow.dict openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/ofp-flow.dict000066400000000000000000000007041514270232600255660ustar00rootroot00000000000000")" "(" "[" "]" "," "-" "0" ":" "=" "ADD" "DEL" "DEL_STRICT" "MOD" "MOD_STRICT" "\x00" "\x20" "\x3F" "actions" "add" "allow_hidden_fields" "cc" "check_overlap" "cookie" "delete" "delete_strict" "duration" "eth" "hard_age" "hard_timeout" "idle_age" "idle_timeout" "importance" "modify" "modify_strict" "n_bytes" "n_packets" "no_byte_counts" "no_packet_counts" "no_readonly_table" "out_group" "out_port" "priority" "reset_counts" "send_flow_rem" "table" openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/ofp_print_target.options000066400000000000000000000000561514270232600301530ustar00rootroot00000000000000[libfuzzer] close_fd_mask = 3 dict = ovs.dict openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/config/ovs.dict000066400000000000000000000066501514270232600246520ustar00rootroot00000000000000"0.2" "ADD_SUBSCRIBE" "-cbc" "CLEARSUB" "CLIENT" "GIMME" "GIMMEDEFS" "GIMMESTATS" "HM" "-hmac96" "HM_CTL" "HM_STAT" "HMST_CLIENT" "LOGIN" "\\MAILSLOT\\BROWSE" "NET-ANNOUNCED" "NET-VISIBLE" "-nodefs" "NONE" "OPSTAFF" "\\PIPE\\LANMAN" "public" "REALM" "REALM-ANNOUNCED" "REALM-VISIBLE" "REQ_SUBSCRIBE" "RLM_SUBSCRIBE" "RLM_UNSUBSCRIBE" "SENT" " %ssub%s" "SUBSCRIBE" "SUBSCRIBE_NODEFS" "un" "UNSUBSCRIBE" "USER_FLUSH" "USER_HIDE" "USER_LOCATE" "USER_UNHIDE" "WG_CTL" "\x01\x00" "\x01\x00\x00" "\x01\x00\x01" "\x01\x00\x02" "\x01\x00\x03" "\x01\x00\x05" "\x01\x01" "\x01\x02" "\x01\x03" "\x01\x04" "\x01\x05" "\x01\x07" "\x01\x0B" "\x01\x0C" "\x01\x10" "\x01\x11" "\x01\x12" "\x01\x13" "\x01\x14" "\x01\x15" "\x01\x16" "\x01\xE8\x48" "\x01\xF4" "\x01\xF5" "\x01\xF6" "\x01\xF7" "\x01\xF8" "\x01\xF9" "\x01\xFA" "\x01\xFB" "\x01\xFC" "\x01\xFD" "\x01\xFE" "\x01\xFF" "\x02\x00" "\x02\x00\x00" "\x02\x01" "\x02\x02" "\x02\x03" "\x02\x04" "\x02\x05" "\x02\x06" "\x02\x07" "\x02\x08" "\x02\x09" "\x02\x0C" "\x02\x0E" "\x02\x0F" "\x02\x11" "\x02\x12" "\x02\x58" "\x02\x81" "\x02\x83" "\x03\x00" "\x03\x01" "\x03\x02" "\x03\x03" "\x03\x06" "\x03\xE8" "\x03\xE9" "\x03\xEA" "\x03\xEB" "\x03\xEC" "\x03\xED" "\x03\xEE" "\x03\xEF" "\x03\xF0" "\x03\xF1" "\x03\xF2" "\x03\xF3" "\x03\xF4" "\x03\xFF\xFF\xFF" "\x04\x00" "\x04\x00\x00" "\x04\x01" "\x04\x02" "\x04\x03" "\x04\x04" "\x04\x51" "\x04\x52" "\x04\x53" "\x04\x55" "\x04\x56" "\x05\x00" "\x05\x01" "\x05\x02" "\x05\x03" "\x05\x53" "\x05\xCC" "\x05\xDC" "\x06\x00" "\x06\x01" "\x06\xCF" "\x07\x07" "\x07\xC1" "\x07\xFF" "\x08\x00" "\x08\x00\x00\x00" "\x08\x00\x07" "\x08\x01" "\x08\x06" "\x08\x38\x00\x00" "\x0A\x00\xB1" "\x0C\x01" "\x0C\x02" "\x0C\x03" "\x0C\x04" "\x0C\x05" "\x0C\x06" "\x0C\x08" "\x0D\x80" "\x0E\x00" "\x0E\x10" "\x0E\xC8" "\x0E\xC9" "\x0F\x42\x40" "\x0F\xFF" "\x10\x00" "\x11\x11" "\x11\xD7" "\x12\x0F" "\x12\xBB" "\x1A\x30" "\x1A\x31" "\x1A\x32" "\x1B\x21" "\x1B\x58" "\x1B\x59" "\x1B\x5A" "\x1B\x5B" "\x1B\x5C" "\x1B\x5D" "\x1B\x5F" "\x1B\x61" "\x1F\x00" "\x1F\x40" "\x1F\xFF" "\x1F\xFF\xFF" "\x20\x00" "\x20\x03" "\x20\x04" "\x27\x10" "\x27\x13" "\x2F\xBF" "\x35\x00\x00" "\x3C\x13" "\x40\x00" "\x40\x04" "\x40\x80" "\x47\x00\x06\x01" "\x4E\x20" "\x4E\x21" "\x4E\x22" "\x4E\x23" "\x4E\x24" "\x4E\x25" "\x4E\x26" "\x4E\x27" "\x4E\x28" "\x4E\x29" "\x4E\x2A" "\x4E\x2C" "\x60\x00" "\x60\x01" "\x60\x02" "\x60\x03" "\x60\x04" "\x60\x07" "\x7F\xFF" "\x7F\xFF\xFF" "\x80\x00" "\x80\x00\x00\x00" "\x80\x01" "\x80\x05" "\x80\x0A" "\x80\x21" "\x80\x21\x10\x01" "\x80\x21\x10\x02" "\x80\x23" "\x80\x35" "\x80\x57" "\x80\x9B" "\x80\xC2" "\x80\xF3" "\x80\xFD" "\x81\x00" "\x81\x37" "\x82\x81" "\x83\xAA\x7E\x80" "\x85\xBE" "\x86\xDD" "\x88\x08" "\x88\x09" "\x88\x0B" "\x88\x47" "\x88\x48" "\x88\x63" "\x88\x64" "\x88\x6F" "\x88\x70" "\x88\x8E" "\x88\x99" "\x88\xA2" "\x88\xA8" "\x88\xCA" "\x88\xCC" "\x89\x02" "\x89\x3A" "\x89\x47" "\x90\x00" "\x91\x00" "\xA0\x00" "\xAB\xCD" "\xB0\x00" "\xC0\x00\x00\x00" "\xC0\x21" "\xC0\x23" "\xC0\x25" "\xC0\x27" "\xC0\x2B" "\xC0\x2D" "\xC1\x23" "\xC2\x23" "\xC2\x27" "\xDA\xDA" "\xE0\x00" "\xE0\x00\x00\x00" "\xF0\x00\x00\x00" "\xF1\x0A" "\xF9\x89" "\xFC\x00" "\xFD\xE9" "\xFE\xFE" "\xFF\x00" "\xFF\x00\x00" "\xFF\x00\x00\x00" "\xFF\xF0" "\xFF\xF8" "\xFF\xFD" "\xFF\xFE" "\xFF\xFF" "\xFF\xFF\x00\x00" "\xFF\xFF\xF0\x00" "\xFF\xFF\xFF\x00" "\xFF\xFF\xFF\x01" "\xFF\xFF\xFF\x02" "\xFF\xFF\xFF\x03" "\xFF\xFF\xFF\xEF" "\xFF\xFF\xFF\xFD" "\xFF\xFF\xFF\xFE" "\xFF\xFF\xFF\xFF" "ZEPH" "ZEPHYR_ADMIN" "ZEPHYR_CTL" openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/flow_extract_target.c000066400000000000000000000057611514270232600261460ustar00rootroot00000000000000#include #include "classifier.h" #include #include "fuzzer.h" #include "dp-packet.h" #include "flow.h" #include "openvswitch/ofp-match.h" #include "openvswitch/ofp-print.h" #include "openvswitch/match.h" #include "openvswitch/util.h" #include "classifier-private.h" static void test_flow_hash(const struct flow *flow) { uint32_t hash = flow_hash_5tuple(flow, 0); hash = flow_hash_symmetric_l4(flow, 0); hash = flow_hash_symmetric_l2(flow, 0); hash = flow_hash_symmetric_l3l4(flow, 0, NULL); hash = flow_hash_symmetric_l3(flow, 0); hash = flow_hash_fields(flow, NX_HASH_FIELDS_ETH_SRC, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_SYMMETRIC_L4, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_SYMMETRIC_L3L4, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_NW_SRC, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_NW_DST, hash); hash = flow_hash_fields(flow, NX_HASH_FIELDS_SYMMETRIC_L3, hash); ovs_ignore(hash); } static void test_flow_mask(const struct flow *flow) { struct flow_wildcards catchall; flow_wildcards_init_catchall(&catchall); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_ETH_SRC); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_SYMMETRIC_L4); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_SYMMETRIC_L3L4); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_NW_SRC); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_NW_DST); flow_mask_hash_fields(flow, &catchall, NX_HASH_FIELDS_SYMMETRIC_L3); } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct dp_packet packet; struct flow flow; dp_packet_use_const(&packet, data, size); flow_extract(&packet, &flow); /* Extract flowmap. */ struct flowmap fmap; flow_wc_map(&flow, &fmap); /* Parse TCP flags. */ if (dp_packet_size(&packet) >= ETH_HEADER_LEN) { uint16_t tcp_flags = parse_tcp_flags(&packet, NULL, NULL, NULL); ovs_ignore(tcp_flags); } /* Count headers. */ int count = flow_count_vlan_headers(&flow); ovs_ignore(count); /* Extract metadata. */ struct match flow_metadata; flow_get_metadata(&flow, &flow_metadata); /* Hashing functions. */ test_flow_hash(&flow); /* Masking functions. */ test_flow_mask(&flow); /* Convert flow to match. */ struct match match; match_wc_init(&match, &flow); struct ofp10_match ext_match; ofputil_match_to_ofp10_match(&match, &ext_match); /* Print match and packet. */ ofp_print_packet(stdout, dp_packet_data(&packet), dp_packet_size(&packet), htonl(PT_ETH)); ovs_hex_dump(stdout, dp_packet_data(&packet), dp_packet_size(&packet), 0, true); match_print(&match, NULL); ovs_hex_dump(stdout, &ext_match, sizeof ext_match, 0, false); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/fuzzer.h000066400000000000000000000002441514270232600234200ustar00rootroot00000000000000#ifndef FUZZER_H #define FUZZER_H 1 #include #include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); #endif /* fuzzer.h */ openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/json_parser_target.c000066400000000000000000000016231514270232600257630ustar00rootroot00000000000000#include #include "fuzzer.h" #include "jsonrpc.h" #include "openvswitch/json.h" #include "ovsdb-error.h" #include "ovsdb/table.h" #include #include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (!size || data[size - 1]) { return 0; } struct json *j1 = json_from_string((const char *)data); if (j1->type == JSON_STRING) { json_destroy(j1); return 0; } free(json_to_string(j1, JSSF_SORT | JSSF_PRETTY)); struct jsonrpc_msg *msg; char *error = jsonrpc_msg_from_json(j1, &msg); /* Frees 'j1'. */ if (error) { free(error); return 0; } struct json *j2 = jsonrpc_msg_to_json(msg); /* Frees 'msg'. */ if (j2->type == JSON_STRING) { json_destroy(j2); return 0; } free(json_to_string(j2, JSSF_SORT | JSSF_PRETTY)); json_destroy(j2); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/miniflow_target.c000066400000000000000000000160451514270232600252660ustar00rootroot00000000000000#include #include "classifier.h" #include "fuzzer.h" #include "dp-packet.h" #include "flow.h" #include "openvswitch/ofp-match.h" #include "openvswitch/ofp-print.h" #include "openvswitch/match.h" #include "classifier-private.h" #include "util.h" /* Returns a copy of 'src'. The caller must eventually free the returned * miniflow with free(). */ static struct miniflow * miniflow_clone__(const struct miniflow *src) { struct miniflow *dst; size_t data_size; data_size = miniflow_alloc(&dst, 1, src); miniflow_clone(dst, src, data_size / sizeof(uint64_t)); return dst; } /* Returns a hash value for 'flow', given 'basis'. */ static inline uint32_t miniflow_hash__(const struct miniflow *flow, uint32_t basis) { const uint64_t *p = miniflow_get_values(flow); size_t n_values = miniflow_n_values(flow); struct flowmap hash_map = FLOWMAP_EMPTY_INITIALIZER; uint32_t hash = basis; size_t idx; FLOWMAP_FOR_EACH_INDEX (idx, flow->map) { uint64_t value = *p++; if (value) { hash = hash_add64(hash, value); flowmap_set(&hash_map, idx, 1); } } map_t map; FLOWMAP_FOR_EACH_MAP (map, hash_map) { hash = hash_add64(hash, map); } return hash_finish(hash, n_values); } #define FLOW_U32S (FLOW_U64S * 2) static void toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask) { const uint32_t *mask_u32 = (const uint32_t *) &mask->masks; uint32_t *flow_u32 = (uint32_t *) flow; int i; for (i = 0; i < FLOW_U32S; i++) { if (mask_u32[i] != 0) { uint32_t bit; do { bit = 1u << random_range(32); } while (!(bit & mask_u32[i])); flow_u32[i] ^= bit; } } } static void wildcard_extra_bits(struct flow_wildcards *mask) { uint32_t *mask_u32 = (uint32_t *) &mask->masks; int i; for (i = 0; i < FLOW_U32S; i++) { if (mask_u32[i] != 0) { uint32_t bit; do { bit = 1u << random_range(32); } while (!(bit & mask_u32[i])); mask_u32[i] &= ~bit; } } } static void test_miniflow(struct flow *flow) { struct miniflow *miniflow, *miniflow2, *miniflow3; struct flow flow2, flow3; struct flow_wildcards mask; struct minimask *minimask; int i; const uint64_t *flow_u64 = (const uint64_t *) flow; /* Convert flow to miniflow. */ miniflow = miniflow_create(flow); /* Obtain miniflow hash. */ uint32_t hash = miniflow_hash_5tuple(miniflow, 0); ovs_ignore(hash); /* Check that the flow equals its miniflow. */ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) { ovs_assert(miniflow_get_vid(miniflow, i) == vlan_tci_to_vid(flow->vlans[i].tci)); } for (i = 0; i < FLOW_U64S; i++) { ovs_assert(miniflow_get(miniflow, i) == flow_u64[i]); } /* Check that the miniflow equals itself. */ ovs_assert(miniflow_equal(miniflow, miniflow)); /* Convert miniflow back to flow and verify that it's the same. */ miniflow_expand(miniflow, &flow2); ovs_assert(flow_equal(flow, &flow2)); /* Check that copying a miniflow works properly. */ miniflow2 = miniflow_clone__(miniflow); ovs_assert(miniflow_equal(miniflow, miniflow2)); ovs_assert(miniflow_hash__(miniflow, 0) == miniflow_hash__(miniflow2, 0)); miniflow_expand(miniflow2, &flow3); ovs_assert(flow_equal(flow, &flow3)); /* Check that masked matches work as expected for identical flows and * miniflows. */ flow_wildcards_init_for_packet(&mask, flow); /* Ensure that mask is not catchall just in case * flow_wildcards_init_for_packet returns a catchall mask */ uint64_t *mask_u64 = (uint64_t *) &mask.masks; mask_u64[0] = 1; ovs_assert(!flow_wildcards_is_catchall(&mask)); minimask = minimask_create(&mask); ovs_assert(!minimask_is_catchall(minimask)); ovs_assert(miniflow_equal_in_minimask(miniflow, miniflow2, minimask)); ovs_assert(miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask)); ovs_assert(miniflow_hash_in_minimask(miniflow, minimask, 0x12345678) == flow_hash_in_minimask(flow, minimask, 0x12345678)); ovs_assert(minimask_hash(minimask, 0) == miniflow_hash__(&minimask->masks, 0)); /* Check that masked matches work as expected for differing flows and * miniflows. */ toggle_masked_flow_bits(&flow2, &mask); ovs_assert(!miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask)); miniflow3 = miniflow_create(&flow2); ovs_assert(!miniflow_equal_in_minimask(miniflow, miniflow3, minimask)); free(miniflow); free(miniflow2); free(miniflow3); free(minimask); } static void test_minimask_has_extra(struct flow *flow) { struct flow_wildcards catchall; struct minimask *minicatchall; flow_wildcards_init_catchall(&catchall); minicatchall = minimask_create(&catchall); ovs_assert(minimask_is_catchall(minicatchall)); struct flow_wildcards mask; struct minimask *minimask; mask.masks = *flow; minimask = minimask_create(&mask); ovs_assert(!minimask_has_extra(minimask, minimask)); ovs_assert(minimask_has_extra(minicatchall, minimask) == !minimask_is_catchall(minimask)); if (!minimask_is_catchall(minimask)) { struct minimask *minimask2; wildcard_extra_bits(&mask); minimask2 = minimask_create(&mask); ovs_assert(minimask_has_extra(minimask2, minimask)); ovs_assert(!minimask_has_extra(minimask, minimask2)); free(minimask2); } free(minimask); free(minicatchall); } static void test_minimask_combine(struct flow *flow) { struct flow_wildcards catchall; struct minimask *minicatchall; flow_wildcards_init_catchall(&catchall); minicatchall = minimask_create(&catchall); ovs_assert(minimask_is_catchall(minicatchall)); struct minimask *minimask, *minimask2; struct flow_wildcards mask, mask2, combined, combined2; struct { struct minimask minicombined; uint64_t storage[FLOW_U64S]; } m; struct flow flow2; memset(&flow2, 0, sizeof flow2); mask.masks = *flow; minimask = minimask_create(&mask); minimask_combine(&m.minicombined, minimask, minicatchall, m.storage); ovs_assert(minimask_is_catchall(&m.minicombined)); /* Create mask based on zero flow */ mask2.masks = flow2; minimask2 = minimask_create(&mask2); minimask_combine(&m.minicombined, minimask, minimask2, m.storage); flow_wildcards_and(&combined, &mask, &mask2); minimask_expand(&m.minicombined, &combined2); ovs_assert(flow_wildcards_equal(&combined, &combined2)); free(minimask); free(minimask2); free(minicatchall); } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct dp_packet packet; struct flow flow; dp_packet_use_const(&packet, data, size); flow_extract(&packet, &flow); /* Do miniflow tests. */ test_miniflow(&flow); test_minimask_has_extra(&flow); test_minimask_combine(&flow); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/odp_target.c000066400000000000000000000073441514270232600242260ustar00rootroot00000000000000#include #include "fuzzer.h" #undef NDEBUG #include "odp-util.h" #include #include "openvswitch/dynamic-string.h" #include "flow.h" #include "openvswitch/match.h" #include "openvswitch/ofpbuf.h" #include "util.h" #include "openvswitch/ofp-flow.h" #include "openvswitch/vlog.h" static int parse_keys(bool wc_keys, const char *in) { int exit_code = 0; enum odp_key_fitness fitness; struct ofpbuf odp_key; struct ofpbuf odp_mask; struct flow flow; struct ds out; int error; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); error = odp_flow_from_string(in, NULL, &odp_key, &odp_mask, NULL); if (error) { printf("odp_flow_from_string: error\n"); goto next; } if (!wc_keys) { struct odp_flow_key_parms odp_parms = { .flow = &flow, .support = { .recirc = true, .ct_state = true, .ct_zone = true, .ct_mark = true, .ct_label = true, .max_vlan_headers = SIZE_MAX, }, }; /* Convert odp_key to flow. */ fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow, NULL); switch (fitness) { case ODP_FIT_PERFECT: break; case ODP_FIT_TOO_LITTLE: printf("ODP_FIT_TOO_LITTLE: "); break; case ODP_FIT_TOO_MUCH: printf("ODP_FIT_TOO_MUCH: "); break; case ODP_FIT_ERROR: printf("odp_flow_key_to_flow: error\n"); goto next; } /* Convert cls_rule back to odp_key. */ ofpbuf_uninit(&odp_key); ofpbuf_init(&odp_key, 0); odp_flow_key_from_flow(&odp_parms, &odp_key); if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) { printf ("too long: %"PRIu32" > %d\n", odp_key.size, ODPUTIL_FLOW_KEY_BYTES); exit_code = 1; } } /* Convert odp_key to string. */ ds_init(&out); if (wc_keys) { odp_flow_format(odp_key.data, odp_key.size, odp_mask.data, odp_mask.size, NULL, &out, false, false); } else { odp_flow_key_format(odp_key.data, odp_key.size, &out); } puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); return exit_code; } static int parse_actions(const char *in) { struct ofpbuf odp_actions; struct ds out; int error; /* Convert string to OVS DP actions. */ ofpbuf_init(&odp_actions, 0); error = odp_actions_from_string(in, NULL, &odp_actions); if (error) { printf("odp_actions_from_string: error\n"); goto next; } /* Convert odp_actions back to string. */ ds_init(&out); format_odp_actions(&out, odp_actions.data, odp_actions.size, NULL); puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_actions); return 0; } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { /* Bail out if we cannot construct at least a 1 char string. */ const char *input = (const char *) data; if (size < 2 || input[size - 1] != '\0' || strchr(input, '\n') || strlen(input) != size - 1) { return 0; } /* Disable logging to avoid write to disk. */ static bool isInit = false; if (!isInit) { vlog_set_verbosity("off"); isInit = true; } /* Parse keys and wc keys. */ parse_keys(false, input); parse_keys(true, input); /* Parse actions. */ parse_actions(input); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/ofctl_parse_target.c000066400000000000000000000063321514270232600257410ustar00rootroot00000000000000#include #include "fuzzer.h" #include "openvswitch/ofp-flow.h" #include "ofp-version-opt.h" #include "ofproto/ofproto.h" #include "openflow/openflow.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/vlog.h" #include "util.h" static void ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol = 0; char *usable_s; size_t i; usable_s = ofputil_protocols_to_string(usable_protocols); printf("usable protocols: %s\n", usable_s); free(usable_s); if (!(usable_protocols & OFPUTIL_P_ANY)) { printf("no usable protocol\n"); goto free; } for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { protocol = 1u << i; if (protocol & usable_protocols & OFPUTIL_P_ANY) { break; } } ovs_assert(is_pow2(protocol)); printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol)); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *msg; msg = ofputil_encode_flow_mod(fm, protocol); ofpbuf_delete(msg); } free: for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; free(CONST_CAST(struct ofpact *, fm->ofpacts)); minimatch_destroy(&fm->match); } } /* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints * it back to stdout. */ static void ofctl_parse_flow(const char *input, int command) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod fm; char *error; error = parse_ofp_flow_mod_str(&fm, input, NULL, NULL, command, &usable_protocols); if (error) { printf("Error encountered: %s\n", error); free(error); } else { ofctl_parse_flows__(&fm, 1, usable_protocols); } } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { /* Bail out if we cannot construct at least a 1 char string. * Reserve 1 byte to decide flow mod command. * * Here's the structure of data we expect * |--Byte 1--|--Byte 2--|...|--Byte (size-1)--| * * where, * * Byte 1: Used to decide which ofp flow mod command to test * Bytes 2--(size-1): The C string that is actually passed to * ofctl_parse_flow() test API. * * This means that the fuzzed input is actually a C string of * length = (size -2) with the terminal byte being the NUL * character. Moreover, this string is expected to not contain * a new-line character. */ const char *stream = (const char *) data; if (size < 3 || stream[size - 1] != '\0' || strchr(&stream[1], '\n') || strlen(&stream[1]) != size - 2) { return 0; } /* Disable logging to avoid write to disk. */ static bool isInit = false; if (!isInit) { vlog_set_verbosity("off"); isInit = true; } /* Decide test parameters using first byte of fuzzed input. */ int command = (stream[0] % OFPFC_DELETE_STRICT) + 1; /* Fuzz extended match parsing. */ const char *input = &stream[1]; ofctl_parse_flow(input, command); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/oss-fuzz/ofp_print_target.c000066400000000000000000000021541514270232600254360ustar00rootroot00000000000000#include #include "fuzzer.h" #include "dp-packet.h" #include "openvswitch/ofp-print.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/vlog.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size < sizeof(struct ofp_header)) { return 0; } static bool isInit = false; if (!isInit) { vlog_set_verbosity("off"); isInit = true; } struct ofpbuf b; ofpbuf_use_const(&b, data, size); for (;;) { /* Check if ofpbuf contains ofp header. */ struct ofp_header *oh = ofpbuf_at(&b, 0, sizeof *oh); if (!oh) { break; } /* Check if length is geq than lower bound. */ size_t length = ntohs(oh->length); if (length < sizeof *oh) { break; } /* Check if ofpbuf contains payload. */ size_t tail_len = length - sizeof *oh; void *tail = ofpbuf_at(&b, sizeof *oh, tail_len); if (!tail) { break; } ofp_print(stdout, ofpbuf_pull(&b, length), length, NULL, NULL, 2); } ofpbuf_uninit(&b); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovs-macros.at000066400000000000000000000343241514270232600225470ustar00rootroot00000000000000AT_TESTED([ovs-vswitchd]) AT_TESTED([ovs-vsctl]) m4_include([m4/compat.m4]) dnl Make AT_SETUP automatically run the ovs_init() shell function dnl as the first step in every test. m4_rename([AT_SETUP], [OVS_AT_SETUP]) m4_define([AT_SETUP], [OVS_AT_SETUP($@) ovs_init ]) dnl Make AT_CLEANUP check for Address Sanitizer errors as the last step dnl in every test. m4_rename([AT_CLEANUP], [OVS_AT_CLEANUP]) m4_define([AT_CLEANUP], [ovs_cleanup OVS_AT_CLEANUP($@) ]) dnl OVS_START_SHELL_HELPERS...OVS_END_SHELL_HELPERS may bracket shell dnl function definitions that invoke AT_CHECK and other Autotest macros dnl that can ordinarily be run only within AT_SETUP...AT_CLEANUP. m4_define([OVS_START_SHELL_HELPERS], [m4_ifdef([AT_ingroup], [m4_fatal([$0: AT_SETUP and OVS_DEFINE_SHELL_HELPERS may not nest])]) m4_define([AT_ingroup]) m4_divert_push([PREPARE_TESTS])]) m4_define([OVS_END_SHELL_HELPERS], [ m4_divert_pop([PREPARE_TESTS]) m4_undefine([AT_ingroup])]) m4_divert_push([PREPARE_TESTS]) [ # Set ovs_base to the base directory in which the test is running and # initialize the OVS_*DIR environment variables to point to this # directory. ovs_init() { ovs_base=`pwd` trap ovs_on_exit 0 : > cleanup ovs_setenv } # Catch testsuite error condition and cleanup test environment by tearing down # all interfaces and processes spawned. # User has an option to leave the test environment in error state so that system # can be poked around to get more information. User can enable this option by setting # environment variable OVS_PAUSE_TEST=1. User needs to press CTRL-D to resume the # cleanup operation. ovs_pause() { echo "=====================================================" echo "Set following environment variable to use various ovs utilities" echo "export OVS_RUNDIR=$ovs_base" echo "Press ENTER to continue: " read } ovs_on_exit () { if [ ! -z "${OVS_PAUSE_TEST}" ] && [ -z $at_verbose ]; then trap '' INT ovs_pause fi . "$ovs_base/cleanup" } # With no parameter or an empty parameter, sets the OVS_*DIR # environment variables to point to $ovs_base, the base directory in # which the test is running. # # With a parameter, sets them to $ovs_base/$1. ovs_setenv() { sandbox=$1 ovs_dir=$ovs_base${1:+/$1} OVS_RUNDIR=$ovs_dir; export OVS_RUNDIR OVS_LOGDIR=$ovs_dir; export OVS_LOGDIR OVS_DBDIR=$ovs_dir; export OVS_DBDIR OVS_SYSCONFDIR=$ovs_dir; export OVS_SYSCONFDIR OVS_PKGDATADIR=$ovs_dir; export OVS_PKGDATADIR } # Prints the integers from $1 to $2, increasing by $3 (default 1) on stdout. seq () { if test $# = 1; then set 1 $1 fi while test $1 -le $2; do echo $1 set `expr $1 + ${3-1}` $2 $3 done } if test "$IS_WIN32" = "yes"; then pwd () { command pwd -W "$@" } diff () { command diff --strip-trailing-cr "$@" } # tskill is more effective than taskkill but it isn't always installed. if (tskill //?) >/dev/null 2>&1; then :; else tskill () { taskkill //F //PID $1 >/dev/null; } fi kill () { signal= retval=0 for arg; do arg=$(echo $arg | tr -d '\n\r') case $arg in -*) signal=$arg ;; [1-9][0-9]*) # tasklist always returns 0. # If pid does exist, there will be a line with the pid. if tasklist //fi "PID eq $arg" | grep $arg >/dev/null; then if test "X$signal" != "X-0"; then tskill $arg fi else retval=1 fi ;; esac done return $retval } fi # parent_pid PID # # Prints the PID of the parent of process PID. parent_pid () { # Using "ps" is portable to any POSIX system, but busybox "ps" (used in # e.g. Alpine Linux) is noncompliant, so we use a Linux-specific approach # when it's available. We check the format of the status file to avoid # the NetBSD file with the same name but different contents. if grep -E '^PPid:[[:space:]]*[0-9]*$' /proc/$1/status > /dev/null 2>&1; then sed -n 's/^PPid: \([0-9]*\)/\1/p' /proc/$1/status else ps -o ppid= -p $1 fi } # kill_ovs_vswitchd [PID] # # Signal the ovs-vswitchd daemon to exit gracefully and wait for it to # terminate or kill it if that takes too long. # # It is used to cleanup all sorts of tests and results. It can't assume # any state, including the availability of PID file which can be provided. kill_ovs_vswitchd () { # Use provided PID or save the current PID if available. TMPPID=$1 if test -z "$TMPPID"; then TMPPID=$(cat $OVS_RUNDIR/ovs-vswitchd.pid 2>/dev/null) fi # Tell the daemon to terminate gracefully ovs-appctl -t ovs-vswitchd exit --cleanup 2>/dev/null # Nothing else to be done if there is no PID test -z "$TMPPID" && return for i in 1 2 3 4 5 6 7 8 9; do # Check if the daemon is alive. kill -0 $TMPPID 2>/dev/null || return # Fallback to whole number since POSIX doesn't require # fractional times to work. sleep 0.1 || sleep 1 done # Make sure it is terminated. kill $TMPPID } # Normalize the output of 'wc' to match POSIX. # POSIX says 'wc' should print "%d %d %d", but GNU prints "%7d %7d %7d". # POSIX says 'wc -l' should print "%d %s", but BSD prints "%8d". # # This fixes all of those (it will screw up filenames that contain # multiple sequential spaces, but that doesn't really matter). wc () { command wc "$@" | tr -s ' ' ' ' | sed 's/^ *//' } uuidfilt () { $PYTHON3 "$top_srcdir"/tests/uuidfilt.py "$@" } # run_as PROGRAM_NAME COMMAND [ARG...] # # Runs a command with argv[0] set to PROGRAM_NAME, if possible, in a # subshell. Most utilities print argc[0] as part of their messages, # so this makes it easier to figure out which particular utility # prints a message if a bunch of identical processes are running. # # Not all shells support "exec -a NAME", so test for it. if (exec -a myname true 2>/dev/null); then run_as () { (exec -a "$@") } else run_as () { shift (exec "$@") } fi ] m4_divert_pop([PREPARE_TESTS]) OVS_START_SHELL_HELPERS ovs_cleanup() { if test "$(echo sanitizers.*)" != 'sanitizers.*'; then echo "Undefined Behavior Sanitizer or Address Sanitizer reported errors in:" sanitizers.* cat sanitizers.* AT_FAIL_IF([:]) fi } ovs_wait () { echo "$1: waiting $2..." >&AS_MESSAGE_LOG_FD # First try the condition without waiting. if ovs_wait_cond; then echo "$1: wait succeeded immediately" >&AS_MESSAGE_LOG_FD; return 0; fi # Try a quick sleep, so that the test completes very quickly # in the normal case. POSIX doesn't require fractional times to # work, so this might not work. sleep 0.1 if ovs_wait_cond; then echo "$1: wait succeeded quickly" >&AS_MESSAGE_LOG_FD; return 0; fi # Then wait up to OVS_CTL_TIMEOUT seconds. local d for d in `seq 1 "$OVS_CTL_TIMEOUT"`; do sleep 1 if ovs_wait_cond; then echo "$1: wait succeeded after $d seconds" >&AS_MESSAGE_LOG_FD; return 0; fi done echo "$1: wait failed after $d seconds" >&AS_MESSAGE_LOG_FD ovs_wait_failed AT_FAIL_IF([:]) } OVS_END_SHELL_HELPERS m4_define([OVS_WAIT], [dnl ovs_wait_cond () { $1 } ovs_wait_failed () { : $2 } ovs_wait "AS_ESCAPE([$3])" "AS_ESCAPE([$4])" ]) dnl OVS_WAIT_UNTIL(COMMAND, [IF-FAILED]) dnl dnl Executes shell COMMAND in a loop until it returns zero. If COMMAND does dnl not return zero within a reasonable time limit, executes the commands dnl in IF-FAILED (if provided) and fails the test. m4_define([OVS_WAIT_UNTIL], [AT_FAIL_IF([test "$#" -ge 3]) dnl The second argument should not be a number (confused with AT_CHECK ?). AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null]) OVS_WAIT([$1], [$2], [AT_LINE], [until $1])]) dnl OVS_WAIT_UNTIL_EQUAL(COMMAND, OUTPUT) dnl dnl Executes shell COMMAND in a loop until it returns zero and the output dnl equals OUTPUT. If COMMAND does not return zero or a desired output within dnl a reasonable time limit, fails the test. m4_define([OVS_WAIT_UNTIL_EQUAL], [AT_FAIL_IF([test "$#" -ge 3]) echo "$2" > wait_until_expected OVS_WAIT_UNTIL([$1 | diff -u wait_until_expected - ])]) dnl OVS_WAIT_WHILE(COMMAND, [IF-FAILED]) dnl dnl Executes shell COMMAND in a loop until it returns nonzero. If COMMAND does dnl not return nonzero within a reasonable time limit, executes the commands dnl in IF-FAILED (if provided) and fails the test. m4_define([OVS_WAIT_WHILE], [AT_FAIL_IF([test "$#" -ge 3]) dnl The second argument should not be a number (confused with AT_CHECK ?). AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null]) OVS_WAIT([if $1; then return 1; else return 0; fi], [$2], [AT_LINE], [while $1])]) dnl OVS_APP_EXIT_AND_WAIT(DAEMON) dnl dnl Ask the daemon named DAEMON to exit, via ovs-appctl, and then wait for it dnl to exit. m4_define([OVS_APP_EXIT_AND_WAIT], [AT_CHECK([test -e $OVS_RUNDIR/$1.pid]) TMPPID=$(cat $OVS_RUNDIR/$1.pid) AT_CHECK(m4_if([$1],[ovs-vswitchd], [ovs-appctl -t $1 exit --cleanup], [ovs-appctl -t $1 exit])) OVS_WAIT_WHILE([kill -0 $TMPPID 2>/dev/null])]) dnl OVS_APP_EXIT_AND_WAIT_BY_TARGET(TARGET, PIDFILE) dnl dnl Ask the daemon identified by TARGET to exit, via ovs-appctl (using the target dnl argument), and then wait for it to exit. m4_define([OVS_APP_EXIT_AND_WAIT_BY_TARGET], [AT_CHECK([test -e $2]) TMPPID=$(cat $2) AT_CHECK([ovs-appctl --target=$1 exit]) OVS_WAIT_WHILE([kill -0 $TMPPID 2>/dev/null])]) dnl OVS_DAEMONIZE([command], [pidfile]) dnl dnl Run 'command' as a background process and record its pid to 'pidfile' to dnl allow cleanup on exit. m4_define([OVS_DAEMONIZE], [$1 & echo $! > $2 on_exit "kill `cat $2`" ]) dnl on_exit "COMMAND" dnl dnl Add the shell COMMAND to a collection executed when the current test dnl completes, as a cleanup action. (The most common use is to kill a dnl daemon started by the test. This is important to prevent tests that dnl start daemons from hanging at exit.) dnl dnl Cleanup commands are executed in the reverse order of calls to this dnl function. m4_divert_text([PREPARE_TESTS], [dnl on_exit () { (echo "$1"; cat cleanup) > cleanup.tmp mv cleanup.tmp cleanup } ]) dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64: m4_ifndef([AS_VAR_APPEND], [m4_divert_text([PREPARE_TESTS], [as_var_append () { eval $1=\$$1\$2 } ]) m4_define([AS_VAR_APPEND], [as_var_append $1 $2])]) dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64: m4_ifndef([AT_CHECK_UNQUOTED], [m4_define([AT_CHECK_UNQUOTED], [_AT_CHECK([$1], [$2], AS_ESCAPE(m4_dquote(m4_expand([$3])), [""]), AS_ESCAPE(m4_dquote(m4_expand([$4])),[""]), [$5], [$6])])]) dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64: m4_ifndef([AT_SKIP_IF], [m4_define([AT_SKIP_IF], [AT_CHECK([($1) \ && exit 77 || exit 0], [0], [ignore], [ignore])])]) dnl Autoconf 2.63 compatibility verison of macro introduced in Autoconf 2.64: m4_ifndef([AT_FAIL_IF], [m4_define([AT_FAIL_IF], [AT_CHECK([($1) \ && exit 99 || exit 0], [0], [ignore], [ignore])])]) dnl Start retis to track all the traffic passing through OVS. m4_define([RETIS_CHECK_AND_RUN], [if test "$OVS_TEST_WITH_RETIS" = yes && retis --version > /dev/null; then on_exit 'retis sort --utc retis.data > retis.sorted' OVS_DAEMONIZE([retis -p ifdump collect --utc --allow-system-changes \ --ovs-track --out --print 2>retis.err 1>retis.log], [retis.pid]) OVS_WAIT_UNTIL([grep -q 'loaded' retis.err]) fi]) dnl Add a rule to always accept the traffic. dnl The first argument to this macro should be the command to run: dnl iptables or ip6tables dnl The second argument to this macro should be the interface name (netdev) m4_define([IPTABLES_ACCEPT], [AT_CHECK([$1 -I INPUT 1 -i $2 -j ACCEPT]) on_exit '$1 -D INPUT 1']) dnl Certain Linux distributions, like CentOS, have default iptable rules dnl to reject input traffic from bridges such as br-underlay. dnl This implies the existence of a ip filter INPUT chain for IPv4 or an dnl ip6 filter INPUT chain for IPv6. If that chain exists then add a rule dnl to it to always accept all traffic. dnl The first argument to this macro should be the filter chain: ip or ipv6 dnl The second argument to this macro should be the interface name (netdev) m4_define([NFT_ACCEPT], [if nft list chain $1 filter INPUT > /dev/null 2>1; then AT_CHECK([nft -ae \ "insert rule $1 filter INPUT iifname \"$2\" counter accept"], [0], [stdout-nolog]) dnl Extract handle, which is used to delete the rule AT_CHECK([sed -n 's/.*handle //; T; p' < stdout], [0], [stdout]) on_exit "nft \"delete rule $1 filter INPUT handle $(cat stdout)\"" fi]) dnl Certain Linux distributions, like CentOS, have default iptable rules dnl to reject input traffic from bridges such as br-underlay. dnl Add a rule to always accept the traffic. dnl IPv4 variant of this macro. m4_define([XT_ACCEPT], [if test $HAVE_NFT = yes; then NFT_ACCEPT([ip], [$1]) else IPTABLES_ACCEPT([iptables], [$1]) fi]) dnl Certain Linux distributions, like CentOS, have default iptable rules dnl to reject input traffic from bridges such as br-underlay. dnl Add a rule to always accept the traffic. dnl IPv6 variant of this macro. m4_define([XT6_ACCEPT], [if test $HAVE_NFT = yes; then NFT_ACCEPT([ip6], [$1]) else IPTABLES_ACCEPT([ip6tables], [$1]) fi]) dnl CHECK_CPU_DISCOVERED([n_cpu]) dnl dnl Waits until CPUs are discovered and checks if the number of discovered CPUs dnl is greater or equal to 'n_cpu'. Without the 'n_cpu' parameter checks that dnl at least one CPU is discovered. m4_define([CHECK_CPU_DISCOVERED], [ PATTERN="Discovered [[0-9]]* NUMA nodes and [[0-9]]* CPU cores" OVS_WAIT_UNTIL([grep "$PATTERN" ovs-vswitchd.log]) N_CPU=$(grep "$PATTERN" ovs-vswitchd.log \ | sed -e 's/.* \([[0-9]]*\) CPU cores/\1/') if [[ -z "$1" ]] then AT_CHECK([test "$N_CPU" -gt "0"]) else AT_SKIP_IF([test "$N_CPU" -lt "$1"]) fi ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovs-ofctl.at000066400000000000000000005136231514270232600223760ustar00rootroot00000000000000AT_BANNER([ovs-ofctl]) AT_SETUP([ovs-ofctl parse-flows choice of protocol]) # This doesn't cover some potential vlan_tci test cases. for test_case in \ 'tun_id=0 NXM,OXM' \ 'tun_id=0/0x1 NXM,OXM' \ 'tun_src=1.2.3.4 NXM,OXM' \ 'tun_src=1.2.3.4/0.0.0.1 NXM,OXM' \ 'tun_dst=1.2.3.4 NXM,OXM' \ 'tun_dst=1.2.3.4/0.0.0.1 NXM,OXM' \ 'tun_flags=1 NXM,OXM' \ 'tun_flags=+oam NXM,OXM' \ 'tun_tos=0 none' \ 'tun_ttl=0 none' \ 'tun_gbp_id=0 NXM,OXM' \ 'tun_gbp_id=0/0x1 NXM,OXM' \ 'tun_gbp_flags=0 NXM,OXM' \ 'tun_gbp_flags=0/0x1 NXM,OXM' \ 'tun_metadata0=0 NXM,OXM' \ 'tun_metadata0=0/0x1 NXM,OXM' \ 'tun_metadata0 NXM,OXM' \ 'metadata=0 NXM,OXM,OpenFlow11' \ 'metadata=1/1 NXM,OXM,OpenFlow11' \ 'in_port=1 any' \ 'skb_priority=0 none' \ 'pkt_mark=1 NXM,OXM' \ 'pkt_mark=1/1 NXM,OXM' \ 'reg0=0 NXM,OXM' \ 'reg0=0/1 NXM,OXM' \ 'reg1=1 NXM,OXM' \ 'reg1=1/1 NXM,OXM' \ 'reg2=2 NXM,OXM' \ 'reg2=2/1 NXM,OXM' \ 'reg3=3 NXM,OXM' \ 'reg3=3/1 NXM,OXM' \ 'reg4=4 NXM,OXM' \ 'reg4=4/1 NXM,OXM' \ 'reg5=5 NXM,OXM' \ 'reg5=5/1 NXM,OXM' \ 'reg6=6 NXM,OXM' \ 'reg6=6/1 NXM,OXM' \ 'reg7=7 NXM,OXM' \ 'reg8=8/1 NXM,OXM' \ 'reg8=8 NXM,OXM' \ 'reg9=9/1 NXM,OXM' \ 'reg9=9 NXM,OXM' \ 'reg10=10 NXM,OXM' \ 'reg10=10/1 NXM,OXM' \ 'reg11=11 NXM,OXM' \ 'reg11=11/1 NXM,OXM' \ 'reg12=12 NXM,OXM' \ 'reg12=12/1 NXM,OXM' \ 'reg13=13 NXM,OXM' \ 'reg13=13/1 NXM,OXM' \ 'reg14=14 NXM,OXM' \ 'reg14=14/1 NXM,OXM' \ 'reg15=0 NXM,OXM' \ 'reg15=0/1 NXM,OXM' \ 'reg16=16 NXM,OXM' \ 'reg16=16/1 NXM,OXM' \ 'reg17=17 NXM,OXM' \ 'reg17=17/1 NXM,OXM' \ 'reg18=18 NXM,OXM' \ 'reg18=18/1 NXM,OXM' \ 'reg19=19 NXM,OXM' \ 'reg19=19/1 NXM,OXM' \ 'reg20=20 NXM,OXM' \ 'reg20=20/1 NXM,OXM' \ 'reg21=21 NXM,OXM' \ 'reg21=21/1 NXM,OXM' \ 'reg22=22 NXM,OXM' \ 'reg22=22/1 NXM,OXM' \ 'reg23=23/1 NXM,OXM' \ 'reg23=23 NXM,OXM' \ 'reg24=24/1 NXM,OXM' \ 'reg24=24 NXM,OXM' \ 'reg25=25 NXM,OXM' \ 'reg25=25/1 NXM,OXM' \ 'reg26=26 NXM,OXM' \ 'reg26=26/1 NXM,OXM' \ 'reg27=27 NXM,OXM' \ 'reg27=27/1 NXM,OXM' \ 'reg28=28 NXM,OXM' \ 'reg28=28/1 NXM,OXM' \ 'reg29=29 NXM,OXM' \ 'reg29=29/1 NXM,OXM' \ 'xreg0=0 NXM,OXM' \ 'xreg0=0/1 NXM,OXM' \ 'xreg1=1 NXM,OXM' \ 'xreg1=1/1 NXM,OXM' \ 'xreg2=2 NXM,OXM' \ 'xreg2=2/3 NXM,OXM' \ 'xreg3=3 NXM,OXM' \ 'xreg3=3/5 NXM,OXM' \ 'xreg4=4 NXM,OXM' \ 'xreg4=4/1 NXM,OXM' \ 'xreg5=5 NXM,OXM' \ 'xreg5=5/1 NXM,OXM' \ 'xreg6=6 NXM,OXM' \ 'xreg6=6/1 NXM,OXM' \ 'xreg7=7 NXM,OXM' \ 'xreg7=7/1 NXM,OXM' \ 'xreg8=8 NXM,OXM' \ 'xreg8=8/1 NXM,OXM' \ 'xreg9=9 NXM,OXM' \ 'xreg9=9/1 NXM,OXM' \ 'xreg10=10 NXM,OXM' \ 'xreg10=10/1 NXM,OXM' \ 'xreg11=11 NXM,OXM' \ 'xreg11=11/5 NXM,OXM' \ 'xreg12=12 NXM,OXM' \ 'xreg12=12/1 NXM,OXM' \ 'xreg13=13 NXM,OXM' \ 'xreg13=13/1 NXM,OXM' \ 'xreg14=14 NXM,OXM' \ 'xreg14=14/1 NXM,OXM' \ 'xreg15=15 NXM,OXM' \ 'xreg15=15/1 NXM,OXM' \ 'xxreg0=0 NXM,OXM' \ 'xxreg0=0/1 NXM,OXM' \ 'xxreg1=1 NXM,OXM' \ 'xxreg1=1/1 NXM,OXM' \ 'xxreg2=2 NXM,OXM' \ 'xxreg2=2/1 NXM,OXM' \ 'xxreg3=3 NXM,OXM' \ 'xxreg3=3/1 NXM,OXM' \ 'xxreg4=4 NXM,OXM' \ 'xxreg4=4/1 NXM,OXM' \ 'xxreg5=5 NXM,OXM' \ 'xxreg5=5/1 NXM,OXM' \ 'xxreg6=6 NXM,OXM' \ 'xxreg6=6/1 NXM,OXM' \ 'xxreg7=7 NXM,OXM' \ 'xxreg7=7/1 NXM,OXM' \ 'xxreg3[[0..0]]=1 NXM,OXM' \ 'xxreg3[[126..127]]=3 NXM,OXM' \ 'xxreg7[[0..0]]=1 NXM,OXM' \ 'xxreg7[[126..127]]=3 NXM,OXM' \ 'reg3[[]]=3 NXM,OXM' \ 'dl_src=00:11:22:33:44:55 any' \ 'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM,OpenFlow11' \ 'dl_dst=00:11:22:33:44:55 any' \ 'dl_dst=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM,OpenFlow11' \ 'dl_type=0x1234 any' \ 'dl_type=0x0800 any' \ 'dl_type=0x0806 any' \ 'dl_type=0x86dd any' \ 'vlan_tci=0 any' \ 'vlan_tci=0x1009 any' \ 'vlan_tci=0x1009/0x1 NXM,OXM' \ 'dl_vlan=9 any' \ 'vlan_vid=11 any' \ 'vlan_vid=11/0x1 NXM,OXM' \ 'dl_vlan_pcp=6 any' \ 'vlan_pcp=5 any' \ 'mpls,mpls_label=5 NXM,OXM,OpenFlow11' \ 'mpls,mpls_tc=1 NXM,OXM,OpenFlow11' \ 'mpls,mpls_bos=0 NXM,OXM' \ 'mpls,mpls_ttl=5 NXM,OXM' \ 'ip,ip_src=1.2.3.4 any' \ 'ip,ip_src=192.168.0.0/24 any' \ 'ip,ip_src=192.0.168.0/255.0.255.0 NXM,OXM,OpenFlow11' \ 'ip,ip_dst=1.2.3.4 any' \ 'ip,ip_dst=192.168.0.0/24 any' \ 'ip,ip_dst=192.0.168.0/255.0.255.0 NXM,OXM,OpenFlow11' \ 'ipv6,ipv6_src=::1 NXM,OXM' \ 'ipv6,ipv6_src=0:0:0:0:0:0:0:1/::1 NXM,OXM' \ 'ipv6,ipv6_dst=::1 NXM,OXM' \ 'ipv6,ipv6_dst=0:0:0:0:0:0:0:1/::1 NXM,OXM' \ 'ipv6,ipv6_label=5 NXM,OXM' \ 'ipv6,ipv6_label=5/1 NXM,OXM' \ 'ip,nw_proto=1 any' \ 'ipv6,nw_proto=1 NXM,OXM' \ 'ip,nw_tos=0xf0 any' \ 'ipv6,nw_tos=0xf0 NXM,OXM' \ 'ip,ip_dscp=0x3c any' \ 'ipv6,ip_dscp=0x3c NXM,OXM' \ 'ip,nw_ecn=1 NXM,OXM,OpenFlow11' \ 'ipv6,nw_ecn=1 NXM,OXM' \ 'ip,nw_ttl=5 NXM,OXM' \ 'ipv6,nw_ttl=5 NXM,OXM' \ 'ip,ip_frag=no NXM,OXM' \ 'ipv6,ip_frag=no NXM,OXM' \ 'arp,arp_op=0 any' \ 'arp,arp_spa=1.2.3.4 any' \ 'arp,arp_spa=1.2.3.4/0.0.0.1 NXM,OXM,OpenFlow11' \ 'arp,arp_tpa=1.2.3.4 any' \ 'arp,arp_tpa=1.2.3.4/0.0.0.1 NXM,OXM,OpenFlow11' \ 'arp,arp_sha=00:11:22:33:44:55 NXM,OXM' \ 'arp,arp_sha=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM' \ 'arp,arp_tha=00:11:22:33:44:55 NXM,OXM' \ 'arp,arp_tha=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM' \ 'tcp,tcp_src=80 any' \ 'tcp,tcp_src=0x1000/0x1000 NXM,OXM' \ 'tcp6,tcp_src=80 NXM,OXM' \ 'tcp6,tcp_src=0x1000/0x1000 NXM,OXM' \ 'tcp,tcp_dst=80 any' \ 'tcp,tcp_dst=0x1000/0x1000 NXM,OXM' \ 'tcp6,tcp_dst=80 NXM,OXM' \ 'tcp6,tcp_dst=0x1000/0x1000 NXM,OXM' \ 'udp,udp_src=80 any' \ 'udp,udp_src=0x1000/0x1000 NXM,OXM' \ 'udp6,udp_src=80 NXM,OXM' \ 'udp6,udp_src=0x1000/0x1000 NXM,OXM' \ 'udp,udp_dst=80 any' \ 'udp,udp_dst=0x1000/0x1000 NXM,OXM' \ 'udp6,udp_dst=80 NXM,OXM' \ 'udp6,udp_dst=0x1000/0x1000 NXM,OXM' \ 'udp6,udp_dst[[12]]=1 NXM,OXM' \ 'icmp,icmp_type=1 any' \ 'icmp,icmp_code=2 any' \ 'icmp6,icmpv6_type=1 NXM,OXM' \ 'icmp6,icmpv6_code=2 NXM,OXM' \ 'ct_state=+trk NXM,OXM' \ 'ct_zone=0 NXM,OXM' \ 'ct_mark=0 NXM,OXM' \ 'ct_label=0 NXM,OXM' \ 'ct_label=0x1234567890ABCDEF12345678 NXM,OXM' do set $test_case echo echo "### test case: '$1' should have usable protocols '$2'" if test "$2" = none; then AT_CHECK([ovs-ofctl parse-flow "$1,actions=drop"], [1], [dnl ], [ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) else AT_CHECK_UNQUOTED([ovs-ofctl parse-flow "$1,actions=drop" | sed 1q], [0], [usable protocols: $2 ]) fi done AT_CLEANUP AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.0)]) AT_DATA([flows.txt], [[ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note ip,actions=set_field:10.4.3.77->ip_src,mod_nw_ecn:2 sctp actions=drop sctp actions=drop ip,nw_proto=2 actions=drop in_port=0 actions=resubmit:0 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) ip,actions=ct(nat) ip,actions=ct(commit,nat(dst)) ip,actions=ct(commit,nat(src)) ip,actions=ct(commit,nat(src=10.0.0.240,random)) ip,actions=ct(commit,nat(src=10.0.0.240:32768-65535,random)) ip,actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)) ip,actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)) ipv6,actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)) ipv6,actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)) ipv6,actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)) tcp,actions=ct(commit,nat(src=10.1.1.240-10.1.1.255),alg=ftp) actions=in_port,output:in_port ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt ], [0], [stdout]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [[usable protocols: any chosen protocol: OpenFlow10-table_id OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD OFPT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 OFPT_FLOW_MOD: ADD ip actions=mod_nw_src:10.4.3.77,load:0x2->NXM_NX_IP_ECN[] OFPT_FLOW_MOD: ADD sctp actions=drop OFPT_FLOW_MOD: ADD sctp actions=drop OFPT_FLOW_MOD: ADD ip,nw_proto=2 actions=drop OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0 OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,ingress) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789,egress) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63]) OFPT_FLOW_MOD: ADD ip actions=ct(nat) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(dst)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src=10.0.0.240,random)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src=10.0.0.240:32768-65535,random)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)) OFPT_FLOW_MOD: ADD ip actions=ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)) OFPT_FLOW_MOD: ADD ipv6 actions=ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)) OFPT_FLOW_MOD: ADD ipv6 actions=ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)) OFPT_FLOW_MOD: ADD ipv6 actions=ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)) OFPT_FLOW_MOD: ADD tcp actions=ct(commit,nat(src=10.1.1.240-10.1.1.255),alg=ftp) OFPT_FLOW_MOD: ADD actions=IN_PORT,IN_PORT ]]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.1)]) AT_DATA([flows.txt], [[ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note ip,actions=mod_nw_ttl:1,set_field:10.4.3.77->ip_src ip,nw_proto=2 actions=drop sctp actions=drop sctp actions=drop in_port=0 actions=resubmit:0 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt ], [0], [stdout]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [[usable protocols: any chosen protocol: OpenFlow11 OFPT_FLOW_MOD (OF1.1): ADD tcp,tp_src=123 actions=FLOOD OFPT_FLOW_MOD (OF1.1): ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop OFPT_FLOW_MOD (OF1.1): ADD udp,dl_vlan_pcp=7 idle:5 actions=pop_vlan,output:0 OFPT_FLOW_MOD (OF1.1): ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 OFPT_FLOW_MOD (OF1.1): ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1 OFPT_FLOW_MOD (OF1.1): ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 OFPT_FLOW_MOD (OF1.1): ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 OFPT_FLOW_MOD (OF1.1): ADD ip actions=mod_nw_ttl:1,mod_nw_src:10.4.3.77 OFPT_FLOW_MOD (OF1.1): ADD ip,nw_proto=2 actions=drop OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0 OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.2)]) AT_DATA([flows.txt], [[ # comment tcp,tp_src[5]=1,actions=flood tcp,tp_src[6..10]=19,actions=flood tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=mod_vlan_vid:7,mod_vlan_pcp:2 udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note ipv6,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src sctp actions=set_field:3334->sctp_src sctp actions=set_field:4445->sctp_dst tcp actions=mod_tp_dst:1234 udp actions=mod_tp_src:1111 ip actions=mod_nw_src:10.1.1.2,mod_nw_dst:192.168.10.1,mod_nw_ttl:1,mod_nw_tos:16,mod_nw_ecn:2 ip,nw_proto=2 actions=drop in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60 in_port=0 actions=resubmit:0 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt ], [0], [stdout]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [[usable protocols: NXM,OXM chosen protocol: OXM-OpenFlow12 OFPT_FLOW_MOD (OF1.2): ADD tcp,tp_src=0x20/0x20 actions=FLOOD OFPT_FLOW_MOD (OF1.2): ADD tcp,tp_src=0x4c0/0x7c0 actions=FLOOD OFPT_FLOW_MOD (OF1.2): ADD tcp,tp_src=123 actions=FLOOD OFPT_FLOW_MOD (OF1.2): ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=set_field:4103->vlan_vid,set_field:2->vlan_pcp OFPT_FLOW_MOD (OF1.2): ADD udp,dl_vlan_pcp=7 idle:5 actions=pop_vlan,output:0 OFPT_FLOW_MOD (OF1.2): ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 OFPT_FLOW_MOD (OF1.2): ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 OFPT_FLOW_MOD (OF1.2): ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 OFPT_FLOW_MOD (OF1.2): ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 OFPT_FLOW_MOD (OF1.2): ADD ipv6 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src OFPT_FLOW_MOD (OF1.2): ADD sctp actions=set_field:3334->sctp_src OFPT_FLOW_MOD (OF1.2): ADD sctp actions=set_field:4445->sctp_dst OFPT_FLOW_MOD (OF1.2): ADD tcp actions=set_field:1234->tcp_dst OFPT_FLOW_MOD (OF1.2): ADD udp actions=set_field:1111->udp_src OFPT_FLOW_MOD (OF1.2): ADD ip actions=set_field:10.1.1.2->ip_src,set_field:192.168.10.1->ip_dst,mod_nw_ttl:1,set_field:4->ip_dscp,set_field:2->nw_ecn OFPT_FLOW_MOD (OF1.2): ADD ip,nw_proto=2 actions=drop OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_src,set_field:10:20:30:40:50:60->eth_dst OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0 OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=0) ]]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-flow with invalid mask]) for test_case in \ 'tun_tos 1/1' \ 'tun_ttl 1/1' \ 'skb_priority 1/1' \ 'eth_type 0x1234/0x1' \ 'dl_vlan 9/0x1' \ 'dl_vlan_pcp 6/0x1' \ 'vlan_pcp 5/0x1' \ 'mpls mpls_label 5/0x1' \ 'mpls mpls_tc 1/0x1' \ 'mpls mpls_bos 1/0x1' \ 'ip nw_proto 1/1' \ 'ipv6 nw_proto 1/1' \ 'ip nw_tos 0xf0/0xf0' \ 'ipv6 nw_tos 0xf0/0xf0' \ 'ip ip_dscp 0x3c/0xf0' \ 'ipv6 ip_dscp 0x3c/0xf0' \ 'ip nw_ecn 1/1' \ 'ipv6 nw_ecn 1/1' \ 'ip nw_ttl 5/1' \ 'ipv6 nw_ttl 5/1' \ 'arp arp_op 0/1' \ 'icmp icmp_type 1/1' \ 'icmp icmp_code 2/1' \ 'icmp6 icmpv6_code 2/1' do set $test_case if test $# = 3; then prereq=$1, field=$2 value=$3 else prereq= field=$1 value=$2 fi AT_CHECK_UNQUOTED([ovs-ofctl parse-flow "$prereq$field=$value,actions=drop"], [1], [], [ovs-ofctl: $value: invalid mask for field $field ]) done AT_CLEANUP AT_SETUP([ovs-ofctl action inconsistency (OpenFlow 1.1)]) OVS_VSWITCHD_START add_of_ports br0 1 2 3 AT_CHECK([ovs-ofctl --protocols OpenFlow11 add-flow br0 'ip actions=mod_tp_dst:1234' ], [1], [stdout], [ovs-ofctl: none of the usable flow formats (OpenFlow10,NXM) is among the allowed flow formats (OpenFlow11) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl parse-flows (skb_priority)]) AT_DATA([flows.txt], [[ skb_priority=0x12341234,tcp,tp_src=123,actions=flood ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt ], [1], [dnl ], [stderr]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-flows (NXM)]) AT_DATA([flows.txt], [[ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop pkt_mark=0xbb,actions=set_field:0xaa->pkt_mark udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tcp,tp_src=0x1230/0xfff0,tun_id=0x1234,cookie=0x5678,actions=flood actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789 actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, reg0[0..12]) actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l3, 1024, iter_hash, 5000, 5050, reg0[0..12]) table=1,actions=drop tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop actions=bundle(eth_src,50,active_backup,ofport,members:1) actions=bundle(symmetric_l4,60,hrw,ofport,members:2,3) actions=bundle(symmetric_l4,60,hrw,ofport,members:) actions=bundle(symmetric_l3,60,hrw,ofport,members:2,3) actions=bundle(symmetric_l3,60,hrw,ofport,members:) actions=output:1,bundle(eth_src,0,hrw,ofport,members:1),output:2 actions=bundle_load(eth_src,50,active_backup,ofport,reg0,members:1) actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) actions=bundle_load(symmetric_l4,60,hrw,ofport,reg0[0..15],members:[2,3]) actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],members:) actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) actions=bundle_load(symmetric_l3,60,hrw,ofport,reg0[0..15],members:[2,3]) actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..30],members:) actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],members:1),output:2 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3) send_flow_rem,actions=output:1,output:NXM_NX_REG0,output:2,output:reg1[16..31],output:3 check_overlap,actions=output:1,exit,output:2 tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15) actions=controller(max_len=123,reason=invalid_ttl,id=555) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0,actions=drop ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[]))) ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->NXM_NX_CT_LABEL[32..95]))) ip,actions=ct(commit,exec(load(1->ct_mark))) ip,actions=ct(commit,exec(load(0x1->ct_label[]))) ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->ct_label[32..95]))) ip,actions=ct(commit,exec(set_field(0x1->ct_label))) ip,ct_state=+trk,ct_label=0x1234567890abcdef12345678,actions=ct(commit) actions=output(max_len=100,port=123) actions=output(port=100,max_len=123) actions=output(port=LOCAL,max_len=123) actions=output(port=IN_PORT,max_len=123) mpls,mpls_label=1,actions=set_mpls_label(0) mpls,mpls_label=1,actions=set_mpls_label(10) mpls,mpls_label=1,actions=set_mpls_label(0x10) mpls,mpls_label=1,actions=set_mpls_label(0xfffff) mpls,mpls_tc=1,actions=set_mpls_tc(0) mpls,mpls_tc=1,actions=set_mpls_tc(3) mpls,mpls_tc=1,actions=set_mpls_tc(7) mpls,mpls_ttl=1,actions=set_mpls_ttl(0) mpls,mpls_ttl=1,actions=set_mpls_ttl(200) mpls,mpls_ttl=1,actions=set_mpls_ttl(255) ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt ], [0], [stdout]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [[usable protocols: OXM,NXM+table_id chosen protocol: NXM+table_id NXT_FLOW_MOD: ADD table:255 tcp,tp_src=123 actions=FLOOD NXT_FLOW_MOD: ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop NXT_FLOW_MOD: ADD table:255 pkt_mark=0xbb actions=load:0xaa->NXM_NX_PKT_MARK[] NXT_FLOW_MOD: ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 NXT_FLOW_MOD: ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 NXT_FLOW_MOD: ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 NXT_FLOW_MOD: ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 NXT_FLOW_MOD: ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 NXT_FLOW_MOD: ADD table:255 tcp,tun_id=0x1234,tp_src=0x1230/0xfff0 cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD table:255 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789 NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12]) NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l3,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12]) NXT_FLOW_MOD: ADD table:1 actions=drop NXT_FLOW_MOD: ADD table:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop NXT_FLOW_MOD: ADD table:255 metadata=0x1234000056780000/0xffff0000ffff0000 actions=drop NXT_FLOW_MOD: ADD table:255 actions=bundle(eth_src,50,active_backup,ofport,members:1) NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,members:) NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l3,60,hrw,ofport,members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l3,60,hrw,ofport,members:) NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle(eth_src,0,hrw,ofport,members:1),output:2 NXT_FLOW_MOD: ADD table:255 actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],members:1) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],members:) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],members:2,3) NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..30],members:) NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],members:1),output:2 NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3) NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2 NXT_FLOW_MOD: ADD table:255 tcp actions=fin_timeout(idle_timeout=5,hard_timeout=15) NXT_FLOW_MOD: ADD table:255 actions=controller(reason=invalid_ttl,max_len=123,id=555) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) NXT_FLOW_MOD: ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=5,mpls_tc=1,mpls_ttl=1,mpls_bos=0 actions=drop NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD table:255 ct_state=+trk,ct_label=0x1234567890abcdef12345678,ip actions=ct(commit) NXT_FLOW_MOD: ADD table:255 actions=output(port=123,max_len=100) NXT_FLOW_MOD: ADD table:255 actions=output(port=100,max_len=123) NXT_FLOW_MOD: ADD table:255 actions=output(port=LOCAL,max_len=123) NXT_FLOW_MOD: ADD table:255 actions=output(port=IN_PORT,max_len=123) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=1 actions=set_mpls_label(0) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=1 actions=set_mpls_label(10) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=1 actions=set_mpls_label(16) NXT_FLOW_MOD: ADD table:255 mpls,mpls_label=1 actions=set_mpls_label(1048575) NXT_FLOW_MOD: ADD table:255 mpls,mpls_tc=1 actions=set_mpls_tc(0) NXT_FLOW_MOD: ADD table:255 mpls,mpls_tc=1 actions=set_mpls_tc(3) NXT_FLOW_MOD: ADD table:255 mpls,mpls_tc=1 actions=set_mpls_tc(7) NXT_FLOW_MOD: ADD table:255 mpls,mpls_ttl=1 actions=set_mpls_ttl(0) NXT_FLOW_MOD: ADD table:255 mpls,mpls_ttl=1 actions=set_mpls_ttl(200) NXT_FLOW_MOD: ADD table:255 mpls,mpls_ttl=1 actions=set_mpls_ttl(255) ]]) AT_CLEANUP AT_SETUP([ovs-ofctl -F nxm parse-flows]) AT_DATA([flows.txt], [ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop ipv6,ipv6_label=0x12345 actions=2 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=3 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5/64 actions=4 ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:5/127 actions=5 tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571 actions=drop icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4F:0571/112 actions=drop icmp6,icmp_type=135,nd_sll=00:0A:E4:25:6B:B0 actions=drop icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571,nd_tll=00:0A:E4:25:6B:B1 actions=drop icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE00:0000/96,nd_tll=00:0A:E4:25:6B:B1 actions=drop cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tun_id=0x1234,cookie=0x5678,actions=flood actions=drop tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=drop dl_dst=00:00:00:00:00:00/01:00:00:00:00:00,actions=drop dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff,actions=drop dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[[]]))) ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[[]]))) reg31[[0..31]]=1,actions=output:NXM_NX_REG15,output:NXOXM_ET_REG16,output:NXOXM_ET_REG31 xreg15[[0..31]]=2,xreg15[[32..63]]=3,actions=output:OXM_OF_PKT_REG7,output:OXM_OF_PKT_REG8,output:OXM_OF_PKT_REG15 xxreg7[[0..31]]=4,xxreg7[[32..63]]=5,xxreg7[[64..95]]=6,xxreg7[[96..127]]=7,actions=output:NXM_NX_XXREG3,output:NXOXM_ET_XXREG4,output:NXOXM_ET_XXREG7 ]) AT_CHECK([ovs-ofctl -F nxm parse-flows flows.txt], [0], [stdout]) # The substitution for fec0:0: is because some libcs (e.g. MUSL) # abbreviate a single zero and others (e.g. glibc) don't. AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)// s/fec0:0:/fec0::/g' stdout]], [0], [dnl usable protocols: NXM,OXM chosen protocol: NXM-table_id NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD NXT_FLOW_MOD: ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop NXT_FLOW_MOD: ADD arp,dl_src=00:0a:e4:25:6b:b0,arp_sha=00:0a:e4:25:6b:b0 actions=drop NXT_FLOW_MOD: ADD ipv6,ipv6_label=0x12345 actions=output:2 NXT_FLOW_MOD: ADD ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output:3 NXT_FLOW_MOD: ADD ipv6,ipv6_src=2001:db8:3c4d:1::/64 actions=output:4 NXT_FLOW_MOD: ADD ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:4/127 actions=output:5 NXT_FLOW_MOD: ADD tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop NXT_FLOW_MOD: ADD udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop NXT_FLOW_MOD: ADD icmp6,in_port=3,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 NXT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 NXT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_target=fec0::1234:f045:8fff:1111:fe4e:571 actions=drop NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_target=fec0::1234:f045:8fff:1111:fe4f:0/112 actions=drop NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_sll=00:0a:e4:25:6b:b0 actions=drop NXT_FLOW_MOD: ADD icmp6,icmp_type=136,nd_target=fec0::1234:f045:8fff:1111:fe4e:571,nd_tll=00:0a:e4:25:6b:b1 actions=drop NXT_FLOW_MOD: ADD icmp6,icmp_type=136,nd_target=fec0::1234:f045:8fff:1111::/96,nd_tll=00:0a:e4:25:6b:b1 actions=drop NXT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 NXT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD actions=drop NXT_FLOW_MOD: ADD tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop NXT_FLOW_MOD: ADD dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop NXT_FLOW_MOD: ADD dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=drop NXT_FLOW_MOD: ADD dl_dst=aa:bb:cc:dd:ee:ff/fe:ff:ff:ff:ff:ff actions=drop NXT_FLOW_MOD: ADD actions=drop NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) NXT_FLOW_MOD: ADD actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[[]],obs_point_id=NXM_NX_CT_LABEL[[32..63]],sampling_port=56789,egress) NXT_FLOW_MOD: ADD ip actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]])) NXT_FLOW_MOD: ADD ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[[0..63]],load:0->NXM_NX_CT_LABEL[[64..127]])) NXT_FLOW_MOD: ADD reg31=0x1 actions=output:NXM_NX_REG15[[]],output:NXOXM_ET_REG16[[]],output:NXOXM_ET_REG31[[]] NXT_FLOW_MOD: ADD reg30=0x3,reg31=0x2 actions=output:OXM_OF_PKT_REG7[[]],output:OXM_OF_PKT_REG8[[]],output:OXM_OF_PKT_REG15[[]] NXT_FLOW_MOD: ADD reg28=0x7,reg29=0x6,reg30=0x5,reg31=0x4 actions=output:NXM_NX_XXREG3[[1..64]],output:NXOXM_ET_XXREG4[[1..64]],output:NXOXM_ET_XXREG7[[1..64]] ]) AT_CLEANUP AT_SETUP([ovs-ofctl -F nxm -mmm parse-flows]) AT_DATA([flows.txt], [[ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop ipv6,ipv6_label=0x12345 actions=2 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=3 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5/64 actions=4 ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:5/127 actions=5 tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop sctp6,ipv6_src=2001:db8:3c4d:1::5,tp_dst=309 actions=drop in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1 sctp,nw_src=192.168.0.3,tp_dst=309 actions=pop_queue,output:1 icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571 actions=drop icmp6,icmp_type=135,nd_sll=00:0A:E4:25:6B:B0 actions=drop icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571,nd_tll=00:0A:E4:25:6B:B1 actions=drop cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tun_id=0x1234,cookie=0x5678,actions=flood actions=drop reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[] actions=push:reg0[0..31],pop:NXM_NX_REG0[] reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci actions=move:eth_dst->eth_src[] actions=push:reg0[0..31],pop:reg0 vlan_tci=0x1123/0x1fff,actions=drop actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_LABEL[]))) ip,actions=ct(commit,exec(set_field(1->ct_label))) reg31[0..31]=1,actions=output:NXM_NX_REG15,output:NXOXM_ET_REG16,output:NXOXM_ET_REG31 xreg15[0..31]=2,xreg15[32..63]=3,actions=output:OXM_OF_PKT_REG7,output:OXM_OF_PKT_REG8,output:OXM_OF_PKT_REG15 xxreg7[0..31]=4,xxreg7[32..63]=5,xxreg7[64..95]=6,xxreg7[96..127]=7,actions=output:NXM_NX_XXREG3,output:NXOXM_ET_XXREG4,output:NXOXM_ET_XXREG7 ]]) AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr]) AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [[usable protocols: NXM,OXM chosen protocol: NXM-table_id NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(000ae4256bb0) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_LABEL(00012345) actions=output:2 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005) actions=output:3 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) actions=output:4 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010002000300040004/fffffffffffffffffffffffffffffffe) actions=output:5 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000005), NXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(0135) actions=drop NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(0003), NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(86) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(0135) actions=pop_queue,output:1 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_SLL(000ae4256bb0) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571), NXM_NX_ND_TLL(000ae4256bb1) actions=drop NXT_FLOW_MOD: ADD cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535 NXT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD actions=drop NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] NXT_FLOW_MOD: ADD actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[] NXT_FLOW_MOD: ADD actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] NXT_FLOW_MOD: ADD actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[] NXT_FLOW_MOD: ADD actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) NXT_FLOW_MOD: ADD actions=sample(probability=12341,collector_set_id=23456,obs_domain_id=NXM_OF_IN_PORT[],obs_point_id=NXM_NX_CT_LABEL[32..63],sampling_port=56789,egress) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800) actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD NXOXM_ET_REG31(00000001) actions=output:NXM_NX_REG15[],output:NXOXM_ET_REG16[],output:NXOXM_ET_REG31[] NXT_FLOW_MOD: ADD NXOXM_ET_REG30(00000003), NXOXM_ET_REG31(00000002) actions=output:OXM_OF_PKT_REG7[],output:OXM_OF_PKT_REG8[],output:OXM_OF_PKT_REG15[] NXT_FLOW_MOD: ADD NXOXM_ET_REG28(00000007), NXOXM_ET_REG29(00000006), NXOXM_ET_REG30(00000005), NXOXM_ET_REG31(00000004) actions=output:NXM_NX_XXREG3[1..64],output:NXOXM_ET_XXREG4[1..64],output:NXOXM_ET_XXREG7[1..64] ]]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-nx-match]) AT_KEYWORDS([nx-match]) AT_DATA([nx-match.txt], [dnl # in port NXM_OF_IN_PORT(0000) NXM_OF_IN_PORT(fffe) # eth dst NXM_OF_ETH_DST(0002e30f80a4) NXM_OF_ETH_DST_W(010000000000/010000000000) NXM_OF_ETH_DST_W(000000000000/010000000000) NXM_OF_ETH_DST_W(ffffffffffff/010000000000) NXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff) NXM_OF_ETH_DST_W(60175619848f/000000000000) NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) NXM_OF_ETH_DST_W(60175619848f/5a5a5a5a5a5a) # eth src NXM_OF_ETH_SRC(020898456ddb) NXM_OF_ETH_SRC_W(012345abcdef/ffffff555555) NXM_OF_ETH_SRC_W(020898456ddb/ffffffffffff) NXM_OF_ETH_SRC_W(020898456ddb/000000000000) # eth type NXM_OF_ETH_TYPE(0800) NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0012) # vlan tci NXM_OF_VLAN_TCI(f009) NXM_OF_VLAN_TCI(f009) NXM_OF_VLAN_TCI(f009) NXM_OF_VLAN_TCI(0000) # Packets without 802.1Q header. NXM_OF_VLAN_TCI(3123) # Packets with VID=123, PCP=1. NXM_OF_VLAN_TCI(0123) # Does not make sense (but supported anyway) NXM_OF_VLAN_TCI_W(1123/1fff) # Packets with VID=123, any PCP. NXM_OF_VLAN_TCI_W(1123/ffff) # Packets with VID=123, PCP=0 NXM_OF_VLAN_TCI_W(1123/0000) # Packets with or without 802.1Q header NXM_OF_VLAN_TCI_W(f000/f000) # Packets with any VID, PCP=7. NXM_OF_VLAN_TCI_W(0000/e000) # No 802.1Q or with VID=0 # IP TOS NXM_OF_ETH_TYPE(0800) NXM_OF_IP_TOS(f0) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_TOS(41) NXM_OF_IP_TOS(f0) # IP ECN NXM_OF_ETH_TYPE(0800) NXM_NX_IP_ECN(03) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_ECN(06) NXM_NX_IP_ECN(03) # IP protocol NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(05) NXM_OF_IP_PROTO(05) # IP TTL NXM_OF_ETH_TYPE(0800) NXM_NX_IP_TTL(80) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_TTL(ff) NXM_NX_IP_TTL(80) # IP source NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/5a5a5a5a) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/ffffffff) NXM_OF_ETH_TYPE(0806) NXM_OF_IP_SRC(ac100014) NXM_OF_IP_SRC_W(C0D80000/FFFF0000) # IP destination NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST(ac100014) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a88012/FFFF0000) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/5a5a5a5a) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/ffffffff) NXM_OF_IP_DST(ac100014) NXM_OF_ETH_TYPE(0806) NXM_OF_IP_DST_W(C0D80000/FFFF0000) # TCP source port NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC(4231) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/F0F0) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/ffff) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_SRC(4231) # TCP destination port NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST(4231) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/FFF0) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/ffff) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231) # TCP flags NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS(0131) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00F0/0FF0) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(01E2/ffff) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_NX_TCP_FLAGS(0fff) # UDP source port NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/ffff) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_UDP_SRC(7823) # UDP destination port NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST(1782) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/F00F) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/FFFF) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_OF_UDP_DST(1293) # ICMP type NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ICMP_TYPE(12) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_OF_ICMP_TYPE(10) # ICMP code NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ICMP_CODE(12) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_OF_ICMP_CODE(10) NXM_OF_ETH_TYPE(0800) NXM_OF_ICMP_CODE(10) NXM_OF_ICMP_CODE(00) # ARP opcode NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(1111) NXM_OF_ETH_TYPE(0000) NXM_OF_ARP_OP(0001) NXM_OF_ARP_OP(0001) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) NXM_OF_ARP_OP(0001) # ARP source protocol address NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA(ac100014) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014) NXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # ARP destination protocol address NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/77777777) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014) NXM_OF_ARP_TPA_W(C0D80000/FFFF0000) # ARP source hardware address NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_SHA(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_SHA(0002e30f80a4) NXM_NX_ARP_SHA(0002e30f80a4) # ARP destination hardware address NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_THA(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4) NXM_NX_ARP_THA(0002e30f80a4) # RARP opcode NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(0003) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(1111) NXM_OF_ETH_TYPE(0000) NXM_OF_ARP_OP(0003) NXM_OF_ARP_OP(0003) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_OP(0003) NXM_OF_ARP_OP(0003) # RARP source protocol address NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA(ac100014) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81200/FFFFFF00) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014) NXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # RARP destination protocol address NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA(ac100014) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81200/FFFFFF00) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81234/77777777) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014) NXM_OF_ARP_TPA_W(C0D80000/FFFF0000) # RARP source hardware address NXM_OF_ETH_TYPE(8035) NXM_NX_ARP_SHA(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_SHA(0002e30f80a4) NXM_NX_ARP_SHA(0002e30f80a4) # RARP destination hardware address NXM_OF_ETH_TYPE(8035) NXM_NX_ARP_THA(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4) NXM_NX_ARP_THA(0002e30f80a4) # IPv6 source NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/5a5a5a5a5a5a5a5a0000000000000000) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000) NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffff000000000000) # IPv6 destination NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/77777777777777777777777777777777) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(00000000000000000000000000000000/00000000000000000000000000000000) NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) # IPv6 Flow Label NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(1000000f) NXM_NX_IPV6_LABEL(0000000f) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(0000000f) # ND target address NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/0123456789abcdeffedcba9876543210) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/ffffffffffffffffffffffffffffffff) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(00000000000000000000000000000000/00000000000000000000000000000000) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/fedcba98765432100123456789abcdef) # ND source hardware address NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3b) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4) # ND destination hardware address NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4) NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3b) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4) # IPv4 fragments. NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(01) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(02) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(03) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(00/03) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(00/fd) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(00/02) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(01/01) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(02/02) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/03) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/ff) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/00) NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(f3) # IPv6 fragments. NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(01) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(02) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(03) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(00/03) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(00/01) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(00/02) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(01/01) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(02/02) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/03) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/00) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/ff) NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(f3) # Flow cookie. NXM_NX_COOKIE(00000000abcdef01) NXM_NX_COOKIE_W(84200000abcdef01/84200000FFFFFFFF) NXM_NX_COOKIE_W(84200000abcdef01/ffffffffffffffff) NXM_NX_COOKIE_W(0000000000000000/0000000000000000) # Tunnel ID. NXM_NX_TUN_ID(00000000abcdef01) NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF) NXM_NX_TUN_ID_W(84200000abcdef01/FFFFFFFFFFFFFFFF) NXM_NX_TUN_ID_W(0000000000000000/0000000000000000) # Register 0. NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/ffffffff) NXM_NX_REG0_W(00000000/00000000) # Register 15. NXM_NX_REG15(acebdf56) NXM_NX_REG15_W(a0e0d050/f0f0f0f0) NXM_NX_REG15_W(a0e0d050/ffffffff) NXM_NX_REG15_W(00000000/00000000) # Register 16. NXOXM_ET_REG16(acebdf56) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16_W(a0e0d050/ffffffff) NXOXM_ET_REG16_W(00000000/00000000) # Register 31. NXOXM_ET_REG31(acebdf56) NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG31_W(a0e0d050/ffffffff) NXOXM_ET_REG31_W(00000000/00000000) # xreg 0. OXM_OF_PKT_REG0(acebdf56acebdf56) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/ffffffffffffffff) OXM_OF_PKT_REG0_W(0000000000000000/0000000000000000) # xreg 7. OXM_OF_PKT_REG7(acebdf56acebdf56) OXM_OF_PKT_REG7_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG7_W(a0e0d050a0e0d050/ffffffffffffffff) OXM_OF_PKT_REG7_W(0000000000000000/0000000000000000) # xreg 8. OXM_OF_PKT_REG8(acebdf56acebdf56) OXM_OF_PKT_REG8_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG8_W(a0e0d050a0e0d050/ffffffffffffffff) OXM_OF_PKT_REG8_W(0000000000000000/0000000000000000) # xreg 15. OXM_OF_PKT_REG15(acebdf56acebdf56) OXM_OF_PKT_REG15_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG15_W(a0e0d050a0e0d050/ffffffffffffffff) OXM_OF_PKT_REG15_W(0000000000000000/0000000000000000) # xxreg 0. NXM_NX_XXREG0(acebdf56acebdf56acebdf56acebdf56) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/ffffffffffffffffffffffffffffffff) NXM_NX_XXREG0_W(00000000000000000000000000000000/00000000000000000000000000000000) # xxreg 3. NXM_NX_XXREG3(acebdf56acebdf56acebdf56acebdf56) NXM_NX_XXREG3_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXM_NX_XXREG3_W(a0e0d050a0e0d050a0e0d050a0e0d050/ffffffffffffffffffffffffffffffff) NXM_NX_XXREG3_W(00000000000000000000000000000000/00000000000000000000000000000000) # xxreg 4. NXOXM_ET_XXREG4(acebdf56acebdf56acebdf56acebdf56) NXOXM_ET_XXREG4_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXOXM_ET_XXREG4_W(a0e0d050a0e0d050a0e0d050a0e0d050/ffffffffffffffffffffffffffffffff) NXOXM_ET_XXREG4_W(00000000000000000000000000000000/00000000000000000000000000000000) # xxreg 7. NXOXM_ET_XXREG7(acebdf56acebdf56acebdf56acebdf56) NXOXM_ET_XXREG7_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXOXM_ET_XXREG7_W(a0e0d050a0e0d050a0e0d050a0e0d050/ffffffffffffffffffffffffffffffff) NXOXM_ET_XXREG7_W(00000000000000000000000000000000/00000000000000000000000000000000) # Connection tracking fields, NXM_OF_ETH_TYPE(0800) NXM_NX_CT_STATE(00000020) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_STATE(00001080) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_STATE_W(00000020/00000020) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_STATE_W(00000020/000000F0) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_ZONE(5a5a) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_MARK(5a5a5a5a) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_MARK_W(5a5a5a5a/fefefefe) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_LABEL(1234567890abcdef1234567890abcdef) NXM_OF_ETH_TYPE(0800) NXM_NX_CT_LABEL_W(10203040506070809000a0b0c0d0e0f0/f1f2f3f4f5f6f7f8f9f0fafbfcfdfeff) # dp_hash (testing experimenter OXM). NXM_NX_DP_HASH(01234567) NXOXM_ET_DP_HASH(01234567) # ERSPAN (testing experimenter OXM). NXOXM_ET_ERSPAN_VER(01) NXOXM_ET_ERSPAN_IDX(01020304) NXOXM_ET_ERSPAN_DIR(01) NXOXM_ET_ERSPAN_HWID(12) # Invalid field number. 01020304(1111/3333) # Invalid field numbers (experimenter OXM). ffff020800002320(11112222) ffff030800002320(1111/3333) ]) AT_CHECK([ovs-ofctl -vPATTERN:'console:%c|%p|%m' --strict parse-nx-match < nx-match.txt], [0], [dnl # in port NXM_OF_IN_PORT(0000) NXM_OF_IN_PORT(fffe) # eth dst NXM_OF_ETH_DST(0002e30f80a4) NXM_OF_ETH_DST_W(010000000000/010000000000) NXM_OF_ETH_DST_W(000000000000/010000000000) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_DST(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS # eth src NXM_OF_ETH_SRC(020898456ddb) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_SRC(020898456ddb) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS # eth type NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0012), NXM_OF_ETH_TYPE(0800) # vlan tci NXM_OF_VLAN_TCI(f009) nx_pull_match() returned error OFPBMC_DUP_FIELD NXM_OF_VLAN_TCI(0000) NXM_OF_VLAN_TCI(3123) NXM_OF_VLAN_TCI(0123) NXM_OF_VLAN_TCI_W(1123/1fff) NXM_OF_VLAN_TCI(1123) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_VLAN_TCI_W(f000/f000) NXM_OF_VLAN_TCI_W(0000/e000) # IP TOS NXM_OF_ETH_TYPE(0800), NXM_OF_IP_TOS(f0) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP ECN NXM_OF_ETH_TYPE(0800), NXM_NX_IP_ECN(03) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP protocol NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(05) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP TTL NXM_OF_ETH_TYPE(0800), NXM_NX_IP_TTL(80) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_TTL(ff) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP source NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80000) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP destination NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(ac100014) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(c0a80000) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # TCP source port NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC_W(5050/f0f0) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(5050) nx_pull_match() returned error OFPBMC_BAD_PREREQ # TCP destination port NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST_W(fde0/fff0) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(fde0) nx_pull_match() returned error OFPBMC_BAD_PREREQ # TCP flags NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(0131) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS_W(00f0/0ff0) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(01e2) nx_pull_match() returned error OFPBMC_BAD_PREREQ # UDP source port NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(0132) nx_pull_match() returned error OFPBMC_BAD_PREREQ # UDP destination port NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST_W(5005/f00f) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(5005) nx_pull_match() returned error OFPBMC_BAD_PREREQ # ICMP type NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_TYPE(12) nx_pull_match() returned error OFPBMC_BAD_PREREQ # ICMP code NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_CODE(12) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP opcode NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_OP(0001) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_DUP_FIELD # ARP source protocol address NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a81234) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP destination protocol address NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(c0a81234) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP source hardware address NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP destination hardware address NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # RARP opcode NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_OP(0003) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_DUP_FIELD # RARP source protocol address NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA(ac100014) NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA_W(c0a81200/ffffff00) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_SPA(c0a81234) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # RARP destination protocol address NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA(ac100014) NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA_W(c0a81200/ffffff00) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(8035), NXM_OF_ARP_TPA(c0a81234) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # RARP source hardware address NXM_OF_ETH_TYPE(8035), NXM_NX_ARP_SHA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # RARP destination hardware address NXM_OF_ETH_TYPE(8035), NXM_NX_ARP_THA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv6 source NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005) nx_pull_match() returned error OFPBMC_BAD_PREREQ NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000000) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv6 destination NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010000000000000000) NXM_OF_ETH_TYPE(86dd) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv6 Flow Label nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_LABEL(0000000f) # ND target address NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS # ND source hardware address NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_SLL(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ND destination hardware address NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_TLL(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv4 fragments. NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(01) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(02) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(00/01) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(00/02) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(01/01) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(02/02) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03) NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS nx_pull_match() returned error OFPBMC_BAD_VALUE # IPv6 fragments. NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(01) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(02) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(00/01) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(00/02) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(01/01) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(02/02) NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03) nx_pull_match() returned error OFPBMC_BAD_VALUE # Flow cookie. NXM_NX_COOKIE(00000000abcdef01) NXM_NX_COOKIE_W(84200000abcdef01/84200000ffffffff) NXM_NX_COOKIE(84200000abcdef01) # Tunnel ID. NXM_NX_TUN_ID(00000000abcdef01) NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff) NXM_NX_TUN_ID(84200000abcdef01) # Register 0. NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050) # Register 15. NXM_NX_REG15(acebdf56) NXM_NX_REG15_W(a0e0d050/f0f0f0f0) NXM_NX_REG15(a0e0d050) # Register 16. NXOXM_ET_REG16(acebdf56) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16(a0e0d050) # Register 31. NXOXM_ET_REG31(acebdf56) NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG31(a0e0d050) # xreg 0. NXM_NX_REG0(acebdf56), NXM_NX_REG1(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050), NXM_NX_REG1(a0e0d050) # xreg 7. NXM_NX_REG14(acebdf56), NXM_NX_REG15(acebdf56) NXM_NX_REG14_W(a0e0d050/f0f0f0f0), NXM_NX_REG15_W(a0e0d050/f0f0f0f0) NXM_NX_REG14(a0e0d050), NXM_NX_REG15(a0e0d050) # xreg 8. NXOXM_ET_REG16(acebdf56), NXOXM_ET_REG17(acebdf56) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG17_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16(a0e0d050), NXOXM_ET_REG17(a0e0d050) # xreg 15. NXOXM_ET_REG30(acebdf56), NXOXM_ET_REG31(acebdf56) NXOXM_ET_REG30_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG30(a0e0d050), NXOXM_ET_REG31(a0e0d050) # xxreg 0. NXM_NX_REG0(acebdf56), NXM_NX_REG1(acebdf56), NXM_NX_REG2(acebdf56), NXM_NX_REG3(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0), NXM_NX_REG3_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050), NXM_NX_REG1(a0e0d050), NXM_NX_REG2(a0e0d050), NXM_NX_REG3(a0e0d050) # xxreg 3. NXM_NX_REG12(acebdf56), NXM_NX_REG13(acebdf56), NXM_NX_REG14(acebdf56), NXM_NX_REG15(acebdf56) NXM_NX_REG12_W(a0e0d050/f0f0f0f0), NXM_NX_REG13_W(a0e0d050/f0f0f0f0), NXM_NX_REG14_W(a0e0d050/f0f0f0f0), NXM_NX_REG15_W(a0e0d050/f0f0f0f0) NXM_NX_REG12(a0e0d050), NXM_NX_REG13(a0e0d050), NXM_NX_REG14(a0e0d050), NXM_NX_REG15(a0e0d050) # xxreg 4. NXOXM_ET_REG16(acebdf56), NXOXM_ET_REG17(acebdf56), NXOXM_ET_REG18(acebdf56), NXOXM_ET_REG19(acebdf56) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG17_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG18_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG19_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16(a0e0d050), NXOXM_ET_REG17(a0e0d050), NXOXM_ET_REG18(a0e0d050), NXOXM_ET_REG19(a0e0d050) # xxreg 7. NXOXM_ET_REG28(acebdf56), NXOXM_ET_REG29(acebdf56), NXOXM_ET_REG30(acebdf56), NXOXM_ET_REG31(acebdf56) NXOXM_ET_REG28_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG29_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG30_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG28(a0e0d050), NXOXM_ET_REG29(a0e0d050), NXOXM_ET_REG30(a0e0d050), NXOXM_ET_REG31(a0e0d050) # Connection tracking fields, dnl dnl When re-serialising, bits 8-31 are wildcarded, because current OVS userspace dnl doesn't understand (or store) those bits. NXM_OF_ETH_TYPE(0800), NXM_NX_CT_STATE_W(00000020/000000ff) nx_pull_match() returned error OFPBMC_BAD_VALUE NXM_OF_ETH_TYPE(0800), NXM_NX_CT_STATE_W(00000020/00000020) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_STATE_W(00000020/000000f0) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_ZONE(5a5a) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_MARK(5a5a5a5a) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_MARK_W(5a5a5a5a/fefefefe) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_LABEL(1234567890abcdef1234567890abcdef) NXM_OF_ETH_TYPE(0800), NXM_NX_CT_LABEL_W(10203040506070809000a0b0c0d0e0f0/f1f2f3f4f5f6f7f8f9f0fafbfcfdfeff) # dp_hash (testing experimenter OXM). NXM_NX_DP_HASH(01234567) NXM_NX_DP_HASH(01234567) # ERSPAN (testing experimenter OXM). NXOXM_ET_ERSPAN_VER_W(01/01) NXOXM_ET_ERSPAN_IDX(01020304) NXOXM_ET_ERSPAN_DIR_W(01/01) NXOXM_ET_ERSPAN_HWID_W(12/12) # Invalid field number. nx_pull_match() returned error OFPBMC_BAD_FIELD # Invalid field numbers (experimenter OXM). nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD ], [stderr]) # Check that at least the first warning made it. (It's rate-limited # so a variable number could show up, especially under valgrind etc.) AT_CHECK([grep '1-bits in value' stderr | sed 1q], [0], [dnl nx_match|WARN|Rejecting NXM/OXM entry 0:0:1:1:12 with 1-bits in value for bits wildcarded by the mask. ]) # Check that there wasn't any other stderr output. AT_CHECK([grep -v '1-bits in value' stderr], [1]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-ofp10-match]) AT_KEYWORDS([OF1.0]) AT_DATA([test-data], [dnl # in_port=LOCAL 003820fe fffe xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_src=00:01:02:03:04:05 003820fb xxxx 000102030405 xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_dst=10:20:30:40:50:60 003820f7 xxxx xxxxxxxxxxxx 102030405060 xxxx xx xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_vlan=291 003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 xx xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_vlan_pcp=5 002820ff xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx 05 xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_vlan=291,dl_vlan_pcp=4 002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 04 xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl dl_vlan_pcp doesn't make sense when 802.1Q is not present, so dnl OVS ignores it and drops it on output. # vlan_tci=0x0000 # 1: 38 -> 28 003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff xx xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl dl_vlan_pcp doesn't make sense when 802.1Q is not present, so dnl OVS ignores it and drops it on output. # vlan_tci=0x0000 # 20: 05 -> 00 002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff 05 xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl Invalid VID and PCP discards out-of-range bits: # dl_vlan=256,dl_vlan_pcp=7 # 18: f1 -> 01 # 20: ff -> 07 002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx f100 ff xx xxxx xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # dl_type=0x1234 003820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # ip,nw_proto=5 003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 05 xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl Ignore nw_proto if not IP or ARP: # dl_type=0x1234,nw_proto=5 # normal: 3: cf -> ef # normal: 25: 05 -> 00 & ofp_match|INFO|normalization changed ofp_match, details: & ofp_match|INFO| pre: dl_type=0x1234,nw_proto=5 & ofp_match|INFO|post: dl_type=0x1234 003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx 05 xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # ip,nw_tos=252 001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 fc xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl Ignore nw_tos if not IP: # arp,nw_tos=4 # 24: 05 -> 04 # normal: 1: 18 -> 38 # normal: 24: 04 -> 00 & ofp_match|INFO|normalization changed ofp_match, details: & ofp_match|INFO| pre: arp,nw_tos=4 & ofp_match|INFO|post: arp 001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 05 xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx dnl Low 2 bits of invalid TOS are forced to 0: # ip,nw_tos=48 # 24: 31 -> 30 001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 31 xx xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # arp,arp_op=2 003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx 02 xxxx dnl xxxxxxxx xxxxxxxx xxxx xxxx # ip,nw_src=192.168.128.85 003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl c0a88055 xxxxxxxx xxxx xxxx # ip,nw_src=192.168.128.0/24 # 31: 55 -> 00 003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl c0a88055 xxxxxxxx xxxx xxxx # ip,nw_dst=192.168.128.85 003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl xxxxxxxx c0a88055 xxxx xxxx # ip,nw_dst=192.168.128.0/24 # 35: 55 -> 00 003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl xxxxxxxx c0a88055 xxxx xxxx # arp,arp_spa=192.168.128.85 003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl c0a88055 xxxxxxxx xxxx xxxx # arp,arp_spa=192.168.128.0/24 # 31: 55 -> 00 003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl c0a88055 xxxxxxxx xxxx xxxx # arp,arp_tpa=192.168.128.85 003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl xxxxxxxx c0a88055 xxxx xxxx # arp,arp_tpa=192.168.128.0/24 # 35: 55 -> 00 003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl xxxxxxxx c0a88055 xxxx xxxx dnl Ignore nw_src if not IP or ARP: # dl_type=0x1234,nw_src=192.168.128.0/24 # 31: 55 -> 00 # normal: 2: 08 -> 20 # normal: 28: c0 -> 00 # normal: 29: a8 -> 00 # normal: 30: 80 -> 00 & ofp_match|INFO|normalization changed ofp_match, details: & ofp_match|INFO| pre: dl_type=0x1234,nw_src=192.168.128.0/24 & ofp_match|INFO|post: dl_type=0x1234 003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl c0a88055 xxxxxxxx xxxx xxxx dnl Ignore nw_dst if not IP or ARP: # dl_type=0x1234,nw_dst=192.168.128.0/24 # 35: 55 -> 00 # normal: 1: 32 -> 38 # normal: 32: c0 -> 00 # normal: 33: a8 -> 00 # normal: 34: 80 -> 00 & ofp_match|INFO|normalization changed ofp_match, details: & ofp_match|INFO| pre: dl_type=0x1234,nw_dst=192.168.128.0/24 & ofp_match|INFO|post: dl_type=0x1234 003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl xxxxxxxx c0a88055 xxxx xxxx # tcp,tp_src=443 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl xxxxxxxx xxxxxxxx 01bb xxxx # tcp,tp_dst=443 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl xxxxxxxx xxxxxxxx xxxx 01bb # udp,tp_src=443 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl xxxxxxxx xxxxxxxx 01bb xxxx # udp,tp_dst=443 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl xxxxxxxx xxxxxxxx xxxx 01bb # sctp,tp_src=443 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 84 xxxx dnl xxxxxxxx xxxxxxxx 01bb xxxx # sctp,tp_dst=443 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 84 xxxx dnl xxxxxxxx xxxxxxxx xxxx 01bb # icmp,icmp_type=5 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl xxxxxxxx xxxxxxxx 0005 xxxx # icmp,icmp_code=8 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl xxxxxxxx xxxxxxxx xxxx 0008 dnl Ignore tp_src if not TCP/UDP/SCTP: # ip,nw_proto=21,tp_src=443 # normal: 3: 8f -> cf # normal: 36: 01 -> 00 # normal: 37: bb -> 00 & ofp_match|INFO|normalization changed ofp_match, details: & ofp_match|INFO| pre: ip,nw_proto=21,tp_src=443 & ofp_match|INFO|post: ip,nw_proto=21 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl xxxxxxxx xxxxxxxx 01bb xxxx dnl Ignore tp_dst if not TCP/UDP/SCTP: # ip,nw_proto=21,tp_dst=443 # normal: 3: 4f -> cf # normal: 38: 01 -> 00 # normal: 39: bb -> 00 dnl The normalization details are suppressed here due to rate-limiting. 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl xxxxxxxx xxxxxxxx xxxx 01bb ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-match < input.txt], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-ofp11-match]) AT_KEYWORDS([OF1.1]) AT_DATA([test-data], [dnl # in_port=LOCAL 0000 0058 fffffffe 000003fe dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # bad ofp11_match: OFPBMC_BAD_VALUE & ofp_port|WARN|port 305419896 is outside the supported range 0 through 65279 or 0xffffff00 through 0xffffffff 0000 0058 12345678 000003fe dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_src=00:01:02:03:04:05 0000 0058 00000000 000003ff dnl 000102030405000000000000 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_src=55:55:55:55:55:55/55:55:55:55:55:55 0000 0058 00000000 000003ff dnl 555555555555aaaaaaaaaaaa 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_dst=00:01:02:03:04:05 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 000102030405000000000000 dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 010000000000feffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_dst=00:01:02:03:04:05/fe:ff:ff:ff:ff:ff 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 000102030405010000000000 dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_dst=55:55:55:55:55:55/55:55:55:55:55:55 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 555555555555aaaaaaaaaaaa dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl dl_vlan_pcp is ignored if dl_vlan is wildcarded, which causes the dnl the wildcard bit and the dl_vlan_pcp to be dropped for output: # in_port=1 # 11: fa -> fe # 38: 03 -> 00 0000 0058 00000001 000003fa dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 03 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_vlan=291 0000 0058 00000000 000003fd dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0123 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl OFPVID_NONE: # vlan_tci=0x0000 0000 0058 00000000 000003fd dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl ffff 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl OFPVID_NONE ignores dl_vlan_pcp even if not wildcarded, which causes dnl the wildcard bit and the dl_vlan_pcp to be dropped for output: # vlan_tci=0x0000 # 11: f9 -> fd # 38: 05 -> 00 0000 0058 00000000 000003f9 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl ffff 05 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # vlan_tci=0x1000/0x1000 0000 0058 00000000 000003fd dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl fffe 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl Try invalid VID: # bad ofp11_match: OFPBMC_BAD_VALUE 0000 0058 00000000 000003fd dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 1234 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_vlan_pcp=4 0000 0058 00000000 000003f9 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl fffe 04 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_vlan=10,dl_vlan_pcp=6 0000 0058 00000000 000003f9 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 000a 06 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # dl_type=0x1234 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_tos=252 0000 0058 00000000 000003e7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 fc 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl Try invalid TOS: # bad ofp11_match: OFPBMC_BAD_VALUE 0000 0058 00000000 000003e7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 01 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_proto=5 0000 0058 00000000 000003d7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 05 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # arp,arp_op=2 0000 0058 00000000 000003d7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0806 00 02 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_src=192.168.128.0/24 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_src=128.160.128.0/165.165.165.165 # 44: c0 -> 80 # 45: a8 -> a0 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 00 c0a880005a5a5a5a 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_dst=192.168.128.0/24 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # ip,nw_dst=128.160.128.0/165.165.165.165 # 52: c0 -> 80 # 53: a8 -> a0 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 00 00000000ffffffff c0a880005a5a5a5a 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # arp,arp_spa=192.168.128.0/24 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0806 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # arp,arp_tpa=192.168.128.0/24 0000 0058 00000000 000003f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0806 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # tcp,tp_src=443 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 01bb 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # tcp,tp_dst=443 0000 0058 00000000 00000357 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 0000 01bb dnl 00000000 00 000000 0000000000000000ffffffffffffffff # udp,tp_src=443 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # icmp,icmp_type=5 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0005 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # icmp,icmp_code=8 0000 0058 00000000 00000357 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0000 0008 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # udp,tp_src=443 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # udp,tp_dst=443 0000 0058 00000000 00000357 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 0000 01bb dnl 00000000 00 000000 0000000000000000ffffffffffffffff # sctp 0000 0058 00000000 000003d7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # sctp,tp_src=443 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 01bb 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff # sctp,tp_dst=443 0000 0058 00000000 00000357 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 01bb dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl Ignore tp_src if not TCP/UDP/SCTP: # ip,nw_proto=21 # 11: 97 -> d7 # 60: 01 -> 00 # 61: bb -> 00 0000 0058 00000000 00000397 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 15 00000000ffffffff 00000000ffffffff 01bb 0000 dnl 00000000 00 000000 0000000000000000ffffffffffffffff dnl Ignore tp_dst if not TCP/UDP/SCTP: # ip,nw_proto=22 # 11: 57 -> d7 # 62: 01 -> 00 # 63: bb -> 00 0000 0058 00000000 00000357 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0800 00 16 00000000ffffffff 00000000ffffffff 0000 01bb dnl 00000000 00 000000 0000000000000000ffffffffffffffff # mpls,mpls_label=284280 # 64: 12 -> 00 # 65: 34 -> 04 0000 0058 00000000 000002f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 8847 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 12345678 00 000000 0000000000000000ffffffffffffffff # mplsm,mpls_tc=2 # 68: 5a -> 02 0000 0058 00000000 000001f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 8848 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 5a 000000 0000000000000000ffffffffffffffff dnl mpls_label and mpls_tc must be ignored if dl_type is not MPLS: # dl_type=0x1234 # 10: 00 -> 03 # 64: 12 -> 00 # 65: 34 -> 00 # 66: 56 -> 00 # 67: 78 -> 00 # 68: 5a -> 00 0000 0058 00000000 000000f7 dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 12345678 5a 000000 0000000000000000ffffffffffffffff dnl metadata match: # metadata=0x1234567890abcdef 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 1234567890abcdef0000000000000000 dnl metadata match: # metadata=0x5555555555555555/0x5555555555555555 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 5555555555555555aaaaaaaaaaaaaaaa dnl metadata match: # metadata=0x1234000090ab0000/0xffff0000ffff0000 # 74: 56 -> 00 # 75: 78 -> 00 # 78: cd -> 00 # 79: ef -> 00 0000 0058 00000000 000003ff dnl 000000000000ffffffffffff 000000000000ffffffffffff dnl 0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl 00000000 00 000000 1234567890abcdef0000ffff0000ffff ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^& //p' < test-data > experr AT_CAPTURE_FILE([input.txt]) AT_CAPTURE_FILE([expout]) AT_CAPTURE_FILE([experr]) AT_CHECK( [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-match < input.txt], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-nx-match loose]) AT_KEYWORDS([nx-match]) AT_DATA([nx-match.txt], [dnl NXM_OF_IN_PORT(0001), 01020304(1111/3333), NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0001), ffff020800002320(11112222), NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0001), ffff030800002320(1111/3333), NXM_OF_ETH_TYPE(0800) ]) AT_CHECK([ovs-ofctl --strict parse-nx-match < nx-match.txt], [0], [dnl nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD ]) AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800) ]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.2)]) AT_KEYWORDS([oxm]) AT_DATA([oxm.txt], [dnl # in port OXM_OF_IN_PORT(00000000) OXM_OF_IN_PORT(fffffffe) # metadata OXM_OF_METADATA(5a5a5a5a5a5a5a5a) OXM_OF_METADATA_W(0000000000000000/00000000ffffffff) OXM_OF_METADATA_W(1234567890abcdef/ffff0000ffff0000) OXM_OF_METADATA_W(1234567890abcdef/ffffffffffffffff) OXM_OF_METADATA_W(1234567890abcdef/0000000000000000) # eth dst OXM_OF_ETH_DST(0002e30f80a4) OXM_OF_ETH_DST_W(010000000000/010000000000) OXM_OF_ETH_DST_W(000000000000/010000000000) OXM_OF_ETH_DST_W(ffffffffffff/010000000000) OXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff) OXM_OF_ETH_DST_W(0002e30f80a4/000000000000) OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) # eth src OXM_OF_ETH_SRC(020898456ddb) # eth type OXM_OF_ETH_TYPE(0800) OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000012) # vlan OXM_OF_VLAN_VID(1009) OXM_OF_VLAN_VID(1009) # Duplicate Field OXM_OF_VLAN_VID(f009) # Bad Value OXM_OF_VLAN_PCP(00) # Bad Pre-Requisite OXM_OF_VLAN_VID(0000) # Packets without 802.1Q header or with VID=0 OXM_OF_VLAN_VID(1123) # Packets with VID=123, any PCP OXM_OF_VLAN_VID(1123) OXM_OF_VLAN_PCP(01) # Packets with VID=123, PCP=1. OXM_OF_VLAN_VID(0123) # Does not make sense (but supported anyway) OXM_OF_VLAN_VID_W(0123/0123) # Does not make sense (but supported anyway) OXM_OF_VLAN_VID_W(1123/0123) # Does not make sense (but supported anyway) OXM_OF_VLAN_VID_W(0123/1123) # Does not make sense (but supported anyway) OXM_OF_VLAN_VID(0123) OXM_OF_VLAN_PCP(01) #Bad Pre-Requisite OXM_OF_VLAN_VID_W(1123/1fff) # Packets with VID=123, any PCP. OXM_OF_VLAN_VID_W(1123/ffff) # Packets with VID=123, any PCP. OXM_OF_VLAN_VID_W(0000/0000) # Packets with or without 802.1Q header OXM_OF_VLAN_VID_W(1103/1f0f), # Packets with # VID=123 (masked) OXM_OF_VLAN_VID_W(1103/1f0f), OXM_OF_VLAN_PCP(01) # Packets with VID=123 (masked), any PCP. OXM_OF_VLAN_VID_W(1000/1000) # Packets with any VID, any PCP OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) # Packets with any VID, PCP=1. # IP TOS OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(f0) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(41) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(3f) OXM_OF_IP_DSCP(3f) # IP ECN OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(03) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(06) OXM_OF_IP_ECN(03) # IP protocol OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(05) OXM_OF_IP_PROTO(05) # IP source OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC(ac100014) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFF0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFFFFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(00000000/00000000) OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_SRC(ac100014) OXM_OF_IPV4_SRC_W(C0D80000/FFFF0000) # IP destination OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST(ac100014) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a80000/FFFF0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/FFFFFFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(00000000/00000000) OXM_OF_IPV4_DST(ac100014) OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_DST_W(C0D80000/FFFF0000) # TCP source port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC(4231) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/F0F0) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_SRC(4231) # TCP destination port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST(4231) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFF0) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_DST(4231) # UDP source port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC(8732) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/01FF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_UDP_SRC(7823) # UDP destination port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST(1782) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/F00F) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_UDP_DST(1293) # SCTP source port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC(8732) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0132/01FF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0132/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_SCTP_SRC(7823) # SCTP destination port OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST(1782) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(5005/F00F) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(5005/FFFF) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(0000/0000) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_SCTP_DST(1293) # ICMP type OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_TYPE(12) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_TYPE(10) # ICMP code OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_CODE(12) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_CODE(10) OXM_OF_ETH_TYPE(0800) OXM_OF_ICMPV4_CODE(10) OXM_OF_ICMPV4_CODE(00) # ARP opcode OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(1111) OXM_OF_ETH_TYPE(0000) OXM_OF_ARP_OP(0001) OXM_OF_ARP_OP(0001) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001) OXM_OF_ARP_OP(0001) # ARP source protocol address OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA(ac100014) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81200/FFFFFF00) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFFFF) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(00000000/00000000) OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SPA(ac100014) OXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # ARP destination protocol address OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA(ac100014) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a81200/FFFFFF00) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/FFFFFFFF) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(00000000/00000000) OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_TPA(ac100014) OXM_OF_ARP_TPA_W(C0D80000/FFFF0000) # ARP source hardware address OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA(0002e30f80a4) OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SHA(0002e30f80a4) OXM_OF_ARP_SHA(0002e30f80a4) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/ffffffffffff) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(000000000000/000000000000) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(000000000004/00000000000f) # ARP destination hardware address OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA(0002e30f80a4) OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_THA(0002e30f80a4) OXM_OF_ARP_THA(0002e30f80a4) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/ffffffffffff) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(000000000000/000000000000) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(000000000004/00000000000f) # IPv6 source OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(00000000000000000000000000000000/00000000000000000000000000000000) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) # IPv6 destination OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(00000000000000000000000000000000/00000000000000000000000000000000) OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) # IPv6 Flow Label OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(1000000f) OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/0000000f) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/000fffff) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(00000000/000ffff0) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/100fffff) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/ffffffff) OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(00000000/00000000) # ND source hardware address OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4) OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4) OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4) # ND destination hardware address OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4) OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4) OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4) # Registers 0, 1, 2, 16, and 31. NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050) NXM_NX_REG1(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2(a0e0d050) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31(a0e0d050) # Extended registers 0, 1, 7, and 15. # (For OpenFlow 1.2, OVS transforms these into its extension registers.) OXM_OF_PKT_REG0_W(acebdf5600000000/ffffffff00000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG0_W(00000000acebdf56/00000000ffffffff) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG7_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG7_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/ffffffff00000000) # Double-extended registers 0, 1, 4, and 7. # (For OpenFlow 1.2, OVS transforms these into its extension registers.) NXM_NX_XXREG0_W(acebdf5600000000acebdf5600000000/ffffffff00000000ffffffff00000000) NXM_NX_XXREG0_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXM_NX_XXREG0_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) NXM_NX_XXREG0_W(00000000acebdf5600000000acebdf56/00000000ffffffff00000000ffffffff) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0fffffffff0f0f0f0ffffffff) NXM_NX_XXREG0_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXM_NX_XXREG1_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXM_NX_XXREG0_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXM_NX_XXREG1_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) NXOXM_ET_XXREG4_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXOXM_ET_XXREG7_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXOXM_ET_XXREG4_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXOXM_ET_XXREG7_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) # Invalid field number. 01020304(1111/3333) # Invalid field numbers (experimenter OXM). ffff020800002320(11112222) ffff030800002320(1111/3333) ]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl # in port OXM_OF_IN_PORT(00000000) OXM_OF_IN_PORT(fffffffe) # metadata OXM_OF_METADATA(5a5a5a5a5a5a5a5a) OXM_OF_METADATA_W(0000000000000000/00000000ffffffff) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS OXM_OF_METADATA(1234567890abcdef) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS # eth dst OXM_OF_ETH_DST(0002e30f80a4) OXM_OF_ETH_DST_W(010000000000/010000000000) OXM_OF_ETH_DST_W(000000000000/010000000000) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS OXM_OF_ETH_DST(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) # eth src OXM_OF_ETH_SRC(020898456ddb) # eth type OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000012), OXM_OF_ETH_TYPE(0800) # vlan nx_pull_match() returned error OFPBMC_DUP_FIELD nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_VLAN_VID(0000) OXM_OF_VLAN_VID(1123) OXM_OF_VLAN_VID(1123), OXM_OF_VLAN_PCP(01) OXM_OF_VLAN_VID(0123) OXM_OF_VLAN_VID_W(0123/0123) nx_pull_match() returned error OFPBMC_BAD_WILDCARDS OXM_OF_VLAN_VID_W(0123/1123) nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_VLAN_VID(1123) OXM_OF_VLAN_VID(1123) OXM_OF_PACKET_TYPE(00000000) OXM_OF_VLAN_VID_W(1103/1f0f) OXM_OF_VLAN_VID_W(1103/1f0f), OXM_OF_VLAN_PCP(01) OXM_OF_VLAN_VID_W(1000/1000) OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) # IP TOS nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_VALUE OXM_OF_ETH_TYPE(0800), OXM_OF_IP_DSCP(3f) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP ECN OXM_OF_ETH_TYPE(0800), OXM_OF_IP_ECN(03) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP protocol OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(05) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP source OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(ac100014) OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC_W(c0a80000/ffff0000) OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(c0a80000) OXM_OF_ETH_TYPE(0800) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # IP destination OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(ac100014) OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST_W(c0a80000/ffff0000) OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(c0a88012) OXM_OF_ETH_TYPE(0800) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # TCP source port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(4231) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC_W(5050/f0f0) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(5050) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06) nx_pull_match() returned error OFPBMC_BAD_PREREQ # TCP destination port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(4231) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST_W(fde0/fff0) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(fde0) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06) nx_pull_match() returned error OFPBMC_BAD_PREREQ # UDP source port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(8732) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC_W(0132/01ff) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(0132) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11) nx_pull_match() returned error OFPBMC_BAD_PREREQ # UDP destination port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(1782) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST_W(5005/f00f) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(5005) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11) nx_pull_match() returned error OFPBMC_BAD_PREREQ # SCTP source port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC(8732) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC_W(0132/01ff) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC(0132) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84) nx_pull_match() returned error OFPBMC_BAD_PREREQ # SCTP destination port OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(1782) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST_W(5005/f00f) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(5005) OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84) nx_pull_match() returned error OFPBMC_BAD_PREREQ # ICMP type OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_TYPE(12) nx_pull_match() returned error OFPBMC_BAD_PREREQ # ICMP code OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_CODE(12) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP opcode OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_OP(0001) nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_DUP_FIELD # ARP source protocol address OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(ac100014) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA_W(c0a81200/ffffff00) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(c0a81234) OXM_OF_ETH_TYPE(0806) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP destination protocol address OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(ac100014) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA_W(c0a81200/ffffff00) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(c0a812fe) OXM_OF_ETH_TYPE(0806) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ARP source hardware address OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4) OXM_OF_ETH_TYPE(0806) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA_W(000000000004/00000000000f) # ARP destination hardware address OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4) OXM_OF_ETH_TYPE(0806) OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA_W(000000000004/00000000000f) # IPv6 source OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005) nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010000000000000000) OXM_OF_ETH_TYPE(86dd) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv6 destination OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010002000300040005) nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010000000000000000) OXM_OF_ETH_TYPE(86dd) nx_pull_match() returned error OFPBMC_BAD_PREREQ # IPv6 Flow Label nx_pull_match() returned error OFPBMC_BAD_VALUE nx_pull_match() returned error OFPBMC_BAD_PREREQ OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(0000000f/0000000f) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(00000000/000ffff0) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f) OXM_OF_ETH_TYPE(86dd) # ND source hardware address OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(87), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_SLL(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # ND destination hardware address OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(88), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_TLL(0002e30f80a4) nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ nx_pull_match() returned error OFPBMC_BAD_PREREQ # Registers 0, 1, 2, 16, and 31. NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050) NXM_NX_REG1(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2(a0e0d050) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG16_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31(a0e0d050) # Extended registers 0, 1, 7, and 15. # (For OpenFlow 1.2, OVS transforms these into its extension registers.) NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050) NXM_NX_REG1(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2(a0e0d050) NXM_NX_REG15_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG30_W(a0e0d050/f0f0f0f0) NXM_NX_REG15_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG30(a0e0d050) # Double-extended registers 0, 1, 4, and 7. # (For OpenFlow 1.2, OVS transforms these into its extension registers.) NXM_NX_REG0(acebdf56), NXM_NX_REG2(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050), NXM_NX_REG2(a0e0d050) NXM_NX_REG1(acebdf56), NXM_NX_REG3(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0), NXM_NX_REG3_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050), NXM_NX_REG2_W(a0e0d050/f0f0f0f0), NXM_NX_REG3(a0e0d050) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG3_W(a0e0d050/f0f0f0f0), NXM_NX_REG4_W(a0e0d050/f0f0f0f0), NXM_NX_REG6_W(a0e0d050/f0f0f0f0) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG3_W(a0e0d050/f0f0f0f0), NXM_NX_REG4(a0e0d050), NXM_NX_REG6(a0e0d050) NXOXM_ET_REG17_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG19_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG28_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG30_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG17_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG19_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG28(a0e0d050), NXOXM_ET_REG30(a0e0d050) # Invalid field number. nx_pull_match() returned error OFPBMC_BAD_FIELD # Invalid field numbers (experimenter OXM). nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD ], [stderr]) # Check that at least the first warning made it. (It's rate-limited # so a variable number could show up, especially under valgrind etc.) AT_CHECK([grep '1-bits in value' stderr | sed 1q], [0], [dnl nx_match|WARN|Rejecting NXM/OXM entry 0:32768:2:1:16 with 1-bits in value for bits wildcarded by the mask. ]) # Check that there wasn't any other stderr output. AT_CHECK([grep -v '1-bits in value' stderr], [1]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.3)]) AT_KEYWORDS([oxm]) AT_DATA([oxm.txt], [dnl # actset_output ONFOXM_ET_ACTSET_OUTPUT(00000000) ONFOXM_ET_ACTSET_OUTPUT(fffffffe) ONFOXM_ET_ACTSET_OUTPUT(fffffff7) OXM_OF_ACTSET_OUTPUT(00000000) OXM_OF_ACTSET_OUTPUT(fffffffe) OXM_OF_ACTSET_OUTPUT(fffffff7) ]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow13 < oxm.txt], [0], [dnl # actset_output ONFOXM_ET_ACTSET_OUTPUT(00000000) ONFOXM_ET_ACTSET_OUTPUT(fffffffe) ONFOXM_ET_ACTSET_OUTPUT(fffffff7) ONFOXM_ET_ACTSET_OUTPUT(00000000) ONFOXM_ET_ACTSET_OUTPUT(fffffffe) ONFOXM_ET_ACTSET_OUTPUT(fffffff7) ], []) AT_CLEANUP AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.5)]) AT_KEYWORDS([oxm]) AT_DATA([oxm.txt], [dnl # Extended registers 0, 1, 14, and 15. OXM_OF_PKT_REG0_W(acebdf5600000000/ffffffff00000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG0_W(00000000acebdf56/00000000ffffffff) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/ffffffff00000000) # Registers 0, 1, 2, 29, and 31. # (OpenFlow 1.5 transforms these into the standard "xregs".) NXM_NX_REG0(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0) NXM_NX_REG0(a0e0d050) NXM_NX_REG1(acebdf56) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0) NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2(a0e0d050) NXOXM_ET_REG29_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31_W(a0e0d050/f0f0f0f0) NXOXM_ET_REG29_W(a0e0d050/f0f0f0f0), NXOXM_ET_REG31(a0e0d050) # Double-extended registers 0, 1, 4, and 7. # (OpenFlow 1.5 transforms these into the standard "xregs".) NXM_NX_XXREG0_W(acebdf5600000000acebdf5600000000/ffffffff00000000ffffffff00000000) NXM_NX_XXREG0_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXM_NX_XXREG0_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) NXM_NX_XXREG0_W(00000000acebdf5600000000acebdf56/00000000ffffffff00000000ffffffff) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0) NXM_NX_XXREG0_W(a0e0d050a0e0d050a0e0d050a0e0d050/f0f0f0f0fffffffff0f0f0f0ffffffff) NXM_NX_XXREG0_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXM_NX_XXREG1_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXM_NX_XXREG0_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXM_NX_XXREG1_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) NXOXM_ET_XXREG4_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXOXM_ET_XXREG7_W(a0e0d05000000000a0e0d05000000000/f0f0f0f000000000f0f0f0f000000000) NXOXM_ET_XXREG4_W(00000000a0e0d05000000000a0e0d050/00000000f0f0f0f000000000f0f0f0f0), NXOXM_ET_XXREG7_W(a0e0d05000000000a0e0d05000000000/ffffffff00000000ffffffff00000000) # actset_output ONFOXM_ET_ACTSET_OUTPUT(00000000) ONFOXM_ET_ACTSET_OUTPUT(fffffffe) ONFOXM_ET_ACTSET_OUTPUT(fffffff7) OXM_OF_ACTSET_OUTPUT(00000000) OXM_OF_ACTSET_OUTPUT(fffffffe) OXM_OF_ACTSET_OUTPUT(fffffff7) ]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow15 < oxm.txt], [0], [dnl # Extended registers 0, 1, 14, and 15. OXM_OF_PKT_REG0_W(acebdf5600000000/ffffffff00000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG0_W(00000000acebdf56/00000000ffffffff) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(a0e0d05000000000/ffffffff00000000) # Registers 0, 1, 2, 29, and 31. # (OpenFlow 1.5 transforms these into the standard "xregs".) OXM_OF_PKT_REG0_W(acebdf5600000000/ffffffff00000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG0_W(00000000acebdf56/00000000ffffffff) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(00000000a0e0d050/00000000f0f0f0f0) OXM_OF_PKT_REG14_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG15_W(00000000a0e0d050/00000000ffffffff) # Double-extended registers 0, 1, 4, and 7. # (OpenFlow 1.5 transforms these into the standard "xregs".) OXM_OF_PKT_REG0_W(acebdf5600000000/ffffffff00000000), OXM_OF_PKT_REG1_W(acebdf5600000000/ffffffff00000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/f0f0f0f000000000), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(a0e0d05000000000/ffffffff00000000), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG0_W(00000000acebdf56/00000000ffffffff), OXM_OF_PKT_REG1_W(00000000acebdf56/00000000ffffffff) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff), OXM_OF_PKT_REG1_W(a0e0d050a0e0d050/f0f0f0f0ffffffff) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG2_W(a0e0d05000000000/f0f0f0f000000000), OXM_OF_PKT_REG3_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG2_W(a0e0d05000000000/ffffffff00000000), OXM_OF_PKT_REG3_W(a0e0d05000000000/ffffffff00000000) OXM_OF_PKT_REG8_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG9_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG14_W(a0e0d05000000000/f0f0f0f000000000), OXM_OF_PKT_REG15_W(a0e0d05000000000/f0f0f0f000000000) OXM_OF_PKT_REG8_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG9_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG14_W(a0e0d05000000000/ffffffff00000000), OXM_OF_PKT_REG15_W(a0e0d05000000000/ffffffff00000000) # actset_output OXM_OF_ACTSET_OUTPUT(00000000) OXM_OF_ACTSET_OUTPUT(fffffffe) OXM_OF_ACTSET_OUTPUT(fffffff7) OXM_OF_ACTSET_OUTPUT(00000000) OXM_OF_ACTSET_OUTPUT(fffffffe) OXM_OF_ACTSET_OUTPUT(fffffff7) ], []) AT_CLEANUP AT_SETUP([ovs-ofctl parse-oxm loose]) AT_KEYWORDS([oxm]) AT_DATA([oxm.txt], [dnl OXM_OF_IN_PORT(00000001), 01020304(1111/3333), OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000001), ffff020800002320(11112222), OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000001), ffff030800002320(1111/3333), OXM_OF_ETH_TYPE(0800) ]) AT_CHECK([ovs-ofctl --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD nx_pull_match() returned error OFPBMC_BAD_FIELD ]) AT_CHECK([ovs-ofctl parse-oxm OpenFlow12 < oxm.txt], [0], [dnl OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800) ]) AT_CLEANUP AT_SETUP([experimenter OXM encoding]) AT_DATA([oxm.txt], [dnl NXOXM_ET_ERSPAN_VER(01) NXOXM_ET_ERSPAN_IDX(01020304) NXOXM_ET_ERSPAN_IDX_W(01020304/0fffffff) NXOXM_ET_ERSPAN_DIR(01) NXOXM_ET_ERSPAN_HWID(12) ]) # Test experimenter OXM encoding. AT_CHECK([ovs-ofctl -m --strict parse-oxm OpenFlow15 < oxm.txt], [0], [dnl NXOXM_ET_ERSPAN_VER_W(01/01) 00000000 00 01 00 0e ff ff 19 06-00 00 23 20 01 01 00 00 NXOXM_ET_ERSPAN_IDX(01020304) 00000000 00 01 00 10 ff ff 16 08-00 00 23 20 01 02 03 04 NXOXM_ET_ERSPAN_IDX_W(01020304/0fffffff) 00000000 00 01 00 14 ff ff 17 0c-00 00 23 20 01 02 03 04 00000010 0f ff ff ff 00 00 00 00 NXOXM_ET_ERSPAN_DIR_W(01/01) 00000000 00 01 00 0e ff ff 1b 06-00 00 23 20 01 01 00 00 NXOXM_ET_ERSPAN_HWID_W(12/12) 00000000 00 01 00 0e ff ff 1d 06-00 00 23 20 12 12 00 00 ]) AT_CHECK([ovs-ofctl -m --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl NXOXM_ET_ERSPAN_VER_W(01/01) 00000000 00 01 00 0e ff ff 19 06-00 00 23 20 01 01 00 00 NXOXM_ET_ERSPAN_IDX(01020304) 00000000 00 01 00 10 ff ff 16 08-00 00 23 20 01 02 03 04 NXOXM_ET_ERSPAN_IDX_W(01020304/0fffffff) 00000000 00 01 00 14 ff ff 17 0c-00 00 23 20 01 02 03 04 00000010 0f ff ff ff 00 00 00 00 NXOXM_ET_ERSPAN_DIR_W(01/01) 00000000 00 01 00 0e ff ff 1b 06-00 00 23 20 01 01 00 00 NXOXM_ET_ERSPAN_HWID_W(12/12) 00000000 00 01 00 0e ff ff 1d 06-00 00 23 20 12 12 00 00 ]) AT_CLEANUP AT_SETUP([check TCP flags expression in OXM and NXM]) # NXM/OXM input for matching on TCP flags. tcp_flags='OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_FLAGS(0fff)' # Check that marshaling into NXM gives all NXM headers. AT_CHECK([echo "$tcp_flags" | ovs-ofctl parse-nxm], [0], [NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(0fff) ]) # Check that marshaling in OXM for OF1.2 gives OXM headers except for # TCP flags, which didn't have an OXM definition. AT_CHECK([echo "$tcp_flags" | ovs-ofctl parse-oxm OpenFlow12], [0], [OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(0fff) ]) # Check that marshaling in OXM for OF1.3 and OF1.4 gives OXM headers, # including the ONF extension for TCP flags introduced in OF1.3. AT_CHECK([echo "$tcp_flags" | ovs-ofctl parse-oxm OpenFlow13], [0], [OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), ONFOXM_ET_TCP_FLAGS(0fff) ]) AT_CHECK([echo "$tcp_flags" | ovs-ofctl parse-oxm OpenFlow14], [0], [OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), ONFOXM_ET_TCP_FLAGS(0fff) ]) # OpenFlow 1.5 added an OXM header for TCP flags: AT_CHECK([echo "$tcp_flags" | ovs-ofctl parse-oxm OpenFlow15], [0], [OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_FLAGS(0fff) ]) AT_CLEANUP dnl Check all of the patterns mentioned in the "VLAN Matching" section dnl in the topics/design doc AT_SETUP([ovs-ofctl check-vlan]) AT_KEYWORDS([VLAN]) dnl [1] AT_CHECK([ovs-ofctl check-vlan 0000 0000], [0], [dnl -> 0000/0000 NXM: -> 0000/0000 OXM: -> 0000/0000,-- OF1.0: 0000/1,00/1 -> 0000/0000 OF1.1: 0000/1,00/1 -> 0000/0000 ]) dnl [2] AT_CHECK([ovs-ofctl check-vlan 0000 ffff], [0], [dnl vlan_tci=0x0000 -> 0000/ffff NXM: NXM_OF_VLAN_TCI(0000) -> 0000/ffff OXM: OXM_OF_VLAN_VID(0000) -> 0000/1fff,-- OF1.0: ffff/0,00/0 -> 0000/ffff OF1.1: ffff/0,00/1 -> 0000/ffff ]) dnl [3] AT_CHECK([ovs-ofctl check-vlan 1abc 1fff], [0], [dnl dl_vlan=2748 -> 1abc/1fff NXM: NXM_OF_VLAN_TCI_W(1abc/1fff) -> 1abc/1fff OXM: OXM_OF_VLAN_VID(1abc) -> 1abc/1fff,-- OF1.0: 0abc/0,00/1 -> 1abc/1fff OF1.1: 0abc/0,00/1 -> 1abc/1fff ]) dnl [4] AT_CHECK([ovs-ofctl check-vlan b000 f000], [0], [dnl dl_vlan_pcp=5 -> b000/f000 NXM: NXM_OF_VLAN_TCI_W(b000/f000) -> b000/f000 OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(05) -> 1000/1000,05 OF1.0: 0000/1,05/0 -> b000/f000 OF1.1: fffe/0,05/0 -> b000/f000 ]) dnl [5] AT_CHECK([ovs-ofctl check-vlan babc ffff], [0], [dnl dl_vlan=2748,dl_vlan_pcp=5 -> babc/ffff NXM: NXM_OF_VLAN_TCI(babc) -> babc/ffff OXM: OXM_OF_VLAN_VID(1abc), OXM_OF_VLAN_PCP(05) -> 1abc/1fff,05 OF1.0: 0abc/0,05/0 -> babc/ffff OF1.1: 0abc/0,05/0 -> babc/ffff ]) dnl [6] AT_CHECK([ovs-ofctl check-vlan 0000 0fff], [0], [dnl vlan_tci=0x0000/0x0fff -> 0000/0fff NXM: NXM_OF_VLAN_TCI_W(0000/0fff) -> 0000/0fff OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,-- OF1.0: 0000/0,00/1 -> 1000/1fff OF1.1: 0000/0,00/1 -> 1000/1fff ]) dnl [7] AT_CHECK([ovs-ofctl check-vlan 0000 f000], [0], [dnl vlan_tci=0x0000/0xf000 -> 0000/f000 NXM: NXM_OF_VLAN_TCI_W(0000/f000) -> 0000/f000 OXM: OXM_OF_VLAN_VID_W(0000/1000) -> 0000/1000,-- OF1.0: ffff/0,00/0 -> 0000/ffff OF1.1: ffff/0,00/1 -> 0000/ffff ]) dnl [8] AT_CHECK([ovs-ofctl check-vlan 0000 efff], [0], [dnl vlan_tci=0x0000/0xefff -> 0000/efff NXM: NXM_OF_VLAN_TCI_W(0000/efff) -> 0000/efff OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,-- OF1.0: 0000/0,00/0 -> 1000/ffff OF1.1: 0000/0,00/0 -> 1000/ffff ]) dnl [9] AT_CHECK([ovs-ofctl check-vlan 1001 1001], [0], [dnl vlan_tci=0x1001/0x1001 -> 1001/1001 NXM: NXM_OF_VLAN_TCI_W(1001/1001) -> 1001/1001 OXM: OXM_OF_VLAN_VID_W(1001/1001) -> 1001/1001,-- OF1.0: 0001/0,00/1 -> 1001/1fff OF1.1: 0001/0,00/1 -> 1001/1fff ]) dnl [10] AT_CHECK([ovs-ofctl check-vlan 3000 3000], [0], [dnl vlan_tci=0x3000/0x3000 -> 3000/3000 NXM: NXM_OF_VLAN_TCI_W(3000/3000) -> 3000/3000 OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) -> 1000/1000,01 OF1.0: 0000/1,01/0 -> 3000/f000 OF1.1: fffe/0,01/0 -> 3000/f000 ]) AT_CHECK AT_CLEANUP dnl Check that "-F openflow10" rejects a flow_mod with unsupported features, dnl such as tunnels and metadata. AT_SETUP([ovs-ofctl -F option and NXM features]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 tun_id=123,actions=drop], [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10) ]) AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 metadata=123,actions=drop], [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM,OpenFlow11) is among the allowed flow formats (OpenFlow10) ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check that "-F nxm" really forces add-flow to use the NXM flow format. dnl (If it doesn't, then either the tun_id won't show up at all, or it will dnl additionally show up as the top 32 bits of the cookie.) This checks dnl for regression against bug #4566. AT_SETUP([ovs-ofctl -F option with flow_mods]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F nxm add-flow br0 tun_id=0x12345678,actions=drop]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: tun_id=0x12345678 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check that "-F openflow10" is really honored on dump-flows. dnl (If it isn't, then dump-flows will show the register match.) AT_SETUP([ovs-ofctl dump-flows honors -F option]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-flow br0 reg0=0x12345,actions=drop]) AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip], [0], [dnl OFPST_FLOW reply: actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check that "-F openflow10" fails on dump-flows if the requested match dnl can't be represented in OpenFlow 1.0. AT_SETUP([ovs-ofctl dump-flows rejects bad -F option]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl -F openflow10 dump-flows unix:br0.mgmt reg0=0xabcdef], [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10) ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check that add-flow reports non-normalized flows (feature #5029). AT_SETUP([ovs-ofctl add-flow reports non-normalized flows]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl TESTABLE_LOG add-flow br0 nw_src=1.2.3.4,actions=5], [0], [], [dnl ofp_match|INFO|normalization changed ofp_match, details: ofp_match|INFO| pre: nw_src=1.2.3.4 ofp_match|INFO|post: @&t@ ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check that --sort and --rsort works with dump-flows dnl Default field is 'priority'. Flow entries are displayed based dnl on field to sort. AT_SETUP([ovs-ofctl dump-flows with sorting]) OVS_VSWITCHD_START AT_KEYWORDS([sort]) AT_DATA([allflows.txt], [[ priority=4,in_port=23213 actions=output:42 priority=5,in_port=1029 actions=output:43 priority=7,in_port=1029 actions=output:43 priority=3,in_port=1028 actions=output:44 priority=1,in_port=1026 actions=output:45 priority=6,in_port=1027 actions=output:64 priority=2,in_port=1025 actions=output:47 priority=8,tcp,tp_src=5 actions=drop priority=9,tcp,tp_src=6 actions=drop priority=10,tun_metadata0=0xab actions=drop priority=11,tun_metadata0=0xcd actions=drop ]]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flows br0 allflows.txt ], [0], [ignore]) AT_CHECK([ovs-ofctl --sort dump-flows br0 | ofctl_strip], [0], [dnl priority=1,in_port=1026 actions=output:45 priority=2,in_port=1025 actions=output:47 priority=3,in_port=1028 actions=output:44 priority=4,in_port=23213 actions=output:42 priority=5,in_port=1029 actions=output:43 priority=6,in_port=1027 actions=output:64 priority=7,in_port=1029 actions=output:43 priority=8,tcp,tp_src=5 actions=drop priority=9,tcp,tp_src=6 actions=drop priority=10,tun_metadata0=0xab actions=drop priority=11,tun_metadata0=0xcd actions=drop ]) AT_CHECK([ovs-ofctl --rsort dump-flows br0 | ofctl_strip], [0], [dnl priority=11,tun_metadata0=0xcd actions=drop priority=10,tun_metadata0=0xab actions=drop priority=9,tcp,tp_src=6 actions=drop priority=8,tcp,tp_src=5 actions=drop priority=7,in_port=1029 actions=output:43 priority=6,in_port=1027 actions=output:64 priority=5,in_port=1029 actions=output:43 priority=4,in_port=23213 actions=output:42 priority=3,in_port=1028 actions=output:44 priority=2,in_port=1025 actions=output:47 priority=1,in_port=1026 actions=output:45 ]) AT_CHECK([ovs-ofctl --sort=in_port dump-flows br0 | ofctl_strip], [0], [dnl priority=2,in_port=1025 actions=output:47 priority=1,in_port=1026 actions=output:45 priority=6,in_port=1027 actions=output:64 priority=3,in_port=1028 actions=output:44 priority=7,in_port=1029 actions=output:43 priority=5,in_port=1029 actions=output:43 priority=4,in_port=23213 actions=output:42 priority=11,tun_metadata0=0xcd actions=drop priority=10,tun_metadata0=0xab actions=drop priority=9,tcp,tp_src=6 actions=drop priority=8,tcp,tp_src=5 actions=drop ]) AT_CHECK([ovs-ofctl --rsort=in_port dump-flows br0 | ofctl_strip], [0], [dnl priority=4,in_port=23213 actions=output:42 priority=7,in_port=1029 actions=output:43 priority=5,in_port=1029 actions=output:43 priority=3,in_port=1028 actions=output:44 priority=6,in_port=1027 actions=output:64 priority=1,in_port=1026 actions=output:45 priority=2,in_port=1025 actions=output:47 priority=11,tun_metadata0=0xcd actions=drop priority=10,tun_metadata0=0xab actions=drop priority=9,tcp,tp_src=6 actions=drop priority=8,tcp,tp_src=5 actions=drop ]) AT_CHECK([ovs-ofctl --sort=tcp_src dump-flows br0 | ofctl_strip], [0], [dnl priority=8,tcp,tp_src=5 actions=drop priority=9,tcp,tp_src=6 actions=drop priority=11,tun_metadata0=0xcd actions=drop priority=10,tun_metadata0=0xab actions=drop priority=7,in_port=1029 actions=output:43 priority=6,in_port=1027 actions=output:64 priority=5,in_port=1029 actions=output:43 priority=4,in_port=23213 actions=output:42 priority=3,in_port=1028 actions=output:44 priority=2,in_port=1025 actions=output:47 priority=1,in_port=1026 actions=output:45 ]) AT_CHECK( [ovs-ofctl --sort=in_port --sort=tcp_src --sort=tun_metadata0 dump-flows br0 | ofctl_strip], [0], [ priority=2,in_port=1025 actions=output:47 priority=1,in_port=1026 actions=output:45 priority=6,in_port=1027 actions=output:64 priority=3,in_port=1028 actions=output:44 priority=7,in_port=1029 actions=output:43 priority=5,in_port=1029 actions=output:43 priority=4,in_port=23213 actions=output:42 priority=8,tcp,tp_src=5 actions=drop priority=9,tcp,tp_src=6 actions=drop priority=10,tun_metadata0=0xab actions=drop priority=11,tun_metadata0=0xcd actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl dump-flows --names]) AT_KEYWORDS([port names]) OVS_VSWITCHD_START([\ -- add-port br0 xyzzy -- set Interface xyzzy type=dummy -- \ -- add-port br0 x-y -- set Interface x-y type=dummy -- \ -- add-port br0 abc123 -- set Interface abc123 type=dummy -- \ -- add-port br0 reallyverylongportname -- set Interface reallyverylongportname type=dummy -- \ -- add-port br0 conflictinglongportname1 -- set Interface conflictinglongportname1 type=dummy -- \ -- add-port br0 conflictinglongportname2 -- set Interface conflictinglongportname2 type=dummy]) # These plain port names should be accepted. AT_CHECK([ovs-ofctl add-flow br0 in_port=xyzzy,actions=x-y,abc123]) # reallyverylongportname is accepted truncated, but not in full. AT_CHECK([ovs-ofctl add-flow br0 in_port=reallyverylongp,actions=drop]) AT_CHECK([ovs-ofctl add-flow br0 in_port=reallyverylongportname,actions=drop], [1], [], [ovs-ofctl: reallyverylongportname: invalid or unknown port for in_port ]) # conflictinglongportname1 and 2 can't be accepted even truncated, since # they conflict when truncated. AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglongportname1,actions=drop], [1], [], [ovs-ofctl: conflictinglongportname1: invalid or unknown port for in_port ]) AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglongportname2,actions=drop], [1], [], [ovs-ofctl: conflictinglongportname2: invalid or unknown port for in_port ]) AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglong,actions=drop], [1], [], [ovs-ofctl: conflictinglong: invalid or unknown port for in_port ]) # Show that the port names get displayed properly and that port names that # aren't alphanumeric get quoted. AT_CHECK([ovs-ofctl --names dump-flows br0 | ofctl_strip | sort], [0], [dnl in_port=reallyverylongp actions=drop in_port=xyzzy actions=output:"x-y",output:abc123 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl diff-flows]) OVS_VSWITCHD_START # Add tons of flows to br0. for i in `seq 0 1023`; do echo "dl_vlan=$i,actions=drop"; done > add-flows.txt AT_CHECK([ovs-ofctl add-flows br0 add-flows.txt]) # Dump them and compare against what we expect by hand, then with diff-flows. for i in `seq 0 1023`; do echo " dl_vlan=$i actions=drop"; done | sort > expout AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sed '/NXST_FLOW/d' | sort], [0], [expout]) AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt]) # Remove even-numbered flows, compare again. for i in `seq 0 1023 2`; do echo "dl_vlan=$i"; done > del-flows.txt AT_CHECK([ovs-ofctl del-flows br0 - < del-flows.txt]) for i in `seq 0 1023 2`; do echo "+dl_vlan=$i actions=drop"; done | sort > expout AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt | sort], [0], [expout]) for i in `seq 0 1023 2`; do echo "-dl_vlan=$i actions=drop"; done | sort > expout AT_CHECK([ovs-ofctl diff-flows add-flows.txt br0 | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl diff-flows - tunnel metadata]) OVS_VSWITCHD_START AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=8}->tun_metadata1"]) # Tunnel metadata requires dynamic allocation of space in the metadata table. # To stress this, try flows with different sizes for metadata, in different # orders, and interspersed with other fields to see if they still compare # correctly. AT_DATA([flows.txt], [dnl priority=0,tun_metadata0=0,actions=drop priority=1,tun_metadata1=0xef/0xff,tun_metadata0=0xabcd,actions=drop priority=2,tun_metadata0=0xffffffff,actions=drop ]) AT_DATA([flows2.txt], [dnl priority=2,tun_metadata0=0xffffffff,actions=drop priority=0,tun_metadata0=0,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl diff-flows br0 flows2.txt], [2], [dnl -priority=1,tun_metadata0=0xabcd,tun_metadata1=0xef/0xff actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl ofpacts that differ bytewise don't necessarily differ when dnl converted to another representation, such as OpenFlow 1.0 dnl or to a string. "resubmit(,1)" is an example of an action dnl of this type: "ofpact_resubmit"s can differ in their "compat" dnl values even though this doesn't affect the string format. dnl dnl This test checks that "ovs-ofctl diff-flows" doesn't report dnl false ofpacts differences. AT_SETUP([ovs-ofctl diff-flows - suppress false differences]) OVS_VSWITCHD_START AT_DATA([flows.txt], [actions=resubmit(,1) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl diff-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br0 idle_timeout=60,dl_vlan=9,actions=output:1]) AT_CHECK([ovs-ofctl diff-flows br0 flows.txt], [2], [dnl -dl_vlan=9 idle_timeout=60 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flow br0 hard_timeout=120,cookie=1234,dl_vlan=9,actions=output:1]) AT_CHECK([ovs-ofctl diff-flows flows.txt br0], [2], [dnl +dl_vlan=9 cookie=0x4d2 hard_timeout=120 actions=output:1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl -F and -O interaction]) AT_CHECK([ovs-ofctl -F oxm -O openflow10], [1], [], [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow10) supports any of the enabled flow formats (OXM). (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.) ]) AT_CHECK([ovs-ofctl -F oxm -O openflow11], [1], [], [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow11) supports any of the enabled flow formats (OXM). (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.) ]) AT_CHECK([ovs-ofctl -F oxm -O openflow10,openflow11], [1], [], [ovs-ofctl: None of the enabled OpenFlow versions (OpenFlow10, OpenFlow11) supports any of the enabled flow formats (OXM). (Use -O to enable additional OpenFlow versions or -F to enable additional flow formats.) ]) AT_CHECK([ovs-ofctl -F oxm -O openflow10,openflow12], [1], [], [ovs-ofctl: missing command name; use --help for help ]) AT_CHECK([ovs-ofctl -F oxm -O openflow12], [1], [], [ovs-ofctl: missing command name; use --help for help ]) AT_CHECK([ovs-ofctl -F oxm -O openflow13], [1], [], [ovs-ofctl: missing command name; use --help for help ]) AT_CLEANUP AT_SETUP([ovs-ofctl ofp-parse]) # Test the echo request/reply messages (0 payload). AT_CHECK([printf '\1\2\0\10\0\0\0\0\1\3\0\10\0\0\0\0' > binary_ofp_msg]) AT_CHECK([ovs-ofctl ofp-parse binary_ofp_msg], [0], [dnl OFPT_ECHO_REQUEST (xid=0x0): 0 bytes of payload OFPT_ECHO_REPLY (xid=0x0): 0 bytes of payload ]) # Test the hello (xid:1 3-byte payload). AT_CHECK([printf '\1\0\0\13\0\0\0\1\101\102\103' > binary_ofp_msg]) AT_CHECK([ovs-ofctl ofp-parse - < binary_ofp_msg], [0], [dnl OFPT_HELLO (xid=0x1): version bitmap: 0x01 unknown data in hello: 00000000 01 00 00 0b 00 00 00 01-41 42 43 |........ABC | ]) AT_CLEANUP AT_SETUP([tcp flags - filtering]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_DATA([flows.txt], [dnl in_port=1,tcp,tp_dst=80,tcp_flags=+syn-rst-ack-fin,action=2 # Allow outbound web traffic bare-SYN in_port=1,tcp,tp_dst=80,tcp_flags=+ack,action=2 # Allow outbound web traffic with ACK bit in_port=1,tcp,tp_dst=80,tcp_flags=+rst,action=2 # Allow outbound web traffic with RST bit in_port=2,tcp,tp_src=80,tcp_flags=+ack,action=1 # Allow inbound web traffic with ACK bit in_port=2,tcp,tp_src=80,tcp_flags=+rst,action=1 # Allow inbound web traffic with RST bit priority=0,in_port=1,action=drop # Default drop outbound priority=0,in_port=2,action=drop # Default drop inbound ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br0 "tcp,tcp_flags=+ack-ack,action="], [1], [], [ovs-ofctl: ack: Each TCP flag can be specified only once ]) AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl p1 1/1: (dummy) p2 2/2: (dummy) ]) dnl Outbound web traffic with bare-SYN AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x002)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl Outbopund web traffic with ACK bit AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x110)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl Outbound web traffic with RST bit AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x104)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 ]) dnl Inbound web traffic with ACK bit AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x010)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) dnl Inbound web traffic with RST bit AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x014)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 1 ]) dnl Inbound web traffic with SYN bit without ACK or RST bits AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0xfeb)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: drop ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Check importance parameter added in OF1.4. dnl It validates whether importance set via add-flow via OpenFlow1.4+ gets dnl set or not by validating it against the dump-flows output via OpenFlow1.4+ dnl If add-flow or dump-flows is used with later version of OpenFlow prior to 1.4+ dnl then the importance will be considered zero whether provided as an argument. AT_SETUP([ovs-ofctl rule with importance]) OVS_VSWITCHD_START dnl Flow with importance parameter added via OF1.4+ and later version AT_CHECK([ovs-ofctl -O OpenFlow14 add-flow br0 priority=21,importance=21,actions=normal]) AT_CHECK([ovs-ofctl add-flow br0 priority=22,importance=22,actions=normal]) dnl Importance parameter will only be visible of flows that are added via OF1.4+ if dumped via OF1.4+ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed '/ST_FLOW reply/d' | sort], [0], [dnl importance=21, priority=21 actions=NORMAL reset_counts priority=22 actions=NORMAL ]) dnl Importance parameter will not be visible if flow is dumped with previous version prior to OF1.4+ whether added via OF1.4+ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sed '/ST_FLOW reply/d' | sort], [0], [dnl priority=21 actions=NORMAL priority=22 actions=NORMAL ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Importance parameter added in OF1.4. dnl This validates whether flows with importance dnl parameter are getting replaced with "replace-flows" or dnl not by validating dump-flows output after replace with the expected output. AT_SETUP([ovs-ofctl replace-flows with importance]) OVS_VSWITCHD_START dnl Add flows to br0 with importance via OF1.4+. For more details refer "ovs-ofctl rule with importance" test case. for i in 1 2 3 4 5 6 7 8; do echo "dl_vlan=$i,importance=$i,actions=drop"; done > add-flows.txt AT_CHECK([ovs-ofctl -O OpenFlow14 add-flows br0 add-flows.txt]) dnl Replace the flows in the bridge. for i in 1 3 5 7; do echo " importance=`expr $i + 10`, dl_vlan=$i actions=drop"; done > replace-flows.txt AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 replace-flows.txt]) dnl Dump them and compare the dump flows output against the expected output. cat replace-flows.txt > expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl replace-flows with fragments]) OVS_VSWITCHD_START AT_DATA([frag_flows.txt], [dnl ip,nw_frag=first actions=drop ip,nw_frag=later actions=drop ip,nw_frag=no actions=NORMAL ip,nw_frag=not_later actions=NORMAL ip,nw_frag=yes actions=LOCAL ]) AT_DATA([replace_flows.txt], [dnl ip,nw_frag=first actions=NORMAL ip,nw_frag=later actions=LOCAL ip,nw_frag=no actions=drop ip,nw_frag=not_later actions=drop ip,nw_frag=yes actions=drop ]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 frag_flows.txt]) on_exit 'ovs-ofctl -O OpenFlow13 dump-flows br0' dnl Check that flow replacement works. AT_CHECK([ovs-ofctl -vvconn:console:dbg -O OpenFlow13 \ replace-flows br0 replace_flows.txt 2>&1 | grep FLOW_MOD \ | sed 's/.*\(OFPT_FLOW_MOD.*\)/\1/' | strip_xids | sort], [0], [dnl OFPT_FLOW_MOD (OF1.3): ADD ip,nw_frag=first actions=NORMAL OFPT_FLOW_MOD (OF1.3): ADD ip,nw_frag=later actions=LOCAL OFPT_FLOW_MOD (OF1.3): ADD ip,nw_frag=no actions=drop OFPT_FLOW_MOD (OF1.3): ADD ip,nw_frag=not_later actions=drop OFPT_FLOW_MOD (OF1.3): ADD ip,nw_frag=yes actions=drop ]) dnl Check that replacement to the same set doesn't cause flow modifications. AT_CHECK([ovs-ofctl -vvconn:console:dbg -O OpenFlow13 \ replace-flows br0 replace_flows.txt 2>&1 | grep FLOW_MOD \ | sed 's/.*\(OFPT_FLOW_MOD.*\)/\1/' | strip_xids | sort], [0], []) dnl Compare the flow dump against the expected set. cat replace_flows.txt > expout AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 \ | ofctl_strip | sed '/OFPST_FLOW/d' | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl replace-flows with --bundle]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set vconn:dbg]) dnl Add flows to br0 with importance via OF1.4+, using an OF1.4+ bundle. For more details refer "ovs-ofctl rule with importance" test case. for i in 1 2 3 4 5 6 7 8; do echo "table=$i,dl_vlan=$i,importance=$i,actions=drop"; done > add-flows.txt AT_CHECK([ovs-ofctl --bundle --no-names add-flows br0 add-flows.txt]) dnl Replace some flows in the bridge. for i in 1 3 5 7; do echo " table=$i, importance=`expr $i + 10`, dl_vlan=$i actions=drop"; done > replace-flows.txt AT_CHECK([ovs-ofctl --bundle --no-names replace-flows br0 replace-flows.txt]) dnl Dump them and compare the dump flows output against the expected output. cat replace-flows.txt > expout AT_CHECK([ovs-ofctl -O OpenFlow14 --no-names dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort], [0], [expout]) dnl Check logs for OpenFlow trace # Prevent race. OVS_WAIT_UNTIL([vconn_sub < ovs-vswitchd.log | test `grep -- "|vconn|DBG|unix: sent (Success): OFPST_FLOW reply" | wc -l` -ge 2]) # AT_CHECK([sed -n "s/^.*\(|vconn|DBG|.*xid=.*:\).*$/\1/p" ovs-vswitchd.log], [0], [dnl AT_CHECK([print_vconn_debug | vconn_sub | ofctl_strip], [0], [dnl vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:1 dl_vlan=1 importance:1 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:2 dl_vlan=2 importance:2 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:3 dl_vlan=3 importance:3 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:4 dl_vlan=4 importance:4 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:5 dl_vlan=5 importance:5 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:6 dl_vlan=6 importance:6 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:7 dl_vlan=7 importance:7 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:8 dl_vlan=8 importance:8 actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.4): vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPST_FLOW request (OF1.4): vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4): table=1, importance=1, dl_vlan=1 actions=drop table=2, importance=2, dl_vlan=2 actions=drop table=3, importance=3, dl_vlan=3 actions=drop table=4, importance=4, dl_vlan=4 actions=drop table=5, importance=5, dl_vlan=5 actions=drop table=6, importance=6, dl_vlan=6 actions=drop table=7, importance=7, dl_vlan=7 actions=drop table=8, importance=8, dl_vlan=8 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:1 dl_vlan=1 importance:11 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:2 dl_vlan=2 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:3 dl_vlan=3 importance:13 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:4 dl_vlan=4 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:5 dl_vlan=5 importance:15 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:6 dl_vlan=6 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): ADD table:7 dl_vlan=7 importance:17 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:8 dl_vlan=8 actions=drop vconn|DBG|unix: received: OFPT_BARRIER_REQUEST (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY (OF1.4): vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPST_FLOW request (OF1.4): vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4): table=1, importance=11, dl_vlan=1 actions=drop table=3, importance=13, dl_vlan=3 actions=drop table=5, importance=15, dl_vlan=5 actions=drop table=7, importance=17, dl_vlan=7 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl ct-flush-zone]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set ct_dpif:dbg]) AT_CHECK([ovs-ofctl ct-flush-zone br0 123]) OVS_WAIT_UNTIL([grep -q "|ct_dpif|DBG|.*ct_flush:" ovs-vswitchd.log]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone 123" ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-ofctl snoop]) OVS_VSWITCHD_START dnl setup controller for br0 before starting the controller AT_CHECK([ovs-vsctl -vsyslog:off set-controller br0 unix:testcontroller]) dnl then start listening on the '.snoop' connection on_exit 'test -e ovs-ofctl-snoop.pid && kill `cat ovs-ofctl-snoop.pid`' AT_CHECK([ovs-ofctl -vsyslog:off --detach --no-chdir \ --unixctl=snoop.ctl --pidfile=ovs-ofctl-snoop.pid \ snoop br0 > snoopbr0.txt 2>&1]) dnl finally start the controller on_exit 'kill `cat ovs-testcontroller.pid`' AT_CHECK([ovs-testcontroller -vsyslog:off --detach --no-chdir --pidfile punix:testcontroller], [0], [ignore]) OVS_WAIT_UNTIL([test -e testcontroller]) dnl check for some of the initial handshake messages OVS_WAIT_UNTIL([grep -E "OFPT_FEATURES_REQUEST" snoopbr0.txt >/dev/null 2>&1]) OVS_WAIT_UNTIL([grep -E "OFPT_FEATURES_REPLY" snoopbr0.txt >/dev/null 2>&1]) OVS_WAIT_UNTIL([grep -E "OFPT_SET_CONFIG" snoopbr0.txt >/dev/null 2>&1]) AT_CHECK([ovs-appctl -t $(pwd)/snoop.ctl exit]) OVS_WAIT_WHILE([test -e ovs-ofctl-snoop.pid]) dnl need to suppress the 'connection failed' WARN message in ovs-vswitchd dnl because we need ovs-vswitchd to have the controller config before starting dnl the controller to 'snoop' the OpenFlow messages from beginning OVS_VSWITCHD_STOP(["/connection failed (No such file or directory)/d"]) AT_CLEANUP AT_SETUP([ovs-ofctl show-flows - Oversized flow]) OVS_VSWITCHD_START printf " priority=90,icmp,reg15=0x8005,metadata=0x1,nw_dst=11.0.0.1,icmp_type=8,icmp_code=0 actions=" > flow.txt for i in `seq 1 2045`; do printf "dec_ttl,resubmit(,39),"; done >> flow.txt printf "resubmit(,39)\n" >> flow.txt AT_CHECK([ovs-ofctl -O OpenFlow15 add-flows br0 flow.txt]) AT_CHECK([ovs-ofctl -O OpenFlow10 dump-flows br0 | ofctl_strip | sed '/NXST_FLOW/d' | sort], [0], []) OVS_WAIT_UNTIL([grep -q "ofp_flow|WARN|Flow exceeded the maximum flow statistics reply size and was excluded from the response set" ovs-vswitchd.log]) cat flow.txt > expout AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort], [0], [expout]) OVS_VSWITCHD_STOP(["/Flow exceeded the maximum flow statistics reply size and was excluded from the response set/d"]) AT_CLEANUP AT_SETUP([ovs-ofctl ct-flush]) OVS_VSWITCHD_START AT_CHECK([ovs-appctl vlog/set ct_dpif:dbg]) # Check flush conntrack with both zone and tuple AT_CHECK([ovs-ofctl ct-flush br0 zone=5 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1']) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 1]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=5 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_tp_src=1,ct_tp_dst=0,ct_nw_proto=17' 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0'" ovs-vswitchd.log]) # Check flush-conntrack just with tuple AT_CHECK([ovs-ofctl ct-flush br0 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_nw_proto=17,ct_tp_src=1']) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 2]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0,ct_nw_proto=17' 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0'" ovs-vswitchd.log]) # Check flush-conntrack with reply tuple AT_CHECK([ovs-ofctl ct-flush br0 '' 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_nw_proto=17,ct_tp_src=1']) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 3]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=17' 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0'" ovs-vswitchd.log]) # Check flush-conntrack with zone and reply tuple AT_CHECK([ovs-ofctl ct-flush br0 zone=5 '' 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_nw_proto=17,ct_tp_src=1']) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 4]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=5 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=17' 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0'" ovs-vswitchd.log]) # Check flush-conntrack without any tuple and zone AT_CHECK([ovs-ofctl ct-flush br0]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 5]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: " ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 mark=0]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 6]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 mark=0" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 mark=0/0x5]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 7]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 mark=0/0x5" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 mark=0xabc/0xdef]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 8]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 mark=0xabc/0xdef" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 labels=0]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 9]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 labels=0" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 labels=0/0x5]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 10]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 labels=0/0x5" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 labels=0xabc/0xdef]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 11]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 labels=0xabc/0xdef" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 zone=5 mark=25 labels=25]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 12]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=5 mark=0x19 labels=0x19" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 zone=5 mark=30/25 labels=30/25]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 13]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=5 mark=0x1e/0x19 labels=0x1e/0x19" ovs-vswitchd.log]) AT_CHECK([ovs-ofctl ct-flush br0 zone=6 mark=30/0 labels=30/0]) OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) -eq 14]) AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone 6" ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovs-router.at000066400000000000000000000426511514270232600226050ustar00rootroot00000000000000AT_BANNER([ovs-router]) AT_SETUP([appctl - route/add with gateway and pkt_mark]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.2/24], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2.2.2.3/32 br0 pkt_mark=1], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.1.0/24 br0 2.2.2.10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.2.0/24 br0 2.2.2.10 pkt_mark=2], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.3.0/24 br0 pkt_mark=3], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.foo.bar/24 br0 2.2.2.10], [2], [], [dnl Invalid 'ip/plen' parameter ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 2.2.2.4/24 br0 pkt_mark=baz], [2], [], [dnl Invalid pkt_mark, IP gateway or src_ip ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [dnl User: 1.1.1.0/24 dev br0 GW 2.2.2.10 SRC 2.2.2.2 User: 1.1.2.0/24 MARK 2 dev br0 GW 2.2.2.10 SRC 2.2.2.2 User: 2.2.2.3/32 MARK 1 dev br0 SRC 2.2.2.2 ]) AT_CHECK([ovs-appctl --format=json --pretty ovs/route/show], [0], [dnl [[ { "dst": "2.2.2.2", "local": true, "nexthops": [ { "dev": "br0"}], "prefix": 32, "prefsrc": "2.2.2.2", "priority": 128, "table": 255, "user": false}, { "dst": "2.2.2.3", "local": false, "mark": 1, "nexthops": [ { "dev": "br0"}], "prefix": 32, "prefsrc": "2.2.2.2", "priority": 160, "table": 254, "user": true}, { "dst": "2.2.2.0", "local": false, "nexthops": [ { "dev": "br0"}], "prefix": 24, "prefsrc": "2.2.2.2", "priority": 120, "table": 254, "user": false}, { "dst": "1.1.1.0", "local": false, "nexthops": [ { "dev": "br0", "gateway": "2.2.2.10"}], "prefix": 24, "prefsrc": "2.2.2.2", "priority": 152, "table": 254, "user": true}, { "dst": "1.1.2.0", "local": false, "mark": 2, "nexthops": [ { "dev": "br0", "gateway": "2.2.2.10"}], "prefix": 24, "prefsrc": "2.2.2.2", "priority": 152, "table": 254, "user": true}]] ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/add with src - ipv4]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.2/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.3/24], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.9.11/32 br0 src=192.168.9.3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.12/32 br0 192.168.9.1 src=192.168.9.3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.13/32 br0 192.168.9.1 pkt_mark=13 src=192.168.9.3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.14/32 br0 192.168.9.1 pkt_mark=14 src=192.168.9.2], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.15/32 br0 192.168.9.1 src=foo.bar.9.200], [2], [], [dnl Invalid pkt_mark, IP gateway or src_ip ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.16/32 br0 192.168.9.1 src=192.168.9.200], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.17/32 br0 192.168.11.1 src=192.168.9.3], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 192.168.10.18/32 br0 src=192.168.9.3], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 192.168.10 | sort], [0], [dnl User: 192.168.10.12/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3 User: 192.168.10.13/32 MARK 13 dev br0 GW 192.168.9.1 SRC 192.168.9.3 User: 192.168.10.14/32 MARK 14 dev br0 GW 192.168.9.1 SRC 192.168.9.2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/add with src - ipv6]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::2/64], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::3/64], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:cafe::11/128 br0 src=2001:db8:cafe::3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::12/128 br0 2001:db8:cafe::1 src=2001:db8:cafe::3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::13/128 br0 2001:db8:cafe::1 pkt_mark=13 src=2001:db8:cafe::3], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::14/128 br0 2001:db8:cafe::1 pkt_mark=14 src=2001:db8:cafe::2], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::15/128 br0 2001:db8:cafe::1 src=foo:bar:2001:db8:cafe], [2], [], [dnl Invalid pkt_mark, IP gateway or src_ip ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::16/128 br0 2001:db8:cafe::1 src=2001:db8:cafe::200], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::17/128 br0 2001:db8:face::1 src=2001:db8:cafe::3], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::18/128 br0 src=2001:db8:cafe::3], [2], [], [dnl Error while inserting route. ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 2001:db8:beef | sort], [0], [dnl User: 2001:db8:beef::12/128 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3 User: 2001:db8:beef::13/128 MARK 13 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3 User: 2001:db8:beef::14/128 MARK 14 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/lookup]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 198.51.100.0/24 br0 192.0.2.254], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 192.0.2.1/24 br0 pkt_mark=123], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 198.51.100.200/24 br0 192.0.2.250 pkt_mark=1234], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [User: 192.0.2.0/24 MARK 123 dev br0 SRC 192.0.2.1 User: 198.51.100.0/24 MARK 1234 dev br0 GW 192.0.2.250 SRC 192.0.2.1 User: 198.51.100.0/24 dev br0 GW 192.0.2.254 SRC 192.0.2.1 ]) AT_CHECK([ovs-appctl ovs/route/lookup 198.51.100.1], [0], [src 192.0.2.1 gateway 192.0.2.254 dev br0 ]) AT_CHECK([ovs-appctl ovs/route/lookup 198.51.100.1 pkt_mark=1234], [0], [src 192.0.2.1 gateway 192.0.2.250 dev br0 ]) AT_CHECK([ovs-appctl ovs/route/del 198.51.100.0/24 pkt_mark=1234], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [User: 192.0.2.0/24 MARK 123 dev br0 SRC 192.0.2.1 User: 198.51.100.0/24 dev br0 GW 192.0.2.254 SRC 192.0.2.1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/lookup6]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::1/64], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:babe::/64 br0 2001:db8:cafe::2], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 2001:db8:babe::/64 br0 2001:db8:cafe::3 pkt_mark=321], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [dnl User: 2001:db8:babe::/64 MARK 321 dev br0 GW 2001:db8:cafe::3 SRC 2001:db8:cafe::1 User: 2001:db8:babe::/64 dev br0 GW 2001:db8:cafe::2 SRC 2001:db8:cafe::1 ]) AT_CHECK([ovs-appctl ovs/route/lookup 2001:db8:babe::1eaf], [0], [src 2001:db8:cafe::1 gateway 2001:db8:cafe::2 dev br0 ]) AT_CHECK([ovs-appctl ovs/route/lookup 2001:db8:babe::1eaf pkt_mark=321], [0], [src 2001:db8:cafe::1 gateway 2001:db8:cafe::3 dev br0 ]) AT_CHECK([ovs-appctl ovs/route/del 2001:db8:babe::/64 pkt_mark=321], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show | grep User | sort], [0], [dnl User: 2001:db8:babe::/64 dev br0 GW 2001:db8:cafe::2 SRC 2001:db8:cafe::1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/rule/show]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK ]) dnl Check standard rules exist. AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/show -6], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main ]) dnl Check JSON output for standard rules. AT_CHECK([ovs-appctl --format=json --pretty ovs/route/rule/show], [0], [dnl [[ { "from": "all", "invert": false, "ipv4": true, "lookup": 255, "priority": 0, "src-prefix": 0, "user": false}, { "from": "all", "invert": false, "ipv4": true, "lookup": 254, "priority": 32766, "src-prefix": 0, "user": false}, { "from": "all", "invert": false, "ipv4": true, "lookup": 253, "priority": 32767, "src-prefix": 0, "user": false}]] ]) AT_CHECK([ovs-appctl --format=json --pretty ovs/route/rule/show -6], [0], [dnl [[ { "from": "all", "invert": false, "ipv4": false, "lookup": 255, "priority": 0, "src-prefix": 0, "user": false}, { "from": "all", "invert": false, "ipv4": false, "lookup": 254, "priority": 32766, "src-prefix": 0, "user": false}]] ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/rule/{add,del}]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 10.1.1.0/24 br0 192.0.2.2 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 10.2.2.0/24 br0 192.0.2.2 table=12], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl Cached: 192.0.2.0/24 dev br0 SRC 192.0.2.1 Cached: 192.0.2.1/32 dev br0 SRC 192.0.2.1 local User: 10.1.1.0/24 dev br0 GW 192.0.2.2 SRC 192.0.2.1 table 11 User: 10.2.2.0/24 dev br0 GW 192.0.2.2 SRC 192.0.2.1 table 12 ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.20/32 table=12], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32764: from 192.0.2.20 lookup 12 User: 32765: from 192.0.2.10 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=192.0.2.10/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32762: from 192.0.2.10 lookup 11 User: 32763: from 192.0.2.10 lookup 11 User: 32764: from 192.0.2.20 lookup 12 User: 32765: from 192.0.2.10 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/del from=192.0.2.10/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32763: from 192.0.2.10 lookup 11 User: 32764: from 192.0.2.20 lookup 12 User: 32765: from 192.0.2.10 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/del from=192.0.2.10/32 prio=32765 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32763: from 192.0.2.10 lookup 11 User: 32764: from 192.0.2.20 lookup 12 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/rule cleanup]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.0.2.1/24], [0], [OK ]) AT_CHECK([for i in `seq 1 100`; do ovs-appctl ovs/route/add 10.1.$i.0/24 br0 192.0.2.2 table=11 ; done >/dev/null]) AT_CHECK([test `ovs-appctl ovs/route/show table=11 | grep '^User:' | wc -l` -eq 100]) AT_CHECK([for i in `seq 1 50`; do ovs-appctl ovs/route/del 10.1.$i.0/24 table=11 ; done >/dev/null]) AT_CHECK([test `ovs-appctl ovs/route/show table=11 | grep '^User:' | wc -l` -eq 50]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([appctl - route/rule lookup]) AT_KEYWORDS([ovs_router]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=dummy]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr p1 10.0.0.11/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr p1 2001:db8:cafe::11/64], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr p2 10.0.0.22/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr p2 2001:db8:cafe::22/64], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 10.0.0.0/24 p1 10.0.0.1 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add ::/0 p1 2001:db8:cafe::1 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl Cached: 10.0.0.0/24 dev p2 SRC 10.0.0.22 Cached: 10.0.0.11/32 dev p1 SRC 10.0.0.11 local Cached: 10.0.0.22/32 dev p2 SRC 10.0.0.22 local Cached: 2001:db8:cafe::/64 dev p2 SRC 2001:db8:cafe::22 Cached: 2001:db8:cafe::11/128 dev p1 SRC 2001:db8:cafe::11 local Cached: 2001:db8:cafe::22/128 dev p2 SRC 2001:db8:cafe::22 local User: 10.0.0.0/24 dev p1 GW 10.0.0.1 SRC 10.0.0.11 table 11 User: ::/0 dev p1 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::11 table 11 ]) dnl Check that table 11 is ignored when no routing rule references it. AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.100], [0], [dnl src 10.0.0.22 gateway :: dev p2 ]) AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.100 src=10.0.0.11], [0], [dnl src 10.0.0.11 gateway :: dev p2 ]) dnl Check that IPv4 route in table 11 is matched and the default IPv6 route dnl in table 11 doesn't swallow other IPv4 traffic. AT_CHECK([ovs-appctl ovs/route/rule/add from=10.0.0.11/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32765: from 10.0.0.11 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.100 src=10.0.0.11], [0], [dnl src 10.0.0.11 gateway 10.0.0.1 dev p1 ]) AT_CHECK([ovs-appctl ovs/route/lookup 20.0.0.100 src=10.0.0.11], [2], [], [dnl Not found ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Check that IPv6 'from all' routing rule matches only IPv6 traffic and that dnl IPv4 route in table 11 still correctly matches IPv4 traffic. AT_CHECK([ovs-appctl ovs/route/add 0.0.0.0/0 p1 10.0.0.10 table=10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add ::/0 p1 2001:db8:cafe::10 table=10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl Cached: 10.0.0.0/24 dev p2 SRC 10.0.0.22 Cached: 10.0.0.11/32 dev p1 SRC 10.0.0.11 local Cached: 10.0.0.22/32 dev p2 SRC 10.0.0.22 local Cached: 2001:db8:cafe::/64 dev p2 SRC 2001:db8:cafe::22 Cached: 2001:db8:cafe::11/128 dev p1 SRC 2001:db8:cafe::11 local Cached: 2001:db8:cafe::22/128 dev p2 SRC 2001:db8:cafe::22 local User: 0.0.0.0/0 dev p1 GW 10.0.0.10 SRC 10.0.0.11 table 10 User: 10.0.0.0/24 dev p1 GW 10.0.0.1 SRC 10.0.0.11 table 11 User: ::/0 dev p1 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::11 table 11 User: ::/0 dev p1 GW 2001:db8:cafe::10 SRC 2001:db8:cafe::11 table 10 ]) AT_CHECK([ovs-appctl ovs/route/rule/add -6 from=all table=10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32765: from 10.0.0.11 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/show -6], [0], [dnl Cached: 0: from all lookup local User: 32764: from all lookup 10 Cached: 32766: from all lookup main ]) AT_CHECK([ovs-appctl ovs/route/lookup 2004::44 src=2001:db8:cafe::22], [0], [dnl src 2001:db8:cafe::22 gateway 2001:db8:cafe::10 dev p1 ]) AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.100 src=10.0.0.11], [0], [dnl src 10.0.0.11 gateway 10.0.0.1 dev p1 ]) dnl Check that a negated IPv6 routing rule matches only IPv6 traffic and dnl correspondingly a negated IPv4 rule only matches IPv4 traffic. AT_CHECK([ovs-appctl ovs/route/add ::/0 p1 2001:db8:cafe::44 table=4], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 0.0.0.0/0 p1 10.0.0.66 table=6], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/add -6 not from=2001:db8:cafe::100 table=6], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/add not from=10.0.0.100 table=4], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32762: not from 10.0.0.100 lookup 4 User: 32765: from 10.0.0.11 lookup 11 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/show -6], [0], [dnl Cached: 0: from all lookup local User: 32763: not from 2001:db8:cafe::100 lookup 6 User: 32764: from all lookup 10 Cached: 32766: from all lookup main ]) AT_CHECK([ovs-appctl ovs/route/lookup 2004::44 src=2001:db8:cafe::22], [0], [dnl src 2001:db8:cafe::22 gateway 2001:db8:cafe::10 dev p1 ]) AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.100 src=10.0.0.11], [0], [dnl src 10.0.0.11 gateway 10.0.0.1 dev p1 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovs-vsctl.at000066400000000000000000002023541514270232600224160ustar00rootroot00000000000000dnl OVS_VSCTL_SETUP dnl dnl Creates an empty database in the current directory and then starts dnl an ovsdb-server on it for ovs-vsctl to connect to. m4_define([OVS_VSCTL_SETUP], [OVSDB_INIT([db]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:db.sock db >/dev/null 2>&1], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`']) dnl OVS_VSCTL_CLEANUP dnl dnl Kills off the database server. m4_define([OVS_VSCTL_CLEANUP], [OVS_APP_EXIT_AND_WAIT_BY_TARGET([ovsdb-server], [ovsdb-server.pid])]) dnl RUN_OVS_VSCTL(COMMAND, ...) dnl dnl Executes each ovs-vsctl COMMAND. m4_define([RUN_OVS_VSCTL], [m4_foreach([command], [$@], [ovs-vsctl --no-wait -vreconnect:emer command ])]) m4_define([RUN_OVS_VSCTL_ONELINE], [m4_foreach([command], [$@], [ovs-vsctl --no-wait -vreconnect:emer --oneline -- command ])]) dnl RUN_OVS_VSCTL_TOGETHER(COMMAND, ...) dnl dnl Executes each ovs-vsctl COMMAND in a single run of ovs-vsctl. m4_define([RUN_OVS_VSCTL_TOGETHER], [ovs-vsctl --no-wait -vreconnect:emer --oneline dnl m4_foreach([command], [$@], [ -- command])]) dnl CHECK_BRIDGES([BRIDGE, PARENT, VLAN], ...) dnl dnl Verifies that "ovs-vsctl list-br" prints the specified list of bridges, dnl which must be in alphabetical order. Also checks that each BRIDGE has the dnl specified PARENT and is on the given VLAN. m4_define([_CHECK_BRIDGE], [AT_CHECK([RUN_OVS_VSCTL([br-to-parent $1])], [0], [$2 ]) # Check br-to-vlan, without --oneline. AT_CHECK([RUN_OVS_VSCTL([br-to-vlan $1])], [0], [$3 ]) # Check br-to-vlan, with --oneline. # (This particular test is interesting with --oneline because it returns # an integer instead of a string and that can cause type mismatches inside # python if not done carefully.) AT_CHECK([RUN_OVS_VSCTL_ONELINE([br-to-vlan $1])], [0], [$3 ]) # Check multiple queries in a single run. AT_CHECK([RUN_OVS_VSCTL_TOGETHER([br-to-parent $1], [br-to-vlan $1])], [0], [$2 $3 ])]) m4_define([CHECK_BRIDGES], [dnl Check that the bridges appear on list-br, without --oneline. AT_CHECK( [RUN_OVS_VSCTL([list-br])], [0], [m4_foreach([brinfo], [$@], [m4_car(brinfo) ])]) dnl Check that the bridges appear on list-br, with --oneline. AT_CHECK( [RUN_OVS_VSCTL_ONELINE([list-br])], [0], [m4_join([\n], m4_foreach([brinfo], [$@], [m4_car(brinfo),])) ]) dnl Check that each bridge exists according to br-exists and that dnl a bridge that should not exist does not. m4_foreach([brinfo], [$@], [AT_CHECK([RUN_OVS_VSCTL([br-exists m4_car(brinfo)])])]) AT_CHECK([RUN_OVS_VSCTL([br-exists nonexistent])], [2]) dnl Check that each bridge has the expected parent and VLAN. m4_map([_CHECK_BRIDGE], [$@])]) dnl CHECK_PORTS(BRIDGE, PORT[, PORT...]) dnl dnl Verifies that "ovs-vsctl list-ports BRIDGE" prints the specified dnl list of ports, which must be in alphabetical order. Also checks dnl that "ovs-vsctl port-to-br" reports that each port is dnl in BRIDGE. m4_define([CHECK_PORTS], [dnl Check ports without --oneline. AT_CHECK( [RUN_OVS_VSCTL([list-ports $1])], [0], [m4_foreach([port], m4_cdr($@), [port ])]) dnl Check ports with --oneline. AT_CHECK( [RUN_OVS_VSCTL_ONELINE([list-ports $1])], [0], [m4_join([\n], m4_shift($@)) ]) AT_CHECK([RUN_OVS_VSCTL([port-to-br $1])], [1], [], [ovs-vsctl: no port named $1 ]) m4_foreach( [port], m4_cdr($@), [AT_CHECK([RUN_OVS_VSCTL([[port-to-br] port])], [0], [$1 ])])]) dnl CHECK_IFACES(BRIDGE, IFACE[, IFACE...]) dnl dnl Verifies that "ovs-vsctl list-ifaces BRIDGE" prints the specified dnl list of ifaces, which must be in alphabetical order. Also checks dnl that "ovs-vsctl iface-to-br" reports that each interface is dnl in BRIDGE. m4_define([CHECK_IFACES], [AT_CHECK( [RUN_OVS_VSCTL([list-ifaces $1])], [0], [m4_foreach([iface], m4_cdr($@), [iface ])]) AT_CHECK([RUN_OVS_VSCTL([iface-to-br $1])], [1], [], [ovs-vsctl: no interface named $1 ]) m4_foreach( [iface], m4_cdr($@), [AT_CHECK([RUN_OVS_VSCTL([[iface-to-br] iface])], [0], [$1 ])])]) dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests]) AT_SETUP([ovs-vsctl connection retry]) dnl Without --retry, there should be no retry for active connections. AT_CHECK([ovs-vsctl --db=unix:foo --timeout=10 -vreconnect:emer -- init], [1], [], [stderr]) AT_CHECK([[sed 's/([^()]*)/(...reason...)/' stderr]], [0], [ovs-vsctl: unix:foo: database connection failed (...reason...) ]) dnl With --retry, we should retry for active connections. AT_CHECK( [ovs-vsctl --db=unix:foo --timeout=1 --retry -vreconnect:emer -vPATTERN:console:'%c|%p|%m' -- init echo $? > status], [0], [], [stderr]) AT_CHECK([grep -c 'terminating with signal' stderr], [0], [1 ]) if test "$IS_WIN32" = "yes"; then AT_CHECK([cat status], [0], [3 ]) else # 128 + SIGALRM AT_CHECK([cat status], [0], [142 ]) fi dnl Without --retry, we should retry for passive connections. AT_CHECK( [ovs-vsctl --db=punix:foo --timeout=1 -vreconnect:emer -vPATTERN:console:'%c|%p|%m' -- init echo $? > status], [0], [], [stderr]) AT_CHECK([grep -c 'terminating with signal' stderr], [0], [1 ]) if test "$IS_WIN32" = "yes"; then AT_CHECK([cat status], [0], [3 ]) else # 128 + SIGALRM AT_CHECK([cat status], [0], [142 ]) fi AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests -- real bridges]) AT_SETUP([add-br a]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br a])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br a]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br a])], [0]) AT_CHECK([RUN_OVS_VSCTL([add-br a])], [1], [], [ovs-vsctl: cannot create a bridge named a because a bridge named a already exists ]) AT_CHECK([RUN_OVS_VSCTL([add-br ''])], [1], [], [ovs-vsctl: bridge name must not be empty string ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br b]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b])]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-br a b 9])], [1], [], [ovs-vsctl: "--may-exist add-br a b 9" but a is not a VLAN bridge ]) CHECK_BRIDGES([a, a, 0], [b, b, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) CHECK_PORTS([b]) CHECK_IFACES([b]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br b, del-br a]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b], [del-br a])]) CHECK_BRIDGES([b, b, 0]) CHECK_PORTS([b]) CHECK_IFACES([b]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, del-br a, add-br a]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [del-br a], [add-br a], [set Interface a other_config:key=value], [get Interface a other_config:key])], [0], [ value ]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-port a a1, add-port a a2]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [--if-exists del-br b], [add-port a a1], [add-port a a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [a1], [a2]) CHECK_IFACES([a], [a1], [a2]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-port a a1, add-port a a1]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-port a a1])]) AT_CHECK([RUN_OVS_VSCTL([add-port a a1])], [1], [], [ovs-vsctl: cannot create a port named a1 because a port named a1 already exists on bridge a ]) AT_CHECK([RUN_OVS_VSCTL([add-port a ''])], [1], [], [ovs-vsctl: port name must not be empty string ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-br a]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-br b], [add-port a a1], [add-port b b1], [--if-exists del-port b b2], [del-br a])], [0], [ ]) CHECK_BRIDGES([b, b, 0]) CHECK_PORTS([b], [b1]) CHECK_IFACES([b], [b1]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-bond a bond0 a1 a2 a3])]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond a bond0 a3 a1 a2])]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond a bond0 a2 a1])], [1], [], [ovs-vsctl: "--may-exist add-bond a bond0 a2 a1" but bond0 actually has interface(s) a1, a2, a3 ]) AT_CHECK([RUN_OVS_VSCTL([add-bond a '' x y z])], [1], [], [ovs-vsctl: port name must not be empty string ]) AT_CHECK([RUN_OVS_VSCTL([add-bond a x '' y z])], [1], [], [ovs-vsctl: interface name must not be empty string ]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a2], [a3]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-bond-iface and del-bond-iface]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP # Create 2-interface bond. AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-bond a bond0 a1 a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a2]) # Add interface a3 to bond. AT_CHECK([RUN_OVS_VSCTL([add-bond-iface bond0 a3])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a2], [a3]) # Delete interface a2 from bond. AT_CHECK([RUN_OVS_VSCTL([del-bond-iface bond0 a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a3]) # Add interface a2 to bond. AT_CHECK([RUN_OVS_VSCTL([add-bond-iface bond0 a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a2], [a3]) # Delete interface a2 from bond. AT_CHECK([RUN_OVS_VSCTL([del-bond-iface a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a3]) AT_CHECK([RUN_OVS_VSCTL([--if-exists del-bond-iface bond0 a4])]) AT_CHECK([RUN_OVS_VSCTL([del-bond-iface bond0 a4])], [1], [], [ovs-vsctl: no interface named a4 ]) AT_CHECK([RUN_OVS_VSCTL([del-bond-iface a4])], [1], [], [ovs-vsctl: no interface named a4 ]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER([add-port a a4], [del-bond-iface bond0 a4])], [1], [], [ovs-vsctl: port bond0 does not have an interface a4 ]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-bond-iface bond0 a3])]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER([add-bond a bond1 b1 b2 b3], [--may-exist add-bond-iface bond1 a3])], [1], [], [ovs-vsctl: "--may-exist add-bond-iface bond1 a3" but a3 is actually attached to port bond0 ]) AT_CHECK([RUN_OVS_VSCTL([add-bond-iface bond0 a3])], [1], [], [ovs-vsctl: cannot create an interface named a3 because an interface named a3 already exists on bridge a ]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER([del-bond-iface a1], [del-bond-iface a3])], [1], [], [ovs-vsctl: cannot delete last interface from port bond0 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-port a a1]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-br b], [add-port a a1 tag=9], [get port a1 tag], [--may-exist add-port b b1], [del-port a a1])], [0], [9 ]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port b b1])]) AT_CHECK([RUN_OVS_VSCTL([del-port a])], [1], [], [ovs-vsctl: cannot delete port a because it is the local port for bridge a (deleting this port requires deleting the entire bridge) ]) AT_CHECK([RUN_OVS_VSCTL([--if-exists del-port a])]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port a b1])], [1], [], [ovs-vsctl: "--may-exist add-port a b1" but b1 is actually attached to bridge b ]) CHECK_BRIDGES([a, a, 0], [b, b, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) CHECK_PORTS([b], [b1]) CHECK_IFACES([b], [b1]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3, del-port bond0]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-bond a bond0 a1 a2 a3 tag=9], [get Port bond0 tag], [del-port bond0])], [0], [ 9 ]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, del-br a + set external-ids on deleted bridge]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br a])]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [del-br a], [set bridge $(RUN_OVS_VSCTL([get bridge a _uuid])) external-ids:key0=value0])], [1], [], [stderr]) AT_CHECK([uuidfilt stderr], [0], [dnl ovs-vsctl: no row "<0>" in table Bridge ]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([external IDs]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_ONELINE( [add-br a], [add-port a a1], [add-bond a bond0 a2 a3], [br-set-external-id a key0 value0], [add Bridge a external_ids key0=value1], [set port a1 external-ids:key1=value1], [set interface a2 external-ids:key2=value2], [set interface a2 external-ids:key3=value3], [set interface a3 external-ids:key4=value4], [br-get-external-id a], [br-get-external-id a key0], [br-get-external-id a key1], [br-set-external-id a key0 othervalue], [br-get-external-id a], [br-set-external-id a key0], [br-get-external-id a], [get port a1 external-ids], [get interface a2 external-ids], [get interface a3 external-ids])], [0], [ key0=value0 value0 key0=othervalue {key1=value1} {key2=value2, key3=value3} {key4=value4} ]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [br-get-external-id a], [get port a1 external-ids], [get interface a2 external-ids], [get interface a3 external-ids])], [0], [ {key1=value1} {key2=value2, key3=value3} {key4=value4} ]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [a1], [bond0]) CHECK_IFACES([a], [a1], [a2], [a3]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([controllers]) AT_KEYWORDS([controller ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br br0], [get-controller br0], [set-controller br0 tcp:4.5.6.7], [get-controller br0], [del-controller br0], [get-controller br0], [set-controller br0 tcp:8.9.10.11 tcp:5.4.3.2], [get-controller br0], [--inactivity-probe=30000 set-controller br0 tcp:1.2.3.4], [get-controller br0])], [0], [ tcp:4.5.6.7 tcp:5.4.3.2\ntcp:8.9.10.11 tcp:1.2.3.4 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([fail-mode]) AT_KEYWORDS([fail-mode ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br br0], [get-fail-mode br0], [set-fail-mode br0 secure], [get-fail-mode br0], [del-fail-mode br0], [get-fail-mode br0], [set-fail-mode br0 standalone], [get-fail-mode br0])], [0], [ secure standalone ]) OVS_VSCTL_CLEANUP AT_CLEANUP # check if emer-reset reset switch to known good state # test is implemented by creating switch entities and checks if after applying # emer-reset these entities are deleted AT_SETUP([emer-reset]) AT_KEYWORDS([emer-reset ovs-vsctl]) OVS_VSCTL_SETUP # define controllers, fail-mode, netflow, mirror AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br br0], [add-port br0 eth0], [add-port br0 eth1], [set-controller br0 tcp:4.5.6.7], [set-fail-mode br0 secure], [set bridge br0 netflow=@nf], [--id=@nf create netflow targets=3.4.5.6], [set bridge br0 mirrors=@m], [--id=@eth0 get port eth0], [--id=@eth1 get port eth1], [--id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1], [set bridge br0 sflow=@sf], [--id=@sf create sflow targets=1.2.3.4], [set bridge br0 datapath-type=dummy other-config:datapath-id=1234 other-config:hwaddr="00:12:34:56:78:bb"], [set bridge br0 ipfix=@fix], [--id=@fix create ipfix targets=1.2.3.4], [set bridge br0 flood_vlans=1], [set bridge br0 datapath_id=2 datapath_version="alpha"], [list bridge > configured_bridge.txt] )], [0], [stdout]) AT_CHECK( [uuidfilt configured_bridge.txt], [0],[[ <0> <1> <2> <3> _uuid : <4> auto_attach : [] controller : [<5>] datapath_id : "2" datapath_type : dummy datapath_version : alpha external_ids : {} fail_mode : secure flood_vlans : [1] flow_tables : {} ipfix : <6> mcast_snooping_enable: false mirrors : [<7>] name : br0 netflow : <8> other_config : {datapath-id="1234", hwaddr="00:12:34:56:78:bb"} ports : [<9>, <10>, <11>] protocols : [] rstp_enable : false rstp_status : {} sflow : <12> status : {} stp_enable : false ]]) # execute emer-reset AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [emer-reset])], [0], [ ]) # check if bridge was cleaned/reset AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [list bridge > cleaned_bridge.txt] )], [0], [stdout]) AT_CHECK( [uuidfilt cleaned_bridge.txt], [0],[[_uuid : <0> auto_attach : [] controller : [] datapath_id : "2" datapath_type : dummy datapath_version : alpha external_ids : {} fail_mode : [] flood_vlans : [] flow_tables : {} ipfix : [] mcast_snooping_enable: false mirrors : [] name : br0 netflow : [] other_config : {hwaddr="00:12:34:56:78:bb"} ports : [<1>, <2>, <3>] protocols : [] rstp_enable : false rstp_status : {} sflow : [] status : {} stp_enable : false ]]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- dnl OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([VLAN]) m4_define([OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF], [AT_CHECK( [RUN_OVS_VSCTL( [add-br xenbr0], [--may-exist add-br xenbr0], [add-port xenbr0 eth0], [--may-exist add-port xenbr0 eth0], [add-br xapi1 xenbr0 $1], [--may-exist add-br xapi1 xenbr0 $1], [add-port xapi1 eth0.$1])])]) dnl OVS_VSCTL_FAKE_BRIDGE_TESTS([VLAN]) m4_define([OVS_VSCTL_FAKE_BRIDGE_TESTS], [ AT_BANNER([ovs-vsctl unit tests -- fake bridges (VLAN $1)]) AT_SETUP([simple fake bridge (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-br xapi1])], [1], [], [ovs-vsctl: "--may-exist add-br xapi1" but xapi1 is a VLAN bridge for VLAN $1 ]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-br xapi1 xxx $1])], [1], [], [ovs-vsctl: "--may-exist add-br xapi1 xxx $1" but xapi1 has the wrong parent xenbr0 ]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-br xapi1 xenbr0 10])], [1], [], [ovs-vsctl: "--may-exist add-br xapi1 xenbr0 10" but xapi1 is a VLAN bridge for the wrong VLAN $1 ]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-br dup xenbr0 $1])], [1], [], [ovs-vsctl: bridge xenbr0 already has a child VLAN bridge xapi1 on VLAN $1 ]) CHECK_BRIDGES([xapi1, xenbr0, $1], [xenbr0, xenbr0, 0]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) CHECK_PORTS([xapi1], [eth0.$1]) CHECK_IFACES([xapi1], [eth0.$1]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([list bridges -- real and fake (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- list-br])], [0], [xapi1\nxenbr0 ]) AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- --real list-br])], [0], [xenbr0 ]) AT_CHECK([RUN_OVS_VSCTL_ONELINE([-- --fake list-br])], [0], [xapi1 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + del-br fake bridge (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL([del-br xapi1])]) CHECK_BRIDGES([xenbr0, xenbr0, 0]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + del-br real bridge (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL([del-br xenbr0])]) CHECK_BRIDGES OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + external IDs (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [br-set-external-id xenbr0 key0 value0], [br-set-external-id xapi1 key1 value1], [br-get-external-id xenbr0], [br-get-external-id xenbr0 key0], [br-get-external-id xapi1], [br-get-external-id xapi1 key1])], [0], [ key0=value0 value0 key1=value1 value1 ]) CHECK_BRIDGES([xapi1, xenbr0, $1], [xenbr0, xenbr0, 0]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) CHECK_PORTS([xapi1], [eth0.$1]) CHECK_IFACES([xapi1], [eth0.$1]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + del-port from parent (VLAN $1)]) AT_KEYWORDS([ovs-vsctl fake-bridge del-port]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF([$1]) AT_CHECK([RUN_OVS_VSCTL([del-port xenbr0 eth0.$1])], [1], [], [ovs-vsctl: bridge xenbr0 does not have a port eth0.$1 (although its child bridge xapi1 does) ]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) CHECK_PORTS([xapi1], [eth0.$1]) CHECK_IFACES([xapi1], [eth0.$1]) AT_CHECK([RUN_OVS_VSCTL([del-port xapi1 eth0.$1])]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) OVS_VSCTL_CLEANUP AT_CLEANUP ]) # OVS_VSCTL_FAKE_BRIDGE_TESTS OVS_VSCTL_FAKE_BRIDGE_TESTS([9]) OVS_VSCTL_FAKE_BRIDGE_TESTS([0]) dnl OVS_VSCTL_SETUP_BOND_FAKE_CONF([VLAN]) m4_define([OVS_VSCTL_SETUP_BOND_FAKE_CONF], [AT_CHECK( [RUN_OVS_VSCTL( [add-br xapi1], [add-bond xapi1 bond0 eth0 eth1], [add-br xapi2 xapi1 $1], [add-port xapi2 bond0.$1])])]) AT_SETUP([fake bridge on bond]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_BOND_FAKE_CONF([11]) CHECK_BRIDGES([xapi1, xapi1, 0], [xapi2, xapi1, 11]) CHECK_PORTS([xapi1], [bond0]) CHECK_IFACES([xapi1], [eth0], [eth1]) CHECK_PORTS([xapi2], [bond0.11]) CHECK_IFACES([xapi2], [bond0.11]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([fake bridge on bond + del-br fake bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_BOND_FAKE_CONF([11]) AT_CHECK([RUN_OVS_VSCTL_ONELINE([del-br xapi2])], [0], [ ]) CHECK_BRIDGES([xapi1, xapi1, 0]) CHECK_PORTS([xapi1], [bond0]) CHECK_IFACES([xapi1], [eth0], [eth1]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([fake bridge on bond + del-br real bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) OVS_VSCTL_SETUP OVS_VSCTL_SETUP_BOND_FAKE_CONF([11]) AT_CHECK([RUN_OVS_VSCTL([del-br xapi1])]) CHECK_BRIDGES OVS_VSCTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests -- manager commands]) AT_SETUP([managers]) AT_KEYWORDS([manager ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [del-manager], [get-manager], [set-manager tcp:4.5.6.7], [get-manager], [set-manager tcp:8.9.10.11 tcp:5.4.3.2], [get-manager], [--inactivity-probe=30000 set-manager tcp:1.2.3.4], [get-manager], [del-manager], [get-manager])], [0], [ tcp:4.5.6.7 tcp:5.4.3.2\ntcp:8.9.10.11 tcp:1.2.3.4 ]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests -- database commands]) AT_SETUP([database commands -- positive checks]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK( [RUN_OVS_VSCTL_TOGETHER([--id=@br0 create bridge name=br123], [set b br123 name=br0], [set o . bridges=@br0])], [0], [stdout]) cp stdout out1 AT_CHECK([RUN_OVS_VSCTL([list bridge], [get bridge br0 _uuid])], [0], [stdout]) cp stdout out2 AT_CHECK([uuidfilt out1 out2], [0], [[<0> _uuid : <0> auto_attach : [] controller : [] datapath_id : [] datapath_type : "" datapath_version : "" external_ids : {} fail_mode : [] flood_vlans : [] flow_tables : {} ipfix : [] mcast_snooping_enable: false mirrors : [] name : br0 netflow : [] other_config : {} ports : [] protocols : [] rstp_enable : false rstp_status : {} sflow : [] status : {} stp_enable : false <0> ]], [ignore]) AT_CHECK( [RUN_OVS_VSCTL([--columns=fail_mode,name,datapath_type list bridge])], [0], [[fail_mode : [] name : br0 datapath_type : "" ]], [ignore]) AT_CHECK( [RUN_OVS_VSCTL([--columns=fail_mode,name,datapath_type find bridge])], [0], [[fail_mode : [] name : br0 datapath_type : "" ]], [ignore]) AT_CHECK([ RUN_OVS_VSCTL_TOGETHER([--id=@br1 create bridge name=br1 datapath_type="foo"], [--id=@br2 create bridge name=br2 external-ids:bar=quux], [add o . bridges @br1 @br2])], [0], [stdout]) AT_CHECK( [RUN_OVS_VSCTL([--columns=name find bridge datapath_type!=foo])], [0], [stdout], [ignore]) AT_CHECK([sed -n '/./p' stdout | sort], [0], [[name : br0 name : br2 ]]) AT_CHECK( [RUN_OVS_VSCTL( [set bridge br0 \ 'other_config:datapath_id="0123456789ab"' \ 'other_config:hwaddr="00:11:22:33:44:55"' \ 'external-ids={"uuids"="9c45f225-a7cf-439d-976d-83db6271fda1"}' -- \ add bridge br0 external_ids '"roles"="local; remote; cloud"'])]) AT_CHECK([RUN_OVS_VSCTL_ONELINE([get bridge br0 other_config external-ids])], [0], [{datapath_id="0123456789ab", hwaddr="00:11:22:33:44:55"}\n{roles="local; remote; cloud", uuids="9c45f225-a7cf-439d-976d-83db6271fda1"} ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 other_config:hwaddr -- --if-exists get bridge br0 other-config:nonexistent])], [0], ["00:11:22:33:44:55" ]) AT_CHECK([RUN_OVS_VSCTL([remove bridge br0 other_config hwaddr 'datapath_id=""' -- get bridge br0 other_config])], [0], [{datapath_id="0123456789ab"} ]) AT_CHECK([RUN_OVS_VSCTL([remove bridge br0 other_config 'datapath_id="0123456789ab"' -- get bridge br0 other_config])], [0], [{} ]) AT_CHECK([RUN_OVS_VSCTL([clear bridge br0 external-ids -- get bridge br0 external_ids])], [0], [{} ]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER([destroy bridge br0], [destroy bridge br1], [destroy bridge br2], [clear o . bridges])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([list bridge])]) AT_CHECK([RUN_OVS_VSCTL([--if-exists get bridge x datapath_id])]) AT_CHECK([RUN_OVS_VSCTL([--if-exists list bridge x])]) AT_CHECK([RUN_OVS_VSCTL([--if-exists set controller x connection_mode=standalone])]) AT_CHECK( [RUN_OVS_VSCTL([--if-exists remove netflow x targets '"1.2.3.4:567"'])]) AT_CHECK( [RUN_OVS_VSCTL([--if-exists clear netflow x targets])]) AT_CHECK([RUN_OVS_VSCTL([-- --id=@m create Datapath datapath_version=0 -- set Open_vSwitch . datapaths:"netdev"=@m])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=1 icmp_first=1 icmp_reply=2])]) AT_CHECK([RUN_OVS_VSCTL([--may-exist add-zone-tp netdev zone=1 icmp_first=1 icmp_reply=2])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:1, Timeout Policies: icmp_first=1 icmp_reply=2 ]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=2 icmp_first=2 icmp_reply=3])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:1, Timeout Policies: icmp_first=1 icmp_reply=2 Zone:2, Timeout Policies: icmp_first=2 icmp_reply=3 ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-tp netdev zone=1])]) AT_CHECK([RUN_OVS_VSCTL([--if-exists del-zone-tp netdev zone=1])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:2, Timeout Policies: icmp_first=2 icmp_reply=3 ]) AT_CHECK( [RUN_OVS_VSCTL_TOGETHER([--id=@n create CT_Zone external_ids:"test"="123"], [--id=@m create Datapath datapath_version=0 ct_zones:"10"=@n], [set Open_vSwitch . datapaths:"netdev"=@m])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:10, Timeout Policies: system default ]) AT_CHECK([RUN_OVS_VSCTL([--if-exists del-zone-tp netdev zone=10])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 1 1])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Zone: 1, Limit: 1 ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 1 5])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Zone: 1, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev 1])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 10 5])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Zone: 10, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=10 icmp_first=1 icmp_reply=2])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [dnl Zone:10, Timeout Policies: icmp_first=1 icmp_reply=2 ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev 10])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [dnl Zone:10, Timeout Policies: icmp_first=1 icmp_reply=2 ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 10 5])]) AT_CHECK([RUN_OVS_VSCTL([del-zone-tp netdev zone=10])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Zone: 10, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [dnl Zone:10, Timeout Policies: system default ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev default 5])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Default, Limit: 5 Zone: 10, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev default 10])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Default, Limit: 10 Zone: 10, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev default])]) AT_CHECK([RUN_OVS_VSCTL([list-zone-limits netdev])], [0], [dnl Zone: 10, Limit: 5 ]) AT_CHECK([RUN_OVS_VSCTL([--if-exists del-zone-limit netdev default])]) AT_CHECK([RUN_OVS_VSCTL([-- --id=@m create Datapath datapath_version=0 'capabilities={recirc=true}' -- set Open_vSwitch . datapaths:"system"=@m])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([list-dp-cap system])], [0], [recirc=true ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([database commands -- negative checks]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([ovs-vsctl --may-exist], [1], [ignore], [ovs-vsctl: missing command name (use --help for help) ]) AT_CHECK([ovs-vsctl --may-exist --], [1], [ignore], [ovs-vsctl: missing command name (use --help for help) ]) AT_CHECK([ovs-vsctl -- --may-exist], [1], [ignore], [ovs-vsctl: missing command name (use --help for help) ]) AT_CHECK([RUN_OVS_VSCTL([add-br br0])], [0], [ignore]) AT_CHECK([RUN_OVS_VSCTL([add-br br1])], [0], [ignore]) AT_CHECK([RUN_OVS_VSCTL([set-controller br1 tcp:127.0.0.1])], [0], [ignore]) AT_CHECK([RUN_OVS_VSCTL([list netflow br0])], [1], [], [ovs-vsctl: no row "br0" in table NetFlow ]) AT_CHECK([ RUN_OVS_VSCTL_TOGETHER([--id=@n create netflow targets='"1.2.3.4:567"'], [set bridge br0 netflow=@n])], [0], [stdout]) cp stdout netflow-uuid AT_CHECK([RUN_OVS_VSCTL([list netflow `cat netflow-uuid`])], [0], [stdout]) AT_CHECK([uuidfilt netflow-uuid stdout], [0], [[<0> _uuid : <0> active_timeout : 0 add_id_to_interface : false engine_id : [] engine_type : [] external_ids : {} targets : ["1.2.3.4:567"] ]], [ignore]) AT_CHECK([RUN_OVS_VSCTL([list interx x])], [1], [], [ovs-vsctl: unknown table "interx" ]) AT_CHECK([RUN_OVS_VSCTL([list c x])], [1], [], [ovs-vsctl: "c" matches multiple table names: CT_Timeout_Policy, CT_Zone, Controller ]) AT_CHECK([RUN_OVS_VSCTL([list bridge x])], [1], [], [ovs-vsctl: no row "x" in table Bridge ]) AT_CHECK([RUN_OVS_VSCTL([get bridge x datapath_id])], [1], [], [ovs-vsctl: no row "x" in table Bridge ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 d])], [1], [], [ovs-vsctl: Bridge contains more than one column whose name matches "d": datapath_id, datapath_type, datapath_version ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 x])], [1], [], [ovs-vsctl: Bridge does not contain a column whose name matches "x" ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 :y=z])], [1], [], [ovs-vsctl: :y=z: missing column name ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 datapath_id:y=z])], [1], [], [ovs-vsctl: datapath_id:y=z: trailing garbage "=z" in argument ]) AT_CHECK([RUN_OVS_VSCTL([set bridge br0 'datapath_id:y>=z'])], [1], [], [ovs-vsctl: datapath_id:y>=z: argument does not end in "=" followed by a value. ]) AT_CHECK([RUN_OVS_VSCTL([set controller x connection_mode=standalone])], [1], [], [ovs-vsctl: no row "x" in table Controller ]) AT_CHECK([RUN_OVS_VSCTL([wait-until bridge br0 datapath_id:y,z])], [1], [], [ovs-vsctl: datapath_id:y,z: argument does not end in "=", "!=", "<", ">", "<=", ">=", "{=}", "{!=}", "{<}", "{>}", "{<=}", "{>=}", "{in}", or "{not-in}" followed by a value. ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 datapath_id::])], [1], [], [ovs-vsctl: datapath_id::: trailing garbage ":" in argument ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 datapath_id:x])], [1], [], [ovs-vsctl: cannot specify key to get for non-map column datapath_id ]) AT_CHECK([RUN_OVS_VSCTL([get bridge br0 external_ids:x])], [1], [], [ovs-vsctl: no key "x" in Bridge record "br0" column external_ids ]) AT_CHECK([RUN_OVS_VSCTL([set bridge br0 flood_vlans=-1])], [1], [], [ovs-vsctl: constraint violation: -1 is not in the valid range 0 to 4095 (inclusive) ]) AT_CHECK([RUN_OVS_VSCTL([set bridge br0 flood_vlans=4096])], [1], [], [ovs-vsctl: constraint violation: 4096 is not in the valid range 0 to 4095 (inclusive) ]) AT_CHECK([RUN_OVS_VSCTL([set controller br1 'connection-mode=xyz'])], [1], [], [[ovs-vsctl: constraint violation: xyz is not one of the allowed values ([in-band, out-of-band]) ]]) AT_CHECK([RUN_OVS_VSCTL([set controller br1 connection-mode:x=y])], [1], [], [ovs-vsctl: cannot specify key to set for non-map column connection_mode ]) AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y])], [1], [], [ovs-vsctl: "add" operation would put 2 values in column datapath_id of table Bridge but the maximum number is 1 ]) AT_CHECK([RUN_OVS_VSCTL([remove netflow `cat netflow-uuid` targets '"1.2.3.4:567"'])], [1], [], [ovs-vsctl: "remove" operation would put 0 values in column targets of table NetFlow but the minimum number is 1 ]) AT_CHECK([RUN_OVS_VSCTL([remove netflow x targets '"1.2.3.4:567"'])], [1], [], [ovs-vsctl: no row "x" in table NetFlow ]) AT_CHECK([RUN_OVS_VSCTL([clear netflow x targets])], [1], [], [ovs-vsctl: no row "x" in table NetFlow ]) AT_CHECK([RUN_OVS_VSCTL([clear netflow `cat netflow-uuid` targets])], [1], [], [ovs-vsctl: "clear" operation cannot be applied to column targets of table NetFlow, which is not allowed to be empty ]) AT_CHECK([RUN_OVS_VSCTL([destroy bridge br2])], [1], [], [ovs-vsctl: no row "br2" in table Bridge ]) AT_CHECK([RUN_OVS_VSCTL([add in br1 name x])], [1], [], [ovs-vsctl: cannot modify read-only column name in table Interface ]) AT_CHECK([RUN_OVS_VSCTL([set port br0 name=br2])], [1], [], [ovs-vsctl: cannot modify read-only column name in table Port ]) AT_CHECK([RUN_OVS_VSCTL([remove bridge br0 name br1])], [1], [], [ovs-vsctl: cannot modify read-only column name in table Bridge ]) AT_CHECK([RUN_OVS_VSCTL([remove bridge br1 flood-vlans true])], [1], [], [ovs-vsctl: "true" is not a valid integer or range ]) AT_CHECK([RUN_OVS_VSCTL([clear bridge br1 name])], [1], [], [ovs-vsctl: cannot modify read-only column name in table Bridge ]) AT_CHECK([RUN_OVS_VSCTL([-- --id=@m create Datapath datapath_version=0 -- set Open_vSwitch . datapaths:"netdev"=@m])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdevxx zone=1 icmp_first=1 icmp_reply=2])], [1], [], [ovs-vsctl: datapath netdevxx does not exist ]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=2 icmp_first=2 icmp_reply=3])]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=2 icmp_first=2 icmp_reply=3])], [1], [], [ovs-vsctl: zone id 2 already has a policy ]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:2, Timeout Policies: icmp_first=2 icmp_reply=3 ]) AT_CHECK([RUN_OVS_VSCTL([add-zone-tp netdev zone=UNKNOWN icmp_first=2 icmp_reply=3])], [1], [], [ovs-vsctl: invalid zone argument, zone=UNKNOWN ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-tp netdev zone=11])], [1], [], [ovs-vsctl: zone id 11 does not have a policy ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-tp netdev zone=UNKNOWN])], [1], [], [ovs-vsctl: invalid zone argument, zone=UNKNOWN ]) AT_CHECK([RUN_OVS_VSCTL([list-zone-tp netdev])], [0], [Zone:2, Timeout Policies: icmp_first=2 icmp_reply=3 ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdevxx 5 1])], [1], [], [ovs-vsctl: datapath netdevxx does not exist ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 88888 1])], [1], [], [ovs-vsctl: zone_id (88888) out of range ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev 5 -1])], [1], [], [ovs-vsctl: limit (-1) out of range ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev 10])], [1], [], [ovs-vsctl: zone_id 10 does not have a limit ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdevxx default 1])], [1], [], [ovs-vsctl: datapath netdevxx does not exist ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev default -1])], [1], [], [ovs-vsctl: limit (-1) out of range ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev default])], [1], [], [ovs-vsctl: datapath netdev does not have a limit ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev UNKNOWN 1])], [1], [], [ovs-vsctl: invalid zone id, UNKNOWN ]) AT_CHECK([RUN_OVS_VSCTL([set-zone-limit netdev default VALUE])], [1], [], [ovs-vsctl: invalid limit, VALUE ]) AT_CHECK([RUN_OVS_VSCTL([del-zone-limit netdev UNKNOWN])], [1], [], [ovs-vsctl: invalid zone id, UNKNOWN ]) AT_CHECK([RUN_OVS_VSCTL([-- --id=@m create Datapath datapath_version=0 'capabilities={recirc=true}' -- set Open_vSwitch . datapaths:"system"=@m])], [0], [stdout]) AT_CHECK([RUN_OVS_VSCTL([list-dp-cap nosystem])], [1], [], [ovs-vsctl: datapath "nosystem" record not found ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([database commands -- conditions]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK( [RUN_OVS_VSCTL_TOGETHER( [add-br br0], [add-br br1], [set bridge br1 flood_vlans=0 other-config:x='""'], [add-br br2], [set bridge br2 flood_vlans=1 other-config:x=y], [add-br br3], [set bridge br3 flood_vlans=0,1 other-config:x=z], [add-br br4], [set bridge br4 flood_vlans=2], [add-br br5], [set bridge br5 flood_vlans=0,2], [add-br br6], [set bridge br6 flood_vlans=1,2], [add-br br7], [set bridge br7 flood_vlans=0,1,2])], [0], [ ]) m4_define([VSCTL_CHECK_FIND], [AT_CHECK([echo `ovs-vsctl --bare --no-wait -vreconnect:emer -- --columns=name find bridge '$1' | sort`], [0], [$2 ])]) # Arithmetic relational operators without keys. VSCTL_CHECK_FIND([flood_vlans=0], [br1]) VSCTL_CHECK_FIND([flood_vlans=1], [br2]) VSCTL_CHECK_FIND([flood_vlans=0,2], [br5]) VSCTL_CHECK_FIND([flood_vlans=0,1,2], [br7]) VSCTL_CHECK_FIND([flood_vlans=3], []) VSCTL_CHECK_FIND([flood_vlans!=0], [br0 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans!=1], [br0 br1 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans!=0,2], [br0 br1 br2 br3 br4 br6 br7]) VSCTL_CHECK_FIND([flood_vlans!=0,1,2], [br0 br1 br2 br3 br4 br5 br6]) VSCTL_CHECK_FIND([flood_vlans!=3], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans<2], [br0 br1 br2]) VSCTL_CHECK_FIND([flood_vlans<0,2], [br0 br1 br2 br3 br4]) VSCTL_CHECK_FIND([flood_vlans>1], [br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans>0,1], [br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans<=2], [br0 br1 br2 br4]) VSCTL_CHECK_FIND([flood_vlans<=0,2], [br0 br1 br2 br3 br4 br5]) VSCTL_CHECK_FIND([flood_vlans>=1], [br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans>=0,1], [br3 br5 br6 br7]) # Set relational operators without keys. VSCTL_CHECK_FIND([flood_vlans{=}0], [br1]) VSCTL_CHECK_FIND([flood_vlans{=}1], [br2]) VSCTL_CHECK_FIND([flood_vlans{=}0,2], [br5]) VSCTL_CHECK_FIND([flood_vlans{=}0,1,2], [br7]) VSCTL_CHECK_FIND([flood_vlans{=}3], []) VSCTL_CHECK_FIND([flood_vlans{!=}0], [br0 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{!=}1], [br0 br1 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{!=}0,2], [br0 br1 br2 br3 br4 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{!=}0,1,2], [br0 br1 br2 br3 br4 br5 br6]) VSCTL_CHECK_FIND([flood_vlans{!=}3], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{<}[[]]], []) VSCTL_CHECK_FIND([flood_vlans{<=}[[]]], [br0]) VSCTL_CHECK_FIND([flood_vlans{in}[[]]], [br0]) VSCTL_CHECK_FIND([flood_vlans{not-in}[[]]], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{<}0], [br0]) VSCTL_CHECK_FIND([flood_vlans{<=}0], [br0 br1]) VSCTL_CHECK_FIND([flood_vlans{in}0], [br0 br1]) VSCTL_CHECK_FIND([flood_vlans{not-in}0], [br0 br2 br4 br6]) VSCTL_CHECK_FIND([flood_vlans{<}1,2], [br0 br2 br4]) VSCTL_CHECK_FIND([flood_vlans{<=}1,2], [br0 br2 br4 br6]) VSCTL_CHECK_FIND([flood_vlans{in}1,2], [br0 br2 br4 br6]) VSCTL_CHECK_FIND([flood_vlans{not-in}1,2], [br0 br1]) VSCTL_CHECK_FIND([flood_vlans{>}[[]]], [br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{>=}[[]]], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([flood_vlans{>}0], [br3 br5 br7]) VSCTL_CHECK_FIND([flood_vlans{>=}0], [br1 br3 br5 br7]) VSCTL_CHECK_FIND([flood_vlans{>}0,2], [br7]) VSCTL_CHECK_FIND([flood_vlans{>=}1,2], [br6 br7]) VSCTL_CHECK_FIND([flood_vlans{>=}0,2], [br5 br7]) # Arithmetic relational operators with keys. VSCTL_CHECK_FIND([other-config:x=""], [br1]) VSCTL_CHECK_FIND([other-config:x=y], [br2]) VSCTL_CHECK_FIND([other-config:x=z], [br3]) VSCTL_CHECK_FIND([other-config:x!=""], [br2 br3]) VSCTL_CHECK_FIND([other-config:x!=y], [br1 br3]) VSCTL_CHECK_FIND([other-config:x!=z], [br1 br2]) VSCTL_CHECK_FIND([other-config:x>y], [br3]) VSCTL_CHECK_FIND([other-config:x>=y], [br2 br3]) VSCTL_CHECK_FIND([other-config:x=}[[]]], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{>=}x], []) VSCTL_CHECK_FIND([other-config:x{>=}""], [br1]) VSCTL_CHECK_FIND([other-config:x{>=}y], [br2]) VSCTL_CHECK_FIND([other-config:x{>=}z], [br3]) VSCTL_CHECK_FIND([other-config:x{>}[[]]], [br1 br2 br3]) VSCTL_CHECK_FIND([other-config:x{>}x], []) VSCTL_CHECK_FIND([other-config:x{>}""], []) VSCTL_CHECK_FIND([other-config:x{>}y], []) VSCTL_CHECK_FIND([other-config:x{>}z], []) VSCTL_CHECK_FIND([other-config:x{in}[[]]], [br0 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{in}x], [br0 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{in}""], [br0 br1 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{in}y], [br0 br2 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{in}z], [br0 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{in}x,y,z], [br0 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}[[]]], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}x], [br0 br1 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}""], [br0 br2 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}y], [br0 br1 br3 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}z], [br0 br1 br2 br4 br5 br6 br7]) VSCTL_CHECK_FIND([other-config:x{not-in}x,y,z], [br0 br1 br4 br5 br6 br7]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([database commands -- wait-until immediately true]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br br0], [add-bond br0 bond0 eth0 eth1], [set port bond0 bond_updelay=500 other-config:abc=def])]) AT_CHECK([RUN_OVS_VSCTL([[wait-until Open_vSwitch . manager_options=[]]])]) AT_CHECK([RUN_OVS_VSCTL([[wait-until Open_vSwitch . bridges!=[]]])]) AT_CHECK([RUN_OVS_VSCTL([[wait-until Port bond0 other-config:abc=def]])]) AT_CHECK([RUN_OVS_VSCTL([[wait-until port bond0 'bond_updelay>50' 'other-config:abc>d' 'other-config:abc stdout1 & (RUN_OVS_VSCTL([[wait-until bridge br1 -- get bridge br1 other-config:abc]])) > stdout2 & (RUN_OVS_VSCTL([[wait-until bridge br1 other-config={abc=def} -- get bridge br1 other-config]])) > stdout3 & (RUN_OVS_VSCTL([[wait-until port bond0 'bond_updelay>50' -- get port bond0 bond-updelay]])) > stdout4 & # Give the ovs-vsctls a chance to read the database sleep 1 AT_CHECK([RUN_OVS_VSCTL([add-br br10 -- set bridge br10 other-config:abc=quux]) RUN_OVS_VSCTL([add-br br1 -- set bridge br1 other-config:abc=def -- add-bond br1 bond0 eth0 eth1 -- set port bond0 bond_updelay=500])]) # Wait for the ovs-vsctls to finish. wait # Check output AT_CHECK([cat stdout1], [0], [quux ]) AT_CHECK([cat stdout2], [0], [def ]) AT_CHECK([cat stdout3], [0], [{abc=def} ]) AT_CHECK([cat stdout4], [0], [500 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([--id option on create, get commands]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([add-br br0], [add-port br0 eth0], [add-port br0 eth1])]) AT_CHECK( [RUN_OVS_VSCTL_TOGETHER( [set bridge br0 mirrors=@m], [--id=@eth0 get port eth0], [--id=@eth1 get port eth1], [--id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1])], [0], [stdout]) AT_CHECK( [uuidfilt stdout], [0], [dnl <0> ]) AT_CHECK( [RUN_OVS_VSCTL( [list port eth0 eth1], [list mirror], [list bridge br0])], [0], [stdout]) AT_CHECK( [sed -n -e '/uuid/p' -e '/name/p' -e '/mirrors/p' -e '/select/p' -e '/output/p' < stdout | uuidfilt], [0], [dnl [_uuid : <0> name : eth0 _uuid : <1> name : eth1 _uuid : <2> name : mymirror output_port : <1> output_vlan : [] select_all : false select_dst_port : [<0>] select_src_port : [<0>] select_vlan : [] _uuid : <3> mirrors : [<2>] name : br0 ]]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([unreferenced record warnings]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK( [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer \ -- create Bridge name=br0 | uuidfilt], [0], [<0> ], [db_ctl_base|WARN|applying "create" command to table Bridge without --id option will have no effect ]) AT_CHECK( [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer \ -- --id=@br0 create Bridge name=br0 | uuidfilt], [0], [<0> ], [vsctl|WARN|row id "@br0" was created but no reference to it was inserted, so it will not actually appear in the database ]) AT_CHECK( [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer \ -- --id=@eth0_iface create Interface name=eth0 \ -- --id=@eth0 create Port name=eth0 interfaces=@eth0_iface \ -- --id=@m0 create Mirror name=m0 output_port=@eth0 \ -- --id=@br0 create Bridge name=br0 mirrors=@m0 \ -- set Open_vSwitch . bridges=@br0 | uuidfilt], [0], [<0> <1> <2> <3> ], [vsctl|WARN|row id "@eth0" was created but only a weak reference to it was inserted, so it will not actually appear in the database ]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl This test really shows a bug -- "create" followed by "list" in dnl the same execution shows the wrong UUID on the "list" command. dnl The bug is documented in ovs-vsctl.8. AT_SETUP([created row UUID is wrong in same execution]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL([--id=@br0 create Bridge name=br0 -- add Open_vSwitch . bridges @br0 -- list bridge])], [0], [stdout]) AT_CHECK([uuidfilt stdout], [0], [[<0> _uuid : <1> auto_attach : [] controller : [] datapath_id : [] datapath_type : "" datapath_version : "" external_ids : {} fail_mode : [] flood_vlans : [] flow_tables : {} ipfix : [] mcast_snooping_enable: false mirrors : [] name : br0 netflow : [] other_config : {} ports : [] protocols : [] rstp_enable : false rstp_status : {} sflow : [] status : {} stp_enable : false ]], [ignore]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl This test will create a linux-htb QoS record that dnl points to a few queues and use it on a1 and a2 port. dnl It also destroys all records from Qos and Queue table. AT_SETUP([--all option on destroy command]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-port a a1], [add-port a a2])]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [a1], [a2]) CHECK_IFACES([a], [a1], [a2]) AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [set Port a1 qos=@newqos], [set Port a2 qos=@newqos], [--id=@newqos create QoS type=linux-htb other-config:max-rate=1000000000 queues=0=@q0,1=@q1], [--id=@q0 create Queue other-config:min-rate=100000000 other-config:max-rate=100000000], [--id=@q1 create Queue other-config:min-rate=500000000])], [0], [ignore]) AT_CHECK([RUN_OVS_VSCTL( [--columns=other_config,type list Qos])], [0], [other_config : {max-rate="1000000000"} type : linux-htb ]) AT_CHECK([RUN_OVS_VSCTL( [--columns=other_config list Queue | sort | sed '/^$/d'])], [0], [other_config : {max-rate="100000000", min-rate="100000000"} other_config : {min-rate="500000000"} ]) AT_CHECK([RUN_OVS_VSCTL( [clear Port a1 qos], [clear Port a2 qos])]) AT_CHECK([RUN_OVS_VSCTL( [--columns=qos list Port a1 a2])], [0], [[qos : [] qos : [] ]]) AT_CHECK([RUN_OVS_VSCTL( [ destroy Qos])], [0],[], [stderr]) AT_CHECK([sed "s/^.*|WARN|//" < stderr], [0], [[either --all or records argument should be specified ]]) AT_CHECK([RUN_OVS_VSCTL( [--all destroy Qos])]) AT_CHECK([RUN_OVS_VSCTL( [-- list Qos])]) AT_CHECK([RUN_OVS_VSCTL( [--all destroy Queue])]) AT_CHECK([RUN_OVS_VSCTL( [-- list Queue])]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl add-port -- reserved port names]) AT_SETUP([add-port -- reserved names 1]) OVS_VSWITCHD_START # Test creating all reserved port names m4_foreach( [reserved_name], [[ovs-netdev], [ovs-dummy], [genev_sys], [gre_sys], [vxlan_sys]], [ # Try creating the port cat >experr < ovs-vswitchd.log], [0], [], []) # Delete the port AT_CHECK([ovs-vsctl del-port br0 reserved_name], [0], [], [])]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([add-port -- reserved names 2]) # Creates all type of tunnel ports OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 ofport_request=1\ -- add-port br0 p3 -- set Interface p3 type=vxlan \ options:remote_ip=2.2.2.2 ofport_request=3 \ -- add-port br0 p4 -- set Interface p4 type=geneve \ options:remote_ip=2.2.2.2 ofport_request=4]) # Test creating all reserved tunnel port names m4_foreach( [reserved_name], [[genev_sys], [gre_sys], [vxlan_sys]], [ # Try creating the port cat >experr < ovs-vswitchd.log], [0], [], []) # Delete the port AT_CHECK([ovs-vsctl del-port br0 reserved_name], [0], [], [])]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([naming in db commands]) AT_KEYWORDS([ovs-vsctl]) OVS_VSCTL_SETUP dnl First check that the database commands can refer to row by database UUID. AT_CHECK([RUN_OVS_VSCTL([add-br br0])]) uuid=`[]RUN_OVS_VSCTL(get port br0 _uuid)` AT_CHECK([RUN_OVS_VSCTL([get port $uuid name])], [0], [br0 ]) dnl Next check that, if a database row is given a name that has the same form dnl as the database UUIDs, the name can still be used to refer to rows. AT_CHECK([RUN_OVS_VSCTL([add-br 0fcd11a1-2ba8-4b38-a358-4bccf2bf3057])]) AT_CHECK([RUN_OVS_VSCTL([get interface 0fcd11a1-2ba8-4b38-a358-4bccf2bf3057 type])], [0], [internal ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([bootstrap ca cert]) AT_KEYWORDS([ovs-vsctl ssl]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) PKIDIR=`pwd` OVS_PKI="sh $abs_top_srcdir/utilities/ovs-pki.in --dir=$PKIDIR/pki --log=$PKIDIR/ovs-pki.log" AT_CHECK([$OVS_PKI init && $OVS_PKI req+sign vsctl switch && $OVS_PKI req ovsdbserver && $OVS_PKI self-sign ovsdbserver], [0], [ignore], [ignore]) dnl Create database. OVSDB_INIT([conf.db]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --private-key=$PKIDIR/ovsdbserver-privkey.pem --certificate=$PKIDIR/ovsdbserver-cert.pem --ca-cert=$PKIDIR/pki/switchca/cacert.pem --remote=pssl:0:127.0.0.1 --log-file="`pwd`"/ovsdb-server.log conf.db], [0], [ignore], [ignore]) on_exit "kill `cat ovsdb-server.pid`" PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # During bootstrap, the connection gets torn down. So the o/p of ovs-vsctl is error. AT_CHECK([ovs-vsctl -t 5 --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem show], [1], [ignore], [ignore]) # If the bootstrap was successful, the following file should exist. OVS_WAIT_UNTIL([test -e $PKIDIR/cacert.pem]) # After bootstrap, the connection should be successful. AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem add-br br0], [0]) AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem list-br], [0], [br0 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([peer ca cert]) AT_KEYWORDS([ovs-vsctl ssl]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) PKIDIR=`pwd` OVS_PKI="sh $abs_top_srcdir/utilities/ovs-pki.in --dir=$PKIDIR/pki --log=$PKIDIR/ovs-pki.log" AT_CHECK([$OVS_PKI init && $OVS_PKI req+sign vsctl switch && $OVS_PKI req+sign ovsdbserver controller], [0], [ignore], [ignore]) dnl Create database. OVSDB_INIT([conf.db]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --private-key=$PKIDIR/ovsdbserver-privkey.pem --certificate=$PKIDIR/ovsdbserver-cert.pem --ca-cert=$PKIDIR/pki/switchca/cacert.pem --peer-ca-cert=$PKIDIR/pki/controllerca/cacert.pem --remote=pssl:0:127.0.0.1 --log-file="`pwd`"/ovsdb-server.log conf.db], [0], [ignore], [ignore]) on_exit "kill `cat ovsdb-server.pid`" PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # During bootstrap, the connection gets torn down. So the o/p of ovs-vsctl is error. AT_CHECK([ovs-vsctl -t 5 --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem show], [1], [ignore], [ignore]) # If the bootstrap was successful, the following file should exist. OVS_WAIT_UNTIL([test -e $PKIDIR/cacert.pem]) # After bootstrap, the connection should be successful. AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem add-br br0], [0]) AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:127.0.0.1:$SSL_PORT --private-key=$PKIDIR/vsctl-privkey.pem --certificate=$PKIDIR/vsctl-cert.pem --bootstrap-ca-cert=$PKIDIR/cacert.pem list-br], [0], [br0 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([TLS server name indication (SNI)]) AT_KEYWORDS([ovsdb server positive ssl tls sni]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) AT_SKIP_IF([test "$HAVE_UNBOUND" = no]) OVSDB_INIT([conf.db]) PKIDIR=$abs_top_builddir/tests AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 -vPATTERN:file:%m -vstream_ssl conf.db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:localhost:$SSL_PORT --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --bootstrap-ca-cert=$PKIDIR/testpki-cacert.pem add-br br0]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([grep "server name" ovsdb-server.log], [0], [connection indicated server name localhost ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([TLS server name indication (SNI) with --ssl-server-name]) AT_KEYWORDS([ovs-vsctl ssl tls sni client]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) OVSDB_INIT([conf.db]) PKIDIR=$abs_top_builddir/tests # This test validates the --ssl-server-name option for SNI. # Test 1: Connect to IP with --ssl-server-name to verify SNI override. # Test 2: Connect to same IP without --ssl-server-name (no SNI sent). AT_CAPTURE_FILE([ovsdb-server.log]) on_exit 'kill $(cat ovsdb-server.pid)' AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ --remote=pssl:0:127.0.0.1 \ -vstream_ssl:file:dbg conf.db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # Test 1: SNI override - connect to IP but specify server name. # This validates that --ssl-server-name overrides connection hostname. AT_CHECK([ovs-vsctl -t 5 --no-wait \ --db=ssl:127.0.0.1:$SSL_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ --ssl-server-name=sni-test.example \ add-br br0]) # Verify SNI was sent with the overridden name. OVS_WAIT_UNTIL([grep -q \ "connection indicated server name sni-test.example" \ ovsdb-server.log]) # Save current log size for Test 2. LOG_SIZE=$(wc -l < ovsdb-server.log) # Test 2: Default behavior without SNI override - should NOT show SNI # connecting to IP address (no hostname to extract). AT_CHECK([ovs-vsctl -t 5 --no-wait \ --db=ssl:127.0.0.1:$SSL_PORT \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ add-br br1]) # Stop server to ensure logs are flushed before checking. OVS_VSCTL_CLEANUP # Check that no new SNI messages appeared in Test 2 (connecting to IP # without --ssl-server-name should not generate SNI). AT_CHECK([tail -n +$(($LOG_SIZE + 1)) ovsdb-server.log | \ grep -q "connection indicated server name"], [1]) AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([set ingress policing test]) AT_SETUP([set ingress_policing_rate and ingress_policing_burst]) AT_KEYWORDS([ingress_policing]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-port a a1], [set interface a1 ingress_policing_rate=100], [set interface a1 ingress_policing_burst=10], [--columns=ingress_policing_burst,ingress_policing_rate list interface a1])], [0], [ ingress_policing_burst: 10 ingress_policing_rate: 100 ]) OVS_VSCTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([set ingress policing(kpkts) test]) AT_SETUP([set ingress_policing_kpkts_rate and ingress_policing_kpkts_burst]) AT_KEYWORDS([ingress_policing_kpkts]) OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-port a a1], [set interface a1 ingress_policing_kpkts_rate=100], [set interface a1 ingress_policing_kpkts_burst=10], [--columns=ingress_policing_kpkts_burst,ingress_policing_kpkts_rate list interface a1])], [0], [ ingress_policing_kpkts_burst: 10 ingress_policing_kpkts_rate: 100 ]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([ovs-vsctl create bridge with uuid]) AT_KEYWORDS([create bridge with uuid]) OVS_VSCTL_SETUP AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \ name=tst0 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [0],[dnl c5cc12f8-eaa1-43a7-8a73-bccd18df1111 ]) AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \ name=tst1 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [1], [ignore], [ignore]) AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge], [0], [dnl c5cc12f8-eaa1-43a7-8a73-bccd18df1111 tst0 ]) ovs-vsctl --no-wait --id=@a create bridge \ name=tst1 -- add open . bridges @a AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge tst1], [0], [ignore]) OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([ovs-vsctl -- return error if OVSDB reload issues are reported]) OVS_VSWITCHD_START cat >experr << EOF ovs-vsctl: Error detected while setting up 'gre0': gre0: bad ip6gre 'remote_ip' gre0: ip6gre type requires valid 'remote_ip' argument. See ovs-vswitchd log for details. ovs-vsctl: The default log directory is "$OVS_RUNDIR". EOF if test "$IS_WIN32" = "yes"; then AT_CHECK([ovs-vsctl add-port br0 gre0 -- set interface gre0 type=ip6gre \ options:key=100 options:remote_ip=192.168.0.300], [160], [], [experr]) else AT_CHECK([ovs-vsctl add-port br0 gre0 -- set interface gre0 type=ip6gre \ options:key=100 options:remote_ip=192.168.0.300], [65], [], [experr]) fi OVS_VSWITCHD_STOP(["/is not a valid IP address/d /netdev_vport|WARN|gre0: bad ip6gre 'remote_ip'/d /netdev|WARN|gre0: could not set configuration/d"]) AT_CLEANUP AT_SETUP([ovs-vsctl filter option usage]) AT_KEYWORDS([ovs-vsctl filter option]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=internal ofport_request=1 -- \ add-port br0 tunnel_port \ -- set Interface tunnel_port type=geneve \ options:remote_ip=1.2.3.4 options:key=123 -- \ add-port br0 tunnel_port2 \ -- set Interface tunnel_port2 type=geneve \ options:remote_ip=1.2.3.5 options:key=125 ]) m4_define([FILTER], [grep -E '(Port|Interface)' | sort]) AT_CHECK([ovs-vsctl --filter='geneve,interface(1.2.3.5)' show | FILTER], [0], [dnl Interface tunnel_port2 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='interface(1.2.3.5),geneve' show | FILTER], [0], [dnl Interface tunnel_port2 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='interface(1.2.3.5)' show | FILTER], [0], [dnl Interface tunnel_port2 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='geneve' show | FILTER], [0], [dnl Interface br0 Interface p1 Interface tunnel_port Interface tunnel_port2 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='port(geneve),interface(1.2.3.5)' show | FILTER], [0], [dnl Interface tunnel_port2 Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='port(geneve),interface(123|125)' show | FILTER], [0], [dnl Interface tunnel_port Interface tunnel_port2 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='interface(geneve)' show | FILTER], [0], [dnl Interface tunnel_port Interface tunnel_port2 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='interface(p1),interface(p2)' show | FILTER], [0], [dnl Interface p1 Port br0 Port p1 Port tunnel_port Port tunnel_port2 ]) AT_CHECK([ovs-vsctl --filter='interface1(p1)' show], [1], [stdout], [dnl ovs-vsctl: unknown table "interface1" ]) AT_CHECK([ovs-vsctl --filter=$'interface\x28p1' show], [1], [stdout], [dnl ovs-vsctl: Malformed filter: missing closing ')' ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovs-vswitchd.at000066400000000000000000000245271514270232600231220ustar00rootroot00000000000000AT_BANNER([ovs-vswitchd]) dnl The OVS initscripts never make an empty database (one without even an dnl Open_vSwitch record) visible to ovs-vswitchd, but hand-rolled scripts dnl sometimes do. At one point, "ovs-vswitchd --detach" would never detach dnl and use 100% CPU if this happened, so this test checks for regression. AT_SETUP([ovs-vswitchd detaches correctly with empty db]) on_exit 'kill `cat ovsdb-server.pid ovs-vswitchd.pid`' dnl Create database. touch .conf.db.~lock~ AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) dnl Start ovsdb-server. *Don't* initialize database. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [ignore], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Start ovs-vswitchd. AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy --disable-system --log-file], [0], [], [stderr]) AT_CAPTURE_FILE([ovs-vswitchd.log]) dnl ovs-vswitchd detached OK or we wouldn't have made it this far. Success. OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP dnl ---------------------------------------------------------------------- m4_define([OVS_VSCTL_CHECK_RX_PKT], [ AT_CHECK([ovs-vsctl list int $1 | grep statistics | sed -n 's/^.*\(rx_packets=[[0-9]][[0-9]]*\).*$/\1/p'],[0], [dnl rx_packets=$2 ]) ]) AT_SETUP([ovs-vswitchd -- stats-update-interval]) OVS_VSWITCHD_START([add-port br0 p1 -- set int p1 type=internal]) ovs-appctl time/stop dnl at the beginning, the update of rx_packets should happen every 5 seconds. ovs-appctl time/warp 11000 1000 OVS_VSCTL_CHECK_RX_PKT([p1], [0]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 11000 1000 OVS_VSCTL_CHECK_RX_PKT([p1], [1]) dnl set the stats update interval to 100K ms, the following 'recv' should not be updated. AT_CHECK([ovs-vsctl set O . other_config:stats-update-interval=100000]) ovs-appctl time/warp 51000 1000 for i in `seq 1 5`; do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) done OVS_VSCTL_CHECK_RX_PKT([p1], [1]) dnl advance the clock by 100K ms, the previous 'recv' should be updated. ovs-appctl time/warp 100000 1000 OVS_VSCTL_CHECK_RX_PKT([p1], [6]) dnl now remove the configuration. 'recv' one packet. there should be an update after 5000 ms. AT_CHECK([ovs-vsctl clear O . other_config]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ovs-appctl time/warp 11000 1000 OVS_VSCTL_CHECK_RX_PKT([p1], [7]) OVS_VSWITCHD_STOP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_SETUP([ovs-vswitchd -- start additional ovs-vswitchd process]) OVS_VSWITCHD_START # start another ovs-vswitchd process. ovs-vswitchd --log-file=fakelog --unixctl=unixctl2 --pidfile=ovs-vswitchd-2.pid & on_exit 'kill `cat ovs-vswitchd-2.pid`' # sleep for a while sleep 5 # stop the process. OVS_APP_EXIT_AND_WAIT_BY_TARGET(["`pwd`"/unixctl2], [`pwd`/ovs-vswitchd-2.pid]) # check the fakelog, should only see one ERR for reporting # the existing ovs-vswitchd process. AT_CHECK([test `grep ERR fakelog | wc -l` -eq 1]) AT_CHECK([tail -n1 fakelog | sed -e 's/^.*ERR|//; s/pid [[0-9]]*//'], [0], [dnl another ovs-vswitchd process is running, disabling this process () until it goes away ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_SETUP([ovs-vswitchd -- switch over to another ovs-vswitchd process]) OVS_VSWITCHD_START # start a new ovs-vswitchd process. ovs-vswitchd --log-file=fakelog --enable-dummy --unixctl=unixctl2 --pidfile=ovs-vswitchd-2.pid & on_exit 'kill `cat ovs-vswitchd-2.pid`' # sleep for a while. sleep 5 # kill the current active ovs-vswitchd process. kill `cat ovs-vswitchd.pid` sleep 5 # check the creation of br0 on the new ovs-vswitchd process. AT_CHECK([grep "bridge br0" fakelog | sed -e 's/port [[0-9]]*$/port/; s/datapath ID [[a-z0-9]]*$/datapath ID/;s/^.*INFO|//'], [0], [dnl bridge br0: added interface br0 on port bridge br0: using datapath ID ]) # stop the process. OVS_APP_EXIT_AND_WAIT_BY_TARGET(["`pwd`"/unixctl2], [ovs-vswitchd-2.pid]) # check the fakelog, should not see WARN/ERR/EMER log other than the one # for reporting the existing ovs-vswitchd process and the one for killing # the process. AT_CHECK([sed -n " /|ERR|another ovs-vswitchd process is running/d /setting extended ack support failed/d /|WARN|/p /|ERR|/p /|EMER|/p" fakelog ]) # cleanup. kill `cat ovsdb-server.pid` AT_CLEANUP dnl ---------------------------------------------------------------------- AT_SETUP([ovs-vswitchd -- invalid database path]) # start an ovs-vswitchd process with invalid db path. ovs-vswitchd unix:invalid.db.sock --log-file --enable-dummy --pidfile & on_exit 'kill `cat ovs-vswitchd.pid`' # sleep for a while. sleep 10 # stop the process. OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) # should not see this log (which indicates high cpu utilization). AT_CHECK([grep "wakeup due to" ovs-vswitchd.log], [ignore]) # check the log, should not see any WARN/ERR/EMER log. AT_CHECK([sed -n " /setting extended ack support failed/d /|WARN|/p /|ERR|/p /|EMER|/p" ovs-vswitchd.log ]) AT_CLEANUP dnl ---------------------------------------------------------------------- AT_SETUP([ovs-vswitchd -- set service controller]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) OVS_VSWITCHD_START AT_CHECK([ovs-vsctl set-controller br0 punix:$(pwd)/br0.void]) OVS_WAIT_UNTIL([test -e br0.void]) AT_CHECK([ovs-vsctl set-controller br0 punix:$(pwd)/br0.void/../overwrite.file]) OVS_WAIT_UNTIL([test -n "`grep ERR ovs-vswitchd.log | grep overwrite.file`"]) OVS_VSWITCHD_STOP(["/Not adding Unix domain socket controller/d"]) AT_CLEANUP dnl ---------------------------------------------------------------------- dnl OVSDB server before release version 2.5 does not support the monitor_cond dnl method. This test defeatures the OVSDB server to simulate an older dnl OVSDB server and make sure ovs-vswitchd can still work with it AT_SETUP([ovs-vswitchd -- Compatible with OVSDB server - w/o monitor_cond]) OVS_VSWITCHD_START dnl defeature OVSDB server -- no monitor_cond AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/disable-monitor-cond]) sleep 1 AT_CHECK([ovs-vsctl add-port br0 p0 -- set interface p0 type=internal]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=internal]) dnl ovs-vswitchd should still 'see' ovsdb change with the 'monitor' method AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p0 1/1: (dummy-internal) p1 2/2: (dummy-internal) ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_SETUP([ovs-vswitchd - do not create sockets with unsafe names]) OVS_VSWITCHD_START # On Unix systems, test for sockets with "test -S". # # On Windows systems, we simulate a socket with a regular file that contains # a TCP port number, so use "test -f" there instead. if test $IS_WIN32 = yes; then S=f else S=S fi # Create a bridge with an ordinary name and make sure that the management # socket gets creatd. AT_CHECK([ovs-vsctl add-br a -- set bridge a datapath-type=dummy]) AT_CHECK([test -$S a.mgmt]) # Create a bridge with an unsafe name and make sure that the management # socket does not get created. mkdir b cat >experr < schema on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client get-schema-version unix:socket ordinals], [0], [5.1.3 ]) AT_CHECK([ovsdb-client get-schema-cksum unix:socket ordinals], [0], [12345678 9 ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-client needs-conversion (no conversion needed)]) AT_KEYWORDS([ovsdb client file positive]) on_exit 'kill `cat *.pid`' ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client needs-conversion unix:socket schema], [0], [no ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-client needs-conversion (conversion needed)]) AT_KEYWORDS([ovsdb client file positive]) ordinal_schema > schema touch .db.~lock~ on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) sed 's/5\.1\.3/5.1.4/' < schema > schema2 AT_CHECK([diff schema schema2], [1], [ignore]) AT_CHECK([ovsdb-client needs-conversion unix:socket schema2], [0], [yes ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-client backup and restore]) AT_KEYWORDS([ovsdb client positive]) on_exit 'kill `cat *.pid`' dnl Create a database. ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema]) dnl Put some data in the database. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] [{"uuid":["uuid","<1>"]},{}] [{"uuid":["uuid","<2>"]},{}] [{"uuid":["uuid","<3>"]},{}] [{"uuid":["uuid","<4>"]},{}] [{"uuid":["uuid","<5>"]},{}] ]], [ignore]) dnl Start the database server. AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db], [0]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Dump a copy of the data and a backup of it. AT_CHECK([ovsdb-client dump > dump1]) AT_CHECK([ovsdb-client backup > backup]) dnl Mess up the data a little, verify that it changed, then restore it dnl and verify restoration. AT_CHECK( [[ovsdb-client transact ' ["ordinals", {"op": "update", "table": "ordinals", "where": [], "row": {"name": ""}}]']], [0], [[[{"count":6}] ]]) AT_CHECK([ovsdb-client --no-headings dump ordinals | sort -k 3 | uuidfilt], [0], [dnl ordinals table <0> "" 0 <1> "" 1 <2> "" 2 <3> "" 3 <4> "" 4 <5> "" 5 ]) AT_CHECK([ovsdb-client restore < backup]) AT_CHECK([ovsdb-client dump | tr -s ' ' | sort -k 3 | uuidfilt], [0], [dnl ordinals table ------------------------------------ ----- ------ <0> zero 0 <1> one 1 <2> two 2 <3> three 3 <4> four 4 <5> five 5 _uuid name number ]) # Combining the original dump and the backup dump should reveal that the # rows have different uuids: AT_CHECK([(ovsdb-client dump; cat dump1) | tr -s ' ' | sort -k 3 | uuidfilt], [0], [dnl ordinals table ordinals table ------------------------------------ ----- ------ ------------------------------------ ----- ------ <0> zero 0 <1> zero 0 <2> one 1 <3> one 1 <4> two 2 <5> two 2 <6> three 3 <7> three 3 <8> four 4 <9> four 4 <10> five 5 <11> five 5 _uuid name number _uuid name number ]) dnl Stop the database server, then re-start it based on the backup. OVSDB_SERVER_SHUTDOWN AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock backup], [0]) dnl Dump a new copy of the data. AT_CHECK([ovsdb-client dump > dump2]) sort dump2 > expout dnl Verify that the two dumps are the same. AT_CHECK([sort dump1], [0], [expout]) AT_CLEANUP AT_SETUP([ovsdb-client query]) AT_KEYWORDS([ovsdb client positive]) on_exit 'kill `cat *.pid`' dnl Create a database. ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema]) dnl Start the database server. AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db], [0]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Put some data in the database. dnl Use "query" for some of them, which won't have any effect. AT_CHECK( [[for txn in 'transact zero 0' \ 'query one 1' \ 'transact two 2' \ 'query three 3' \ 'transact four 4' \ 'query five 5' do set -- $txn ovsdb-client $1 ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$2'", "number": '$3'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] [{"uuid":["uuid","<1>"]},{}] [{"uuid":["uuid","<2>"]},{}] [{"uuid":["uuid","<3>"]},{}] [{"uuid":["uuid","<4>"]},{}] [{"uuid":["uuid","<5>"]},{}] ]], [ignore]) AT_CHECK([ovsdb-client -f csv dump | sort -t, -k 3 | uuidfilt], [0], [dnl ordinals table <0>,zero,0 <1>,two,2 <2>,four,4 _uuid,name,number ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-client record/replay]) AT_KEYWORDS([ovsdb client record replay]) on_exit 'kill `cat *.pid`' dnl Create a database. ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema]) dnl Start the database server. AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile dnl --log-file --remote=punix:db.sock db], [0]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Start a monitor on the 'ordinals' db with recording enabled. AT_CHECK([mkdir replay_dir]) AT_CHECK([ovsdb-client --record=./replay_dir dnl -vfile -vvlog:off --detach --no-chdir dnl --pidfile --log-file=monitor.log dnl --db-change-aware --no-headings dnl monitor unix:db.sock dnl ordinals ordinals number name dnl > monitor.stdout 2> monitor.stderr]) AT_CAPTURE_FILE([monitor.log]) dnl Put some data in the database. AT_CHECK( [[for txn in 'transact zero 0' \ 'transact two 2' \ 'transact four 4' do set -- $txn ovsdb-client $1 ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$2'", "number": '$3'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] [{"uuid":["uuid","<1>"]},{}] [{"uuid":["uuid","<2>"]},{}] ]], [ignore]) AT_CHECK([ovsdb-client -f csv dump | sort -t, -k 3 | uuidfilt], [0], [dnl ordinals table <0>,zero,0 <1>,two,2 <2>,four,4 _uuid,name,number ]) dnl Stopping the server. OVSDB_SERVER_SHUTDOWN dnl ovsdb-client should exit by itself after disconnection form the server. OVS_WAIT_WHILE([test -e ovsdb-client.pid]) dnl Starting replay. AT_CHECK([ovsdb-client --replay=./replay_dir dnl -vfile -vvlog:off --detach --no-chdir dnl --pidfile --log-file=monitor-replay.log dnl --db-change-aware --no-headings dnl monitor unix:db.sock dnl ordinals ordinals number name dnl > monitor-replay.stdout 2> monitor-replay.stderr]) dnl Waiting for client to exit the same way as it exited during recording. OVS_WAIT_WHILE([test -e ovsdb-client.pid]) AT_CHECK([diff -u monitor.stdout monitor-replay.stdout]) AT_CHECK([diff -u monitor.stderr monitor-replay.stderr]) dnl Stripping out timestamps, PIDs and poll_loop warnings from the log. dnl Also stripping socket_util errors as sockets are not used in replay. m4_define([CLEAN_LOG_FILE], [sed 's/[[0-9\-]]*T[[0-9:\.]]*Z|[[0-9]]*\(|.*$\)/\1/g' $1 | dnl sed '/|poll_loop|/d' | dnl sed '/|socket_util|/d' | dnl sed 's/[[0-9]]*\.ctl/\.ctl/g'> $2]) CLEAN_LOG_FILE([monitor.log], [monitor.log.clear]) CLEAN_LOG_FILE([monitor-replay.log], [monitor-replay.log.clear]) AT_CHECK([diff -u monitor.log.clear monitor-replay.log.clear]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-cluster-testsuite.at000066400000000000000000000003601514270232600252720ustar00rootroot00000000000000AT_INIT m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/ovsdb-execution.at]) m4_include([tests/ovsdb-cluster.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-cluster.at000066400000000000000000001406561514270232600232600ustar00rootroot00000000000000OVS_START_SHELL_HELPERS # ovsdb_check_cluster N_SERVERS SCHEMA_FUNC OUTPUT USE_LOCAL_CONFIG TRANSACTION... ovsdb_check_cluster () { set -x local n=$1 schema_func=$2 output=$3 local_config=$4 shift; shift; shift; shift $schema_func > schema schema=`ovsdb-tool schema-name schema` AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr]) if test X$local_config = X"yes"; then for i in `seq $n`; do AT_CHECK([ovsdb-tool create c$i.db $top_srcdir/ovsdb/local-config.ovsschema], [0], [], [stderr]) local ctxn="[[\"Local_Config\", {\"op\": \"insert\", \"table\": \"Config\", \"row\": {\"connections\": [\"named-uuid\",\"conn$n\"]}}, {\"op\": \"insert\", \"table\": \"Connection\", \"uuid-name\": \"conn$n\", \"row\": {\"target\": \"punix:s$i.ovsdb\"}}]]" AT_CHECK([ovsdb-tool transact c$i.db "$ctxn"], [0], [ignore], [stderr]) done fi AT_CHECK([grep -v 'from ephemeral to persistent' stderr], [1]) cid=`ovsdb-tool db-cid s1.db` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do local remote=punix:s$i.ovsdb local config_db= if test X$local_config = X"yes"; then remote=db:Local_Config,Config,connections config_db=c$i.db fi AT_CHECK([ovsdb-server -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=$remote s$i.db $config_db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema connected]) done for txn do AT_CHECK([ovsdb-client -vjsonrpc -vconsole:off -vsyslog:off -vvlog:off --log-file transact unix:s1.ovsdb,unix:s2.ovsdb,unix:s3.ovsdb "$txn"], [0], [stdout]) cat stdout >> output done AT_CHECK_UNQUOTED([uuidfilt output], [0], [$output]) for i in `seq $n`; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done AT_CHECK([ovsdb-tool check-cluster s*.db]) } OVS_END_SHELL_HELPERS # Test a 1-server cluster. AT_BANNER([OVSDB - clustered transactions (1 server)]) m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1 - cluster of 1]) AT_KEYWORDS([ovsdb server positive unix cluster cluster1 $5]) ovsdb_check_cluster 1 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) AT_CLEANUP]) EXECUTION_EXAMPLES # Test a 3-server cluster. AT_BANNER([OVSDB - clustered transactions (3 servers)]) m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1 - cluster of 3]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3 $5]) ovsdb_check_cluster 3 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) AT_CLEANUP]) EXECUTION_EXAMPLES # Test a 5-server cluster. AT_BANNER([OVSDB - clustered transactions (5 servers)]) m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1 - cluster of 5]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5 $5]) ovsdb_check_cluster 5 "$2" '$4' no m4_foreach([txn], [$3], ['txn' ]) AT_CLEANUP]) EXECUTION_EXAMPLES # Test a 3-server cluster using a Local_Config db. AT_BANNER([OVSDB - clustered transactions Local_Config (3 servers)]) m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1 - cluster of 3]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3 Local_Config $5]) ovsdb_check_cluster 3 "$2" '$4' yes m4_foreach([txn], [$3], ['txn' ]) AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([OVSDB - disconnect from cluster]) OVS_START_SHELL_HELPERS # ovsdb_test_cluster_disconnect N_SERVERS LEADER_OR_FOLLOWER [CHECK_FLAPPING] # Test server disconnected from the cluster. # N_SERVERS: Number of servers in the cluster. # LEADER_OR_FOLLOWER: The role of the server that is disconnected from the # cluster: "leader" or "follower". # CHECK_FLAPPING: Whether to check if is_disconnected flapped. "yes", "no". ovsdb_test_cluster_disconnect () { n=$1 leader_or_follower=$2 check_flapping=$3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done AT_CHECK([ovsdb-client transact unix:s1.ovsdb '[["idltest", {"op": "insert", "table": "simple", "row": {"i": 1}}]]'], [0], [ignore], [ignore]) # When a node is disconnected from the cluster, the IDL should disconnect # and retry even if it uses a single remote, because the remote IP can be # a VIP on a load-balance. So we use single remote to test here. if test $leader_or_follower = "leader"; then target=1 shutdown=`seq $(($n/2 + 1)) $n` cleanup=`seq $(($n/2))` else target=$n # shutdown followers before the leader (s1) so that there is no chance for # s$n to become leader during the process. shutdown="`seq 2 $(($n/2 + 1))` 1" cleanup=`seq $(($n/2 + 2)) $n` fi echo shutdown=$shutdown echo cleanup=$cleanup # Connect to $target. Use "wait" to trigger a non-op transaction so # that test-ovsdb will not quit. txn='[["idltest", {"op": "wait", "table": "simple", "where": [["i", "==", 1]], "columns": ["i"], "until": "==", "rows": [{"i": 1}]}]]' test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -v -t10 idl \ unix:s$target.ovsdb "$txn" > test-ovsdb.log 2>&1 & echo $! > test-ovsdb.pid OVS_WAIT_UNTIL([grep "000: table simple: i=1" test-ovsdb.log]) $PYTHON3 $srcdir/test-ovsdb.py -t10 idl $abs_srcdir/idltest.ovsschema \ unix:s$target.ovsdb "$txn" > test-ovsdb-py.log 2>&1 & echo $! > test-ovsdb-py.pid OVS_WAIT_UNTIL([grep "000: table simple: i=1" test-ovsdb-py.log]) # Start collecting raft_is_connected logs for $target before shutting down # any servers. tail -f s$target.log > raft_is_connected.log & echo $! > tail.pid # Shutdown the other servers so that $target is disconnected from the cluster. for i in $shutdown; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done # The test-ovsdb should detect the disconnect and retry. OVS_WAIT_UNTIL([grep disconnect test-ovsdb.log]) OVS_WAIT_UNTIL([grep disconnect test-ovsdb-py.log]) # The $target debug log should show raft_is_connected: false. OVS_WAIT_UNTIL([grep "raft_is_connected: false" raft_is_connected.log]) # Save the current count of "raft_is_connected: true" count_old=`grep "raft_is_connected: true" raft_is_connected.log | wc -l` echo count_old $count_old if test X$check_flapping = X"yes"; then sleep 10 fi # Make sure raft_is_connected didn't flap from false to true. count_new=`grep "raft_is_connected: true" raft_is_connected.log | wc -l` echo count_new $count_new AT_CHECK([test $count_new = $count_old]) for i in $cleanup; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done } OVS_END_SHELL_HELPERS AT_SETUP([OVSDB cluster - follower disconnect from cluster, single remote]) AT_KEYWORDS([ovsdb server negative unix cluster disconnect]) ovsdb_test_cluster_disconnect 3 follower AT_CLEANUP AT_SETUP([OVSDB cluster - leader disconnect from cluster, single remote]) AT_KEYWORDS([ovsdb server negative unix cluster disconnect]) ovsdb_test_cluster_disconnect 3 leader AT_CLEANUP AT_SETUP([OVSDB cluster - leader disconnect from cluster, check flapping]) AT_KEYWORDS([ovsdb server negative unix cluster disconnect]) ovsdb_test_cluster_disconnect 5 leader yes AT_CLEANUP AT_SETUP([OVSDB cluster - initial status should be disconnected]) AT_KEYWORDS([ovsdb server negative unix cluster disconnect]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done # Stop all servers, and start the s1 only, to test initial connection status # when there is no leader yet. for i in `seq 1 $n`; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done i=1 AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) # The initial status should be disconnected. So wait should fail. AT_CHECK([ovsdb_client_wait --timeout=1 unix:s$i.ovsdb $schema_name connected], [142], [ignore], [ignore]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) AT_CLEANUP AT_BANNER([OVSDB cluster election timer change]) AT_SETUP([OVSDB cluster - election timer change]) AT_KEYWORDS([ovsdb server positive unix cluster timer]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done # Change not allowed through follower. AT_CHECK([ovs-appctl -t "`pwd`"/s2 cluster/change-election-timer $schema_name 2000], [2], [], [ignore]) # Timer cannot be changed to bigger than 2x the original value. AT_CHECK([ovs-appctl -t "`pwd`"/s1 cluster/change-election-timer $schema_name 4000], [2], [], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/s1 cluster/change-election-timer $schema_name 2000], [0], [dnl change of election timer initiated. ], []) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s1 cluster/status $schema_name | grep "Election timer: 2000"]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s2 cluster/status $schema_name | grep "Election timer: 2000"]) AT_CHECK([ovs-appctl -t "`pwd`"/s1 cluster/change-election-timer $schema_name 4000], [0], [dnl change of election timer initiated. ], []) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s1 cluster/status $schema_name | grep "Election timer: 4000"]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s2 cluster/status $schema_name | grep "Election timer: 4000"]) # Latest timer should be used after restart for i in `seq $n`; do printf "\ns$i: stopping\n" OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s1 cluster/status $schema_name | grep "Election timer: 4000"]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s2 cluster/status $schema_name | grep "Election timer: 4000"]) # Wait until cluster is ready for i in `seq $n`; do OVS_WAIT_WHILE([ovs-appctl -t "`pwd`"/s$i cluster/status $schema_name | grep "Leader: unknown"]) done # Latest timer should be restored after DB compact and restart. # This is to test the install_snapshot RPC. # Compact online for i in `seq $n`; do AT_CHECK([ovs-appctl -t "`pwd`"/s$i ovsdb-server/compact]) done for i in `seq $n`; do printf "\ns$i: stopping\n" OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s$i cluster/status $schema_name | grep "Election timer: 4000"]) done # Wait until cluster is ready for i in `seq $n`; do OVS_WAIT_WHILE([ovs-appctl -t "`pwd`"/s$i cluster/status $schema_name | grep "Leader: unknown"]) done # Newly joined member should use latest timer value AT_CHECK([ovsdb-tool join-cluster s4.db $schema_name unix:s4.raft unix:s1.raft]) AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s4.log --pidfile=s4.pid --unixctl=s4 --remote=punix:s4.ovsdb s4.db]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s4 cluster/status $schema_name | grep "Election timer: 4000"]) # for i in `seq 10`; do # ovs-appctl -t "`pwd`"/s4 cluster/status $schema_name # sleep 1 # done AT_CLEANUP AT_BANNER([OVSDB cluster install snapshot RPC]) AT_SETUP([OVSDB cluster - install snapshot RPC]) AT_KEYWORDS([ovsdb server positive unix cluster snapshot]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done AT_CHECK([ovsdb-client transact unix:s1.ovsdb '[["idltest", {"op": "insert", "table": "indexed", "row": {"i": 0}}]]'], [0], [ignore], [ignore]) # Kill one follower (s2) and write some data to cluster, so that the follower is falling behind printf "\ns2: stopping\n" OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s2], [s2.pid]) # Delete "i":0 and readd it to get a different UUID for it. AT_CHECK([ovsdb-client transact unix:s1.ovsdb '[["idltest", {"op": "delete", "table": "indexed", "where": [["i", "==", 0]]}]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:s1.ovsdb '[["idltest", {"op": "insert", "table": "indexed", "row": {"i": 0}}]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:s1.ovsdb '[["idltest", {"op": "insert", "table": "indexed", "row": {"i": 1}}]]'], [0], [ignore], [ignore]) # Compact leader online to generate snapshot AT_CHECK([ovs-appctl -t "`pwd`"/s1 ovsdb-server/compact]) # Start the follower s2 again. AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s2.log --pidfile=s2.pid --unixctl=s2 --remote=punix:s2.ovsdb s2.db]) AT_CHECK([ovsdb_client_wait unix:s2.ovsdb $schema_name connected]) # A client transaction through s2. During this transaction, there will be a # install_snapshot RPC because s2 detects it is behind and s1 doesn't have the # pre_log_index requested by s2 because it is already compacted. # After the install_snapshot RPC process, the transaction through s2 should # succeed. AT_CHECK([ovsdb-client transact unix:s2.ovsdb '[["idltest", {"op": "insert", "table": "indexed", "row": {"i": 2}}]]'], [0], [ignore], [ignore]) # The snapshot should overwrite the in-memory contents of the DB on S2 # without generating any constraint violations. All tree records (0, 1, 2) # should be in the DB at this point. AT_CHECK([ovsdb-client --no-headings dump unix:s2.ovsdb idltest indexed | uuidfilt | sort -k 2], [0], [dnl <0> 0 <1> 1 <2> 2 indexed table ]) for i in `seq $n`; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done AT_CLEANUP AT_BANNER([OVSDB - cluster failure while joining]) AT_SETUP([OVSDB cluster - follower crash while joining]) AT_KEYWORDS([ovsdb server negative unix cluster join]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' dnl Starting followers first, so we can configure them to crash on join. for j in `seq $n`; do i=$(($n + 1 - $j)) AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off dnl --detach --no-chdir --log-file=s$i.log dnl --pidfile=s$i.pid --unixctl=s$i dnl --remote=punix:s$i.ovsdb s$i.db]) if test $i != 1; then OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s$i dnl cluster/failure-test crash-before-sending-install-snapshot-reply dnl | grep -q "engaged"]) fi done dnl Make sure that followers really crashed. for i in `seq 2 $n`; do OVS_WAIT_WHILE([test -s s$i.pid]) done dnl Bring them back. for i in `seq 2 $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off dnl --detach --no-chdir --log-file=s$i.log dnl --pidfile=s$i.pid --unixctl=s$i dnl --remote=punix:s$i.ovsdb s$i.db]) done dnl Make sure that all servers joined the cluster. for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done for i in `seq $n`; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done AT_CLEANUP AT_SETUP([OVSDB cluster - leadership change after replication while joining]) AT_KEYWORDS([ovsdb server negative unix cluster join]) n=5 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=$(ovsdb-tool db-cid s1.db) schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) for i in $(seq 2 $n); do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill $(cat *.pid)' on_exit " for i in \$(ls $(pwd)/s[[0-$n]]); do ovs-appctl --timeout 1 -t \$i cluster/status $schema_name; done " dnl Starting servers one by one asking all exisitng servers to transfer dnl leadership after append reply forcing the joining server to try another dnl one that will also transfer leadership. Since transfer is happening dnl after the servers update is replicated to other servers, one of the dnl other servers will actually commit it. It may be a new leader from dnl one of the old members or the new joining server itself. for i in $(seq $n); do dnl Make sure that all already started servers joined the cluster. for j in $(seq $((i - 1)) ); do AT_CHECK([ovsdb_client_wait unix:s$j.ovsdb $schema_name connected]) done for j in $(seq $((i - 1)) ); do OVS_WAIT_UNTIL([ovs-appctl -t "$(pwd)"/s$j \ cluster/failure-test \ transfer-leadership-after-sending-append-request \ | grep -q "engaged"]) done AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ --detach --no-chdir --log-file=s$i.log \ --pidfile=s$i.pid --unixctl=s$i \ --remote=punix:s$i.ovsdb s$i.db]) done dnl Make sure that all servers joined the cluster. for i in $(seq $n); do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done for i in $(seq $n); do OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) done AT_CLEANUP AT_SETUP([OVSDB cluster - leadership change before replication while joining]) AT_KEYWORDS([ovsdb server negative unix cluster join]) n=5 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=$(ovsdb-tool db-cid s1.db) schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) for i in $(seq 2 $n); do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill $(cat *.pid)' on_exit " for i in \$(ls $(pwd)/s[[0-$n]]); do ovs-appctl --timeout 1 -t \$i cluster/status $schema_name; done " dnl Starting servers one by one asking all exisitng servers to transfer dnl leadership right after starting to add a server. Joining server will dnl need to find a new leader that will also transfer leadership. dnl This will continue until the same server will not become a leader dnl for the second time and will be able to add a new server. for i in $(seq $n); do dnl Make sure that all already started servers joined the cluster. for j in $(seq $((i - 1)) ); do AT_CHECK([ovsdb_client_wait unix:s$j.ovsdb $schema_name connected]) done for j in $(seq $((i - 1)) ); do OVS_WAIT_UNTIL([ovs-appctl -t "$(pwd)"/s$j \ cluster/failure-test \ transfer-leadership-after-starting-to-add \ | grep -q "engaged"]) done AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ --detach --no-chdir --log-file=s$i.log \ --pidfile=s$i.pid --unixctl=s$i \ --remote=punix:s$i.ovsdb s$i.db]) done dnl Make sure that all servers joined the cluster. for i in $(seq $n); do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done for i in $(seq $n); do OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) done AT_CLEANUP AT_BANNER([OVSDB - cluster failure while leaving]) AT_SETUP([OVSDB cluster - leaving the cluster with some servers down]) AT_KEYWORDS([ovsdb server negative unix cluster leave]) AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db \ $top_srcdir/vswitchd/vswitch.ovsschema unix:s1.raft], [0], [], [stderr]) schema_name=$(ovsdb-tool schema-name $top_srcdir/vswitchd/vswitch.ovsschema) for i in 2 3 4 5; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill $(cat *.pid)' on_exit " for i in \$(ls $(pwd)/s[[0-5]]); do ovs-appctl --timeout 1 -t \$i cluster/status $schema_name; done " dnl Starting all the servers. for i in 1 2 3 4 5; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ --detach --no-chdir --log-file=s$i.log \ --pidfile=s$i.pid --unixctl=s$i \ --remote=punix:s$i.ovsdb s$i.db]) done dnl Make sure that all servers joined the cluster. for i in 1 2 3 4 5; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done dnl Make sure the cluster is operational. m4_define([DB_REMOTE], [unix:s1.ovsdb,unix:s2.ovsdb,unix:s3.ovsdb,unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" --no-wait init]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-1], [0], [ignore], [ignore]) dnl Stop servers 1 and 2. OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s1], [s1.pid]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s2], [s2.pid]) dnl Make sure that all remaining servers are functional as a cluster. for i in 3 4 5; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done dnl Make sure the cluster is still operational. m4_define([DB_REMOTE], [unix:s3.ovsdb,unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-2], [0], [ignore], [ignore]) dnl Servers 1 and 2 in a cluster of 5 are down, 3 servers are still alive. dnl Server 3 can't leave, because the NEW configuration will be a cluster of dnl 4 with 2 servers down and it doesn't have a quorum. Try it. dnl The cluster will fall apart until servers 1 or 2 come back to resolve dnl the quorum issue, because servers 4 and 5 will no longer consider 3 dnl to be part of the configuration. AT_CHECK([ovs-appctl -t $(pwd)/s3 cluster/leave $schema_name]) dnl Check that the cluster is not operational. for i in 3 4 5; do OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name \ | grep -qE 'leaving|disconnected']) done dnl Try to commit a transaction, it should not be successful. m4_define([DB_REMOTE], [unix:s3.ovsdb,unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-3], [1], [ignore], [stderr]) dnl Now bring back the server 2. This should allow server 3 to leave. AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ --detach --no-chdir --log-file=s2.log \ --pidfile=s2.pid --unixctl=s2 \ --remote=punix:s2.ovsdb s2.db]) dnl Wait for server 3 to actually leave and stop the server. AT_CHECK([ovsdb_client_wait unix:s3.ovsdb $schema_name removed]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s3], [s3.pid]) dnl Make sure that all remaining servers are functional as a cluster. for i in 2 4 5; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done dnl Make sure the cluster is operational again. m4_define([DB_REMOTE], [unix:s2.ovsdb,unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-4], [0], [ignore], [ignore]) dnl Now we have a cluster of 4 servers (1, 2, 4, 5) with 1 server down. dnl Server 2 should be able to leave, because the NEW configuration will dnl be a cluster of 3 servers with 1 being down and it has a quorum. AT_CHECK([ovs-appctl -t $(pwd)/s2 cluster/leave $schema_name]) dnl Wait for server 2 to actually leave and stop the server. AT_CHECK([ovsdb_client_wait unix:s2.ovsdb $schema_name removed]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s2], [s2.pid]) dnl Make sure the cluster is still operational. m4_define([DB_REMOTE], [unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-5], [0], [ignore], [ignore]) dnl Now we have a cluster of 3 servers (1, 4, 5) with 1 server down. dnl None of the alive servers can leave, because the NEW configuration dnl will be a cluster of 2 with 1 server down and it has no quorum. dnl Request both to leave anyway. for i in 4 5; do AT_CHECK([ovs-appctl -t $(pwd)/s$i cluster/leave $schema_name]) done dnl Check that the cluster is not operational. for i in 4 5; do OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name \ | grep -qE 'leaving|disconnected']) done dnl Try to commit a transaction, it should not be successful. m4_define([DB_REMOTE], [unix:s4.ovsdb,unix:s5.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-leader-only \ --no-wait create QoS type=test-6], [1], [ignore], [stderr]) dnl Now bring back the first server. AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ --detach --no-chdir --log-file=s1.log \ --pidfile=s1.pid --unixctl=s1 \ --remote=punix:s1.ovsdb s1.db]) dnl Now it should be possible for all the other servers to leave, so we dnl should end up with a single-node cluster that consists of server 1. for i in 4 5; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name removed]) done for i in 4 5; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) done dnl Wait for the first server to become a leader of a single-node cluster. OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s1 cluster/status $schema_name \ | grep -q 'Role: leader']) AT_CHECK([ovs-appctl -t $(pwd)/s1 cluster/status $schema_name \ | grep -c ' s[[1-5]] '], [0], [dnl 1 ]) dnl Check that the database is operational and the data is still in there. m4_define([DB_REMOTE], [unix:s1.ovsdb]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" -vovsdb_cs:console:dbg --no-wait \ create QoS type=test-7], [0], [ignore], [ignore]) AT_CHECK([ovs-vsctl --db="DB_REMOTE" --no-wait \ --columns=type --bare list QoS | sed '/^$/d' | sort], [0], [dnl test-1 test-2 test-4 test-5 test-7 ]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s1], [s1.pid]) AT_CLEANUP OVS_START_SHELL_HELPERS # ovsdb_cluster_failure_test SCHEMA_FUNC OUTPUT TRANSACTION... ovsdb_cluster_failure_test () { # Initial state: s1 is leader, s2 and s3 are followers remote_1=$1 remote_2=$2 crash_node=$3 crash_command=$4 if test "$crash_node" = "1"; then new_leader=$5 fi log_grep=$6 cp $top_srcdir/vswitchd/vswitch.ovsschema schema schema=`ovsdb-tool schema-name schema` AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr]) AT_CHECK([sed < stderr "/ovsdb|WARN|schema: changed .* columns in 'Open_vSwitch' database from ephemeral to persistent/d"]) n=3 join_cluster() { local i=$1 others= for j in `seq 1 $n`; do if test $i != $j; then others="$others unix:s$j.raft" fi done AT_CHECK([ovsdb-tool join-cluster s$i.db $schema unix:s$i.raft $others]) } start_server() { local i=$1 printf "\ns$i: starting\n" AT_CHECK([ovsdb-server -vjsonrpc -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) } connect_server() { local i=$1 printf "\ns$i: waiting to connect to storage\n" AT_CHECK([ovsdb_client_wait --log-file=connect$i.log unix:s$i.ovsdb $schema connected]) } cid=`ovsdb-tool db-cid s1.db` for i in `seq 2 $n`; do join_cluster $i; done on_exit 'kill `cat *.pid`' for i in `seq $n`; do start_server $i; done for i in `seq $n`; do connect_server $i; done db=unix:s$remote_1.ovsdb,unix:s$remote_2.ovsdb # To ensure $new_leader node the new leader, we delay election timer for # the other follower. if test -n "$new_leader"; then if test "$new_leader" = "2"; then delay_election_node=3 else delay_election_node=2 fi AT_CHECK([ovs-appctl -t "`pwd`"/s$delay_election_node cluster/failure-test delay-election], [0], [ignore]) fi # Initializing the database separately to avoid extra 'wait' operation # in later transactions. AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait init], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/s$crash_node cluster/failure-test $crash_command], [0], [ignore]) AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait create QoS type=x], [0], [ignore], [ignore]) # Make sure that the node really crashed or has specific log message. if test -z "$log_grep"; then AT_CHECK([ls s$crash_node.ovsdb], [2], [ignore], [ignore]) # XXX: Client will fail if remotes contains unix socket that doesn't exist (killed). if test "$remote_1" = "$crash_node"; then db=unix:s$remote_2.ovsdb fi else OVS_WAIT_UNTIL([grep -q "$log_grep" s${crash_node}.log]) fi AT_CHECK([ovs-vsctl --db="$db" --no-leader-only --no-wait --columns=type --bare list QoS], [0], [x ]) } OVS_END_SHELL_HELPERS AT_BANNER([OVSDB - cluster failure with pending transaction]) AT_SETUP([OVSDB cluster - txn on follower-2, leader crash before sending appendReq, follower-2 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-before-sending-append-request 2 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, leader crash before sending appendReq, follower-3 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-before-sending-append-request 3 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, leader crash before sending execRep, follower-2 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-before-sending-execute-command-reply 2 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, leader crash before sending execRep, follower-3 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-before-sending-execute-command-reply 3 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, leader crash after sending execRep, follower-2 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-after-sending-execute-command-reply 2 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, leader crash after sending execRep, follower-3 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 1 crash-after-sending-execute-command-reply 3 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, leader crash before sending appendReq, follower-2 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 1 2 1 crash-before-sending-append-request 2 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, leader crash before sending appendReq, follower-3 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 1 2 1 crash-before-sending-append-request 3 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, leader crash after sending appendReq, follower-2 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) # XXX: Detect and skip repeated transaction before enabling this test AT_CHECK([exit 77]) ovsdb_cluster_failure_test 1 2 1 crash-after-sending-append-request 2 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, leader crash after sending appendReq, follower-3 becomes leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) # XXX: Detect and skip repeated transaction before enabling this test AT_CHECK([exit 77]) ovsdb_cluster_failure_test 1 2 1 crash-after-sending-append-request 3 AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, follower-2 crash before sending execReq, reconnect to follower-3]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 3 2 crash-before-sending-execute-command-request AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, follower-2 crash before sending execReq, reconnect to leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 1 2 crash-before-sending-execute-command-request AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, follower-2 crash after sending execReq, reconnect to follower-3]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) # XXX: Detect and skip repeated transaction before enabling this test AT_CHECK([exit 77]) ovsdb_cluster_failure_test 2 3 2 crash-after-sending-execute-command-request AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, follower-2 crash after sending execReq, reconnect to leader]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) # XXX: Detect and skip repeated transaction before enabling this test AT_CHECK([exit 77]) ovsdb_cluster_failure_test 2 1 2 crash-after-sending-execute-command-request AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, follower-2 crash after receiving appendReq for the update]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 1 1 2 crash-after-receiving-append-request-update AT_CLEANUP AT_SETUP([OVSDB cluster - txn on follower-2, follower-3 crash after receiving appendReq for the update]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn]) ovsdb_cluster_failure_test 2 2 3 crash-after-receiving-append-request-update AT_CLEANUP AT_SETUP([OVSDB cluster - txn on leader, leader transfers leadership after sending appendReq]) AT_KEYWORDS([ovsdb server negative unix cluster pending-txn transfer]) ovsdb_cluster_failure_test 1 2 1 transfer-leadership-after-sending-append-request -1 "Transferring leadership" AT_CLEANUP AT_SETUP([OVSDB cluster - competing candidates]) AT_KEYWORDS([ovsdb server negative unix cluster competing-candidates]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=`ovsdb-tool db-cid s1.db` schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema` for i in `seq 2 $n`; do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill `cat *.pid`' for i in `seq $n`; do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) done for i in `seq $n`; do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done # We need to simulate the situation when 2 candidates starts election with same # term. # # Before triggering leader election, tell follower s2 don't send vote request (simulating # vote-request lost or not handled in time), and tell follower s3 to delay # election timer to make sure s3 doesn't send vote-request before s2 enters # term 2. AT_CHECK([ovs-appctl -t "`pwd`"/s2 cluster/failure-test dont-send-vote-request], [0], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/s3 cluster/failure-test delay-election], [0], [ignore]) # Restart leader, which will become follower, and both old followers will start # election as candidate. The new follower (old leader) will vote one of them, # and the other candidate should step back as follower as again. kill -9 `cat s1.pid` AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s1.log --pidfile=s1.pid --unixctl=s1 --remote=punix:s1.ovsdb s1.db]) # Tell s1 to delay election timer so that it won't start election before s3 # becomes candidate. AT_CHECK([ovs-appctl -t "`pwd`"/s1 cluster/failure-test delay-election], [0], [ignore]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s1 cluster/status $schema_name | grep "Term: 2"]) for i in `seq $n`; do OVS_WAIT_WHILE([ovs-appctl -t "`pwd`"/s$i cluster/status $schema_name | grep "candidate"]) AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done for i in `seq $n`; do OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) done AT_CLEANUP AT_SETUP([OVSDB cluster - disruptive server]) AT_KEYWORDS([ovsdb server negative unix cluster disruptive]) n=3 AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster \ s1.db $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) cid=$(ovsdb-tool db-cid s1.db) schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) for i in $(seq 2 $n); do AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill $(cat *.pid)' for i in $(seq $n); do AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off --detach --no-chdir \ --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i \ --remote=punix:s$i.ovsdb s$i.db]) done for i in $(seq $n); do AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) done # An unstable follower shouldn't disrupt the healthy cluster - shouldn't # trigger term change. AT_CHECK([ovs-appctl -t $(pwd)/s2 cluster/failure-test stop-raft-rpc], [0], [ignore]) OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s2 cluster/status $schema_name | grep "Role: candidate"]) AT_CHECK([ovs-appctl -t $(pwd)/s2 cluster/failure-test clear], [0], [ignore]) # Should step back to follower. OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s2 cluster/status $schema_name | grep "Role: follower"]) # No term change. for i in $(seq $n); do AT_CHECK([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name | grep "Term: 1"], [0], [ignore]) done # Now force election without pausing RPC, so the requests are actually sent. AT_CHECK([ovs-appctl -t $(pwd)/s2 cluster/failure-test force-election], [0], [ignore]) # The server transitions into a candidate role while engaging the test # and shortly after it should step back to be a follower. OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s2 cluster/status $schema_name | grep "Role: follower"]) # No term change. for i in $(seq $n); do AT_CHECK([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name | grep "Term: 1"], [0], [ignore]) done for i in $(seq $n); do OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) done AT_CLEANUP AT_BANNER([OVSDB - cluster tests]) # Torture test. OVS_START_SHELL_HELPERS ovsdb_torture_test () { local n=$1 # Number of cluster members local victim=$2 # Cluster member to kill or remove local variant=$3 # 'kill' and restart or 'remove' and add cp $top_srcdir/vswitchd/vswitch.ovsschema schema schema=`ovsdb-tool schema-name schema` AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr]) AT_CHECK([sed < stderr "/ovsdb|WARN|schema: changed .* columns in 'Open_vSwitch' database from ephemeral to persistent/d"]) join_cluster() { local i=$1 others= for j in `seq 1 $n`; do if test $i != $j; then others="$others unix:s$j.raft" fi done AT_CHECK([ovsdb-tool join-cluster s$i.db $schema unix:s$i.raft $others]) } start_server() { local i=$1 printf "\ns$i: starting\n" AT_CHECK([ovsdb-server -vjsonrpc -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db]) } stop_server() { local i=$1 printf "\ns$i: stopping\n" OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid]) } connect_server() { local i=$1 printf "\ns$i: waiting to connect to storage\n" AT_CHECK([ovsdb_client_wait --log-file=connect$i.log unix:s$i.ovsdb $schema connected]) } remove_server() { local i=$1 printf "\ns$i: removing from cluster\n" AT_CHECK([ovs-appctl -t "`pwd`"/s$i cluster/leave Open_vSwitch]) printf "\ns$i: waiting for removal to complete\n" AT_CHECK([ovsdb_client_wait --log-file=remove$i.log unix:s$i.ovsdb $schema removed]) stop_server $i } add_server() { local i=$1 rm s$i.db join_cluster $i start_server $i connect_server $i } cid=`ovsdb-tool db-cid s1.db` for i in `seq 2 $n`; do join_cluster $i; done on_exit 'kill `cat *.pid`' for i in `seq $n`; do start_server $i; done for i in `seq $n`; do connect_server $i; done db=unix:s1.ovsdb for i in `seq 2 $n`; do db=$db,unix:s$i.ovsdb done n1=10 n2=5 n3=50 echo "starting $n1*$n2 ovs-vsctl processes..." for i in $(seq 0 $(expr $n1 - 1) ); do (for j in $(seq $n2); do : > $i-$j.running txn="add Open_vSwitch . external_ids $i-$j=$i-$j" for k in $(seq $n3); do txn="$txn -- add Open_vSwitch . external_ids $i-$j-$k=$i-$j-$k" done run_as "ovs-vsctl($i-$j)" ovs-vsctl "-vPATTERN:console:ovs-vsctl($i-$j)|%D{%H:%M:%S}|%05N|%c|%p|%m" --log-file=$i-$j.log -vfile -vsyslog:off -vtimeval:off --timeout=120 --db="$db" --no-leader-only --no-wait $txn status=$? if test $status != 0; then echo "$i-$j exited with status $status" > $i-$j:$status fi rm $i-$j.running done : > $i.done)& done echo "...done" echo "waiting for ovs-vsctl processes to exit..." # Use file instead of var because code inside "while" runs in a subshell. echo 0 > phase i=0 (while :; do echo || exit 0; sleep 0.1; done) | while read REPLY; do printf "t=%2d s:" $i done=0 for j in $(seq 0 $(expr $n1 - 1)); do if test -f $j.done; then printf " $j" done=$(expr $done + 1) fi done printf '\n' if test $done = $n1; then break fi case $(cat phase) in # ( 0) if test $done -ge $(expr $n1 / 10); then if test $variant = kill; then stop_server $victim else remove_server $victim fi echo 1 > phase next=$(expr $i + 2) fi ;; # ( 1) if test $i -ge $next; then if test $variant = kill; then start_server $victim connect_server $victim else add_server $victim fi echo 2 > phase fi ;; esac i=$(expr $i + 1) done echo "...done" AT_CHECK([if test $(cat phase) != 2; then exit 77; fi]) for i in $(seq 0 $(expr $n1 - 1) ); do for j in `seq $n2`; do echo "$i-$j=$i-$j" for k in `seq $n3`; do echo "$i-$j-$k=$i-$j-$k" done done done | sort > expout AT_CHECK([ovs-vsctl --db="$db" --no-wait --log-file=finalize.log -vtimeval:off -vfile -vsyslog:off --bare get Open_vSwitch . external-ids | tr ',' '\n' | sed 's/[[{}"" ]]//g' | sort], [0], [expout]) for i in `seq $n`; do if test $i != $victim || test $(cat phase) != 1; then stop_server $i fi done # We ignore stdout because non-fatal warnings get printed there. AT_CHECK([ovsdb-tool check-cluster s*.db], [0], [ignore]) } OVS_END_SHELL_HELPERS AT_SETUP([OVSDB 3-server torture test - kill/restart leader]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 1 kill AT_CLEANUP AT_SETUP([OVSDB 3-server torture test - kill/restart follower 1]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 2 kill AT_CLEANUP AT_SETUP([OVSDB 3-server torture test - kill/restart follower 2]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 3 kill AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - kill/restart leader]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 1 kill AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - kill/restart follower 1]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 2 kill AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - kill/restart follower 2]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 3 kill AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - kill/restart follower 3]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 4 kill AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - kill/restart follower 4]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 5 kill AT_CLEANUP AT_SETUP([OVSDB 3-server torture test - remove/re-add leader]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 1 remove AT_CLEANUP AT_SETUP([OVSDB 3-server torture test - remove/re-add follower 1]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 2 remove AT_CLEANUP AT_SETUP([OVSDB 3-server torture test - remove/re-add follower 2]) AT_KEYWORDS([ovsdb server positive unix cluster cluster3]) ovsdb_torture_test 3 3 remove AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - remove/re-add leader]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 1 remove AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - remove/re-add follower 1]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 2 remove AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - remove/re-add follower 2]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 3 remove AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - remove/re-add follower 3]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 4 remove AT_CLEANUP AT_SETUP([OVSDB 5-server torture test - remove/re-add follower 4]) AT_KEYWORDS([ovsdb server positive unix cluster cluster5]) ovsdb_torture_test 5 5 remove AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-column.at000066400000000000000000000006771514270232600230720ustar00rootroot00000000000000AT_BANNER([OVSDB -- columns]) OVSDB_CHECK_POSITIVE_CPY([ordinary column], [[parse-column mycol '{"type": "integer"}']], [[{"type":"integer"}]]) OVSDB_CHECK_POSITIVE_CPY([immutable column], [[parse-column mycol '{"type": "real", "mutable": false}']], [[{"mutable":false,"type":"real"}]]) OVSDB_CHECK_POSITIVE_CPY([ephemeral column], [[parse-column mycol '{"type": "uuid", "ephemeral": true}']], [[{"ephemeral":true,"type":"uuid"}]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-condition.at000066400000000000000000001220121514270232600235470ustar00rootroot00000000000000AT_BANNER([OVSDB -- conditions]) OVSDB_CHECK_POSITIVE([null condition], [[parse-conditions \ '{"columns": {"name": {"type": "string"}}}' \ '[]']], [[[]]]) OVSDB_CHECK_POSITIVE([conditions on scalars], [[parse-conditions \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[["i", "==", 0]]' \ '[["i", "!=", 1]]' \ '[["i", "<", 2]]' \ '[["i", "<=", 3]]' \ '[["i", ">", 4]]' \ '[["i", ">=", 5]]' \ '[["i", "includes", 6]]' \ '[["i", "excludes", 7]]' \ '[["r", "==", 0.5]]' \ '[["r", "!=", 1.5]]' \ '[["r", "<", 2.5]]' \ '[["r", "<=", 3.5]]' \ '[["r", ">", 4.5]]' \ '[["r", ">=", 5.5]]' \ '[["r", "includes", 6.5]]' \ '[["r", "excludes", 7.5]]' \ '[["b", "==", true]]' \ '[["b", "!=", false]]' \ '[["b", "includes", false]]' \ '[["b", "excludes", true]]' \ '[["s", "==", "a"]]' \ '[["s", "!=", "b"]]' \ '[["s", "includes", "c"]]' \ '[["s", "excludes", "d"]]' \ '[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \ '[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "includes", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \ '[["u", "excludes", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']], [[[["i","==",0]] [["i","!=",1]] [["i","<",2]] [["i","<=",3]] [["i",">",4]] [["i",">=",5]] [["i","includes",6]] [["i","excludes",7]] [["r","==",0.5]] [["r","!=",1.5]] [["r","<",2.5]] [["r","<=",3.5]] [["r",">",4.5]] [["r",">=",5.5]] [["r","includes",6.5]] [["r","excludes",7.5]] [["b","==",true]] [["b","!=",false]] [["b","includes",false]] [["b","excludes",true]] [["s","==","a"]] [["s","!=","b"]] [["s","includes","c"]] [["s","excludes","d"]] [["u","==",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]] [["u","!=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]] [["u","includes",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]] [["u","excludes",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]]]], [condition]) AT_SETUP([disallowed conditions on scalars]) AT_KEYWORDS([ovsdb negative condition]) AT_CHECK([[test-ovsdb parse-conditions \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[["b", ">", true]]' \ '[["b", ">=", false]]' \ '[["b", "<", false]]' \ '[["b", "<=", false]]' \ '[["s", ">", "a"]]' \ '[["s", ">=", "b"]]' \ '[["s", "<", "c"]]' \ '[["s", "<=", "d"]]' \ '[["u", ">", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \ '[["u", ">=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "<", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \ '[["u", "<=", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]' \ '[["i", "==", ["set", []]]]' \ '[["i", "!=", ["set", []]]]' \ '[["i", ">", ["set", []]]]' \ '[["i", ">=", ["set", []]]]' \ '[["i", "<", ["set", []]]]' \ '[["i", "<=", ["set", []]]]' \ '[["i", "includes", ["set", []]]]' \ '[["i", "excludes", ["set", []]]]' \ '[["i", ">", ["set", []]]]' \ '[["i", "==", ["set", []]]]' \ '[["r", "==", ["set", []]]]' \ '[["r", "!=", ["set", []]]]' \ '[["r", ">", ["set", []]]]' \ '[["r", ">=", ["set", []]]]' \ '[["r", "<", ["set", []]]]' \ '[["r", "<=", ["set", []]]]' \ '[["r", "includes", ["set", []]]]' \ '[["r", "excludes", ["set", []]]]' \ '[["r", ">", ["set", []]]]' \ '[["r", "==", ["set", []]]]' \ '[["b", "==", ["set", []]]]' \ '[["b", "!=", ["set", []]]]' \ '[["b", ">", ["set", []]]]' \ '[["b", ">=", ["set", []]]]' \ '[["b", "<", ["set", []]]]' \ '[["b", "<=", ["set", []]]]' \ '[["b", "includes", ["set", []]]]' \ '[["b", "excludes", ["set", []]]]' \ '[["b", ">", ["set", []]]]' \ '[["b", "==", ["set", []]]]' \ '[["s", "==", ["set", []]]]' \ '[["s", "!=", ["set", []]]]' \ '[["s", ">", ["set", []]]]' \ '[["s", ">=", ["set", []]]]' \ '[["s", "<", ["set", []]]]' \ '[["s", "<=", ["set", []]]]' \ '[["s", "includes", ["set", []]]]' \ '[["s", "excludes", ["set", []]]]' \ '[["s", ">", ["set", []]]]' \ '[["s", "==", ["set", []]]]' \ '[["u", "==", ["set", []]]]' \ '[["u", "!=", ["set", []]]]' \ '[["u", ">", ["set", []]]]' \ '[["u", ">=", ["set", []]]]' \ '[["u", "<", ["set", []]]]' \ '[["u", "<=", ["set", []]]]' \ '[["u", "includes", ["set", []]]]' \ '[["u", "excludes", ["set", []]]]' \ '[["u", ">", ["set", []]]]' \ '[["u", "==", ["set", []]]]' \ ]], [1], [], [[test-ovsdb: syntax "["b",">",true]": syntax error: Type mismatch: ">" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b",">=",false]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","<",false]": syntax error: Type mismatch: "<" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","<=",false]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["s",">","a"]": syntax error: Type mismatch: ">" operator may not be applied to column s of type string. test-ovsdb: syntax "["s",">=","b"]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","<","c"]": syntax error: Type mismatch: "<" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","<=","d"]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type string. test-ovsdb: syntax "["u",">",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u",">=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","<",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","<=",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["b",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["b",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["s",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column s of type string. test-ovsdb: syntax "["s",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type string. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["s",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column s of type string. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["u",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["u",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present ]]) AT_CLEANUP OVSDB_CHECK_POSITIVE([conditions on optional scalars], [[parse-conditions \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": 1}}, "r": {"type": {"key": "real", "min": 0, "max": 1}}, "b": {"type": {"key": "boolean", "min": 0, "max": 1}}, "s": {"type": {"key": "string", "min": 0, "max": 1}}, "u": {"type": {"key": "uuid", "min": 0, "max": 1}}}}' \ '[["i", "==", 0]]' \ '[["i", "!=", 1]]' \ '[["i", "<", 2]]' \ '[["i", "<=", 3]]' \ '[["i", ">", 4]]' \ '[["i", ">=", 5]]' \ '[["i", "includes", 6]]' \ '[["i", "excludes", 7]]' \ '[["r", "==", 0.5]]' \ '[["r", "!=", 1.5]]' \ '[["r", "<", 2.5]]' \ '[["r", "<=", 3.5]]' \ '[["r", ">", 4.5]]' \ '[["r", ">=", 5.5]]' \ '[["r", "includes", 6.5]]' \ '[["r", "excludes", 7.5]]' \ '[["b", "==", true]]' \ '[["b", "!=", false]]' \ '[["b", "includes", false]]' \ '[["b", "excludes", true]]' \ '[["s", "==", "a"]]' \ '[["s", "!=", "b"]]' \ '[["s", "includes", "c"]]' \ '[["s", "excludes", "d"]]' \ '[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \ '[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "includes", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \ '[["u", "excludes", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']], [[[["i","==",0]] [["i","!=",1]] [["i","<",2]] [["i","<=",3]] [["i",">",4]] [["i",">=",5]] [["i","includes",6]] [["i","excludes",7]] [["r","==",0.5]] [["r","!=",1.5]] [["r","<",2.5]] [["r","<=",3.5]] [["r",">",4.5]] [["r",">=",5.5]] [["r","includes",6.5]] [["r","excludes",7.5]] [["b","==",true]] [["b","!=",false]] [["b","includes",false]] [["b","excludes",true]] [["s","==","a"]] [["s","!=","b"]] [["s","includes","c"]] [["s","excludes","d"]] [["u","==",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]] [["u","!=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]] [["u","includes",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]] [["u","excludes",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]]]], [condition]) AT_SETUP([disallowed conditions on optional scalars]) AT_KEYWORDS([ovsdb negative condition]) AT_CHECK([[test-ovsdb parse-conditions \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": 1}}, "r": {"type": {"key": "real", "min": 0, "max": 1}}, "b": {"type": {"key": "boolean", "min": 0, "max": 1}}, "s": {"type": {"key": "string", "min": 0, "max": 1}}, "u": {"type": {"key": "uuid", "min": 0, "max": 1}}}}' \ '[["b", ">", true]]' \ '[["b", ">=", false]]' \ '[["b", "<", false]]' \ '[["b", "<=", false]]' \ '[["s", ">", "a"]]' \ '[["s", ">=", "b"]]' \ '[["s", "<", "c"]]' \ '[["s", "<=", "d"]]' \ '[["u", ">", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \ '[["u", ">=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "<", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \ '[["u", "<=", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]' \ '[["i", ">", ["set", []]]]' \ '[["i", ">=", ["set", []]]]' \ '[["i", "<", ["set", []]]]' \ '[["i", "<=", ["set", []]]]' \ '[["i", ">", ["set", []]]]' \ '[["r", ">", ["set", []]]]' \ '[["r", ">=", ["set", []]]]' \ '[["r", "<", ["set", []]]]' \ '[["r", "<=", ["set", []]]]' \ '[["r", ">", ["set", []]]]' \ '[["b", ">", ["set", []]]]' \ '[["b", ">=", ["set", []]]]' \ '[["b", "<", ["set", []]]]' \ '[["b", "<=", ["set", []]]]' \ '[["b", ">", ["set", []]]]' \ '[["s", ">", ["set", []]]]' \ '[["s", ">=", ["set", []]]]' \ '[["s", "<", ["set", []]]]' \ '[["s", "<=", ["set", []]]]' \ '[["s", ">", ["set", []]]]' \ '[["u", ">", ["set", []]]]' \ '[["u", ">=", ["set", []]]]' \ '[["u", "<", ["set", []]]]' \ '[["u", "<=", ["set", []]]]' \ '[["u", ">", ["set", []]]]' \ ]], [1], [], [[test-ovsdb: syntax "["b",">",true]": syntax error: Type mismatch: ">" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b",">=",false]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b","<",false]": syntax error: Type mismatch: "<" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b","<=",false]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["s",">","a"]": syntax error: Type mismatch: ">" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s",">=","b"]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s","<","c"]": syntax error: Type mismatch: "<" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s","<=","d"]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["u",">",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u",">=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u","<",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u","<=",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["set",[]]": syntax error: set must have exactly one member but 0 are present test-ovsdb: syntax "["b",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["b",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column b of type set of up to 1 booleans. test-ovsdb: syntax "["s",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["s",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column s of type set of up to 1 strings. test-ovsdb: syntax "["u",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u",">=",["set",[]]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u","<",["set",[]]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u","<=",["set",[]]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type set of up to 1 uuids. test-ovsdb: syntax "["u",">",["set",[]]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type set of up to 1 uuids. ]]) AT_CLEANUP OVSDB_CHECK_POSITIVE([conditions on sets], [[parse-conditions \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}, "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}}, "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}, "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \ '[["i", "==", ["set", []]]]' \ '[["i", "!=", ["set", [1]]]]' \ '[["i", "includes", ["set", [1, 2]]]]' \ '[["i", "excludes", ["set", [1, 2, 3]]]]' \ '[["r", "==", ["set", []]]]' \ '[["r", "!=", ["set", [1.5]]]]' \ '[["r", "includes", ["set", [1.5, 2.5]]]]' \ '[["r", "excludes", ["set", [1.5, 2.5, 3.5]]]]' \ '[["b", "==", ["set", [true]]]]' \ '[["b", "!=", ["set", [false]]]]' \ '[["b", "includes", ["set", [false]]]]' \ '[["b", "excludes", ["set", [true, false]]]]' \ '[["s", "==", ["set", ["a"]]]]' \ '[["s", "!=", ["set", ["a", "b"]]]]' \ '[["s", "includes", ["set", ["c"]]]]' \ '[["s", "excludes", ["set", ["c", "d"]]]]' \ '[["u", "==", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]' \ '[["u", "==", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"], ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]]]' \ '[["u", "includes", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"], ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"], ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]]' \ '[["u", "excludes", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"], ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"], ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"], ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]]]' \ ]], [[[["i","==",["set",[]]]] [["i","!=",1]] [["i","includes",["set",[1,2]]]] [["i","excludes",["set",[1,2,3]]]] [["r","==",["set",[]]]] [["r","!=",1.5]] [["r","includes",["set",[1.5,2.5]]]] [["r","excludes",["set",[1.5,2.5,3.5]]]] [["b","==",true]] [["b","!=",false]] [["b","includes",false]] [["b","excludes",["set",[false,true]]]] [["s","==","a"]] [["s","!=",["set",["a","b"]]]] [["s","includes","c"]] [["s","excludes",["set",["c","d"]]]] [["u","==",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]] [["u","==",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]] [["u","includes",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]] [["u","excludes",["set",[["uuid","62315898-64e0-40b9-b26f-ff74225303e6"],["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]]], [condition]) OVSDB_CHECK_POSITIVE([condition sorting], [[parse-conditions \ '{"columns": {"i": {"type": "integer"}}}' \ '[["i", "excludes", 7], ["i", "!=", 8], ["i", "==", 1], ["i", "includes", 2], ["i", "<=", 3], ["i", "<", 4], ["i", ">", 6], ["i", ">=", 5], ["_uuid", "==", ["uuid", "d50e85c6-8ae7-4b16-b69e-4395928bd9be"]]]']], [[[["_uuid","==",["uuid","d50e85c6-8ae7-4b16-b69e-4395928bd9be"]],["i","==",1],["i","includes",2],["i","<=",3],["i","<",4],["i",">=",5],["i",">",6],["i","excludes",7],["i","!=",8]]]]) OVSDB_CHECK_POSITIVE([boolean condition], [[parse-conditions \ '{"columns": {"name": {"type": "string"}}}' \ '[true]']], [[[true]]]) OVSDB_CHECK_POSITIVE([boolean condition], [[parse-conditions \ '{"columns": {"name": {"type": "string"}}}' \ '[false]']], [[[false]]]) OVSDB_CHECK_POSITIVE([evaluating null condition], [[evaluate-conditions \ '{"columns": {"i": {"type": "integer"}}}' \ '[[]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [condition 0: TTT]) OVSDB_CHECK_POSITIVE([evaluating conditions on integers], [[evaluate-conditions \ '{"columns": {"i": {"type": "integer"}}}' \ '[[["i", "<", 1]], [["i", "<=", 1]], [["i", "==", 1]], [["i", "!=", 1]], [["i", ">=", 1]], [["i", ">", 1]], [["i", "includes", 1]], [["i", "excludes", 1]], [["i", ">", 0], ["i", "<", 2]]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [condition 0: T-- condition 1: TT- condition 2: -T- condition 3: T-T condition 4: -TT condition 5: --T condition 6: -T- condition 7: T-T condition 8: -T-], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on reals], [[evaluate-conditions \ '{"columns": {"r": {"type": "real"}}}' \ '[[["r", "<", 5.0]], [["r", "<=", 5.0]], [["r", "==", 5.0]], [["r", "!=", 5.0]], [["r", ">=", 5.0]], [["r", ">", 5.0]], [["r", "includes", 5.0]], [["r", "excludes", 5.0]], [["r", "!=", 0], ["r", "!=", 5.1]]]' \ '[{"r": 0}, {"r": 5.0}, {"r": 5.1}']]], [condition 0: T-- condition 1: TT- condition 2: -T- condition 3: T-T condition 4: -TT condition 5: --T condition 6: -T- condition 7: T-T condition 8: -T-], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on booleans], [[evaluate-conditions \ '{"columns": {"b": {"type": "boolean"}}}' \ '[[["b", "==", true]], [["b", "!=", true]], [["b", "includes", true]], [["b", "excludes", true]], [["b", "==", false]], [["b", "!=", false]], [["b", "includes", false]], [["b", "excludes", false]], [["b", "==", true], ["b", "==", false]]]' \ '[{"b": true}, {"b": false}']]], [condition 0: T- condition 1: -T condition 2: T- condition 3: -T condition 4: -T condition 5: T- condition 6: -T condition 7: T- condition 8: --], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on strings], [[evaluate-conditions \ '{"columns": {"s": {"type": "string"}}}' \ '[[["s", "==", ""]], [["s", "!=", ""]], [["s", "includes", ""]], [["s", "excludes", ""]], [["s", "==", "foo"]], [["s", "!=", "foo"]], [["s", "includes", "foo"]], [["s", "excludes", "foo"]], [["s", "!=", "foo"], ["s", "!=", ""]]]' \ '[{"s": ""}, {"s": "foo"}, {"s": "xxx"}']]], [condition 0: T-- condition 1: -TT condition 2: T-- condition 3: -TT condition 4: -T- condition 5: T-T condition 6: -T- condition 7: T-T condition 8: --T], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on UUIDs], [[evaluate-conditions \ '{"columns": {"u": {"type": "uuid"}}}' \ '[[["u", "==", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]], [["u", "!=", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]], [["u", "includes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]], [["u", "excludes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]], [["u", "==", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]], [["u", "!=", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]], [["u", "includes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]], [["u", "excludes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]], [["u", "!=", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]], ["u", "!=", ["uuid", "cb160ed6-92a6-4503-a6aa-a09a09e01f0d"]]]]' \ '[{"u": ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]}, {"u": ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]}, {"u": ["uuid", "00000000-0000-0000-0000-000000000000"]}']]], [condition 0: T-- condition 1: -TT condition 2: T-- condition 3: -TT condition 4: -T- condition 5: T-T condition 6: -T- condition 7: T-T condition 8: T-T], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on sets], [[evaluate-conditions \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \ '[[["i", "==", ["set", []]]], [["i", "==", ["set", [0]]]], [["i", "==", ["set", [1]]]], [["i", "==", ["set", [0, 1]]]], [["i", "==", ["set", [2]]]], [["i", "==", ["set", [2, 0]]]], [["i", "==", ["set", [2, 1]]]], [["i", "==", ["set", [2, 1, 0]]]], [["i", "!=", ["set", []]]], [["i", "!=", ["set", [0]]]], [["i", "!=", ["set", [1]]]], [["i", "!=", ["set", [0, 1]]]], [["i", "!=", ["set", [2]]]], [["i", "!=", ["set", [2, 0]]]], [["i", "!=", ["set", [2, 1]]]], [["i", "!=", ["set", [2, 1, 0]]]], [["i", "includes", ["set", []]]], [["i", "includes", ["set", [0]]]], [["i", "includes", ["set", [1]]]], [["i", "includes", ["set", [0, 1]]]], [["i", "includes", ["set", [2]]]], [["i", "includes", ["set", [2, 0]]]], [["i", "includes", ["set", [2, 1]]]], [["i", "includes", ["set", [2, 1, 0]]]], [["i", "excludes", ["set", []]]], [["i", "excludes", ["set", [0]]]], [["i", "excludes", ["set", [1]]]], [["i", "excludes", ["set", [0, 1]]]], [["i", "excludes", ["set", [2]]]], [["i", "excludes", ["set", [2, 0]]]], [["i", "excludes", ["set", [2, 1]]]], [["i", "excludes", ["set", [2, 1, 0]]]], [["i", "includes", ["set", [0]]], ["i", "includes", ["set", [1]]]]]' \ '[{"i": ["set", []]}, {"i": ["set", [0]]}, {"i": ["set", [1]]}, {"i": ["set", [0, 1]]}, {"i": ["set", [2]]}, {"i": ["set", [2, 0]]}, {"i": ["set", [2, 1]]}, {"i": ["set", [2, 1, 0]]}]']], [dnl condition 0: T---- --- condition 1: -T--- --- condition 2: --T-- --- condition 3: ---T- --- condition 4: ----T --- condition 5: ----- T-- condition 6: ----- -T- condition 7: ----- --T condition 8: -TTTT TTT condition 9: T-TTT TTT condition 10: TT-TT TTT condition 11: TTT-T TTT condition 12: TTTT- TTT condition 13: TTTTT -TT condition 14: TTTTT T-T condition 15: TTTTT TT- condition 16: TTTTT TTT condition 17: -T-T- T-T condition 18: --TT- -TT condition 19: ---T- --T condition 20: ----T TTT condition 21: ----- T-T condition 22: ----- -TT condition 23: ----- --T condition 24: TTTTT TTT condition 25: T-T-T -T- condition 26: TT--T T-- condition 27: T---T --- condition 28: TTTT- --- condition 29: T-T-- --- condition 30: TT--- --- condition 31: T---- --- condition 32: ---T- --T], [condition]) # This is the same as the "set" test except that it adds values, # all of which always match. OVSDB_CHECK_POSITIVE([evaluating conditions on maps (1)], [[evaluate-conditions \ '{"columns": {"i": {"type": {"key": "integer", "value": "boolean", "min": 0, "max": "unlimited"}}}}' \ '[[["i", "==", ["map", []]]], [["i", "==", ["map", [[0, true]]]]], [["i", "==", ["map", [[1, false]]]]], [["i", "==", ["map", [[0, true], [1, false]]]]], [["i", "==", ["map", [[2, true]]]]], [["i", "==", ["map", [[2, true], [0, true]]]]], [["i", "==", ["map", [[2, true], [1, false]]]]], [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "!=", ["map", []]]], [["i", "!=", ["map", [[0, true]]]]], [["i", "!=", ["map", [[1, false]]]]], [["i", "!=", ["map", [[0, true], [1, false]]]]], [["i", "!=", ["map", [[2, true]]]]], [["i", "!=", ["map", [[2, true], [0, true]]]]], [["i", "!=", ["map", [[2, true], [1, false]]]]], [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", []]]], [["i", "includes", ["map", [[0, true]]]]], [["i", "includes", ["map", [[1, false]]]]], [["i", "includes", ["map", [[0, true], [1, false]]]]], [["i", "includes", ["map", [[2, true]]]]], [["i", "includes", ["map", [[2, true], [0, true]]]]], [["i", "includes", ["map", [[2, true], [1, false]]]]], [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "excludes", ["map", []]]], [["i", "excludes", ["map", [[0, true]]]]], [["i", "excludes", ["map", [[1, false]]]]], [["i", "excludes", ["map", [[0, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true]]]]], [["i", "excludes", ["map", [[2, true], [0, true]]]]], [["i", "excludes", ["map", [[2, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", [[0, true]]]], ["i", "includes", ["map", [[1, false]]]]]]' \ '[{"i": ["map", []]}, {"i": ["map", [[0, true]]]}, {"i": ["map", [[1, false]]]}, {"i": ["map", [[0, true], [1, false]]]}, {"i": ["map", [[2, true]]]}, {"i": ["map", [[2, true], [0, true]]]}, {"i": ["map", [[2, true], [1, false]]]}, {"i": ["map", [[2, true], [1, false], [0, true]]]}]']], [dnl condition 0: T---- --- condition 1: -T--- --- condition 2: --T-- --- condition 3: ---T- --- condition 4: ----T --- condition 5: ----- T-- condition 6: ----- -T- condition 7: ----- --T condition 8: -TTTT TTT condition 9: T-TTT TTT condition 10: TT-TT TTT condition 11: TTT-T TTT condition 12: TTTT- TTT condition 13: TTTTT -TT condition 14: TTTTT T-T condition 15: TTTTT TT- condition 16: TTTTT TTT condition 17: -T-T- T-T condition 18: --TT- -TT condition 19: ---T- --T condition 20: ----T TTT condition 21: ----- T-T condition 22: ----- -TT condition 23: ----- --T condition 24: TTTTT TTT condition 25: T-T-T -T- condition 26: TT--T T-- condition 27: T---T --- condition 28: TTTT- --- condition 29: T-T-- --- condition 30: TT--- --- condition 31: T---- --- condition 32: ---T- --T], [condition]) # This is the same as the "set" test except that it adds values, # and those values don't always match. OVSDB_CHECK_POSITIVE([evaluating conditions on maps (2)], [[evaluate-conditions \ '{"columns": {"i": {"type": {"key": "integer", "value": "boolean", "min": 0, "max": "unlimited"}}}}' \ '[[["i", "==", ["map", []]]], [["i", "==", ["map", [[0, true]]]]], [["i", "==", ["map", [[1, false]]]]], [["i", "==", ["map", [[0, true], [1, false]]]]], [["i", "==", ["map", [[2, true]]]]], [["i", "==", ["map", [[2, true], [0, true]]]]], [["i", "==", ["map", [[2, true], [1, false]]]]], [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "!=", ["map", []]]], [["i", "!=", ["map", [[0, true]]]]], [["i", "!=", ["map", [[1, false]]]]], [["i", "!=", ["map", [[0, true], [1, false]]]]], [["i", "!=", ["map", [[2, true]]]]], [["i", "!=", ["map", [[2, true], [0, true]]]]], [["i", "!=", ["map", [[2, true], [1, false]]]]], [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", []]]], [["i", "includes", ["map", [[0, true]]]]], [["i", "includes", ["map", [[1, false]]]]], [["i", "includes", ["map", [[0, true], [1, false]]]]], [["i", "includes", ["map", [[2, true]]]]], [["i", "includes", ["map", [[2, true], [0, true]]]]], [["i", "includes", ["map", [[2, true], [1, false]]]]], [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "excludes", ["map", []]]], [["i", "excludes", ["map", [[0, true]]]]], [["i", "excludes", ["map", [[1, false]]]]], [["i", "excludes", ["map", [[0, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true]]]]], [["i", "excludes", ["map", [[2, true], [0, true]]]]], [["i", "excludes", ["map", [[2, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", [[0, true]]]], ["i", "includes", ["map", [[1, false]]]]]]' \ '[{"i": ["map", []]}, {"i": ["map", [[0, true]]]}, {"i": ["map", [[0, false]]]}, {"i": ["map", [[1, false]]]}, {"i": ["map", [[1, true]]]}, {"i": ["map", [[0, true], [1, false]]]}, {"i": ["map", [[0, true], [1, true]]]}, {"i": ["map", [[2, true]]]}, {"i": ["map", [[2, false]]]}, {"i": ["map", [[2, true], [0, true]]]}, {"i": ["map", [[2, false], [0, true]]]}, {"i": ["map", [[2, true], [1, false]]]}, {"i": ["map", [[2, true], [1, true]]]}, {"i": ["map", [[2, true], [1, false], [0, true]]]}, {"i": ["map", [[2, true], [1, false], [0, false]]]}]']], [dnl condition 0: T---- ----- ----- condition 1: -T--- ----- ----- condition 2: ---T- ----- ----- condition 3: ----- T---- ----- condition 4: ----- --T-- ----- condition 5: ----- ----T ----- condition 6: ----- ----- -T--- condition 7: ----- ----- ---T- condition 8: -TTTT TTTTT TTTTT condition 9: T-TTT TTTTT TTTTT condition 10: TTT-T TTTTT TTTTT condition 11: TTTTT -TTTT TTTTT condition 12: TTTTT TT-TT TTTTT condition 13: TTTTT TTTT- TTTTT condition 14: TTTTT TTTTT T-TTT condition 15: TTTTT TTTTT TTT-T condition 16: TTTTT TTTTT TTTTT condition 17: -T--- TT--T T--T- condition 18: ---T- T---- -T-TT condition 19: ----- T---- ---T- condition 20: ----- --T-T -TTTT condition 21: ----- ----T ---T- condition 22: ----- ----- -T-TT condition 23: ----- ----- ---T- condition 24: TTTTT TTTTT TTTTT condition 25: T-TTT --TT- -TT-T condition 26: TTT-T -TTTT T-T-- condition 27: T-T-T --TT- --T-- condition 28: TTTTT TT-T- T---- condition 29: T-TTT ---T- ----- condition 30: TTT-T -T-T- T---- condition 31: T-T-T ---T- ----- condition 32: ----- T---- ---T-], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on optional integers], [[evaluate-conditions \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": 1}}}}' \ '[[["i", "<", 1]], [["i", "<=", 1]], [["i", "==", 1]], [["i", "!=", 1]], [["i", ">=", 1]], [["i", ">", 1]], [["i", "includes", 1]], [["i", "excludes", 1]], [["i", ">", 0], ["i", "<", 2]], [["i", "==", ["set", []]]], [["i", "!=", ["set", []]]], [["i", "includes", ["set", []]]], [["i", "excludes", ["set", []]]]]' \ '[{"i": ["set", []]}, {"i": ["set", [0]]}, {"i": ["set", [1]]}, {"i": ["set", [2]]}]']], [dnl condition 0: -T-- condition 1: -TT- condition 2: --T- condition 3: TT-T condition 4: --TT condition 5: ---T condition 6: --T- condition 7: TT-T condition 8: --T- condition 9: T--- condition 10: -TTT condition 11: TTTT condition 12: TTTT], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on optional strings], [[evaluate-conditions \ '{"columns": {"s": {"type": {"key": "string", "min": 0, "max": 1}}}}' \ '[[["s", "==", ""]], [["s", "!=", ""]], [["s", "includes", ""]], [["s", "excludes", ""]], [["s", "==", "foo"]], [["s", "!=", "foo"]], [["s", "includes", "foo"]], [["s", "excludes", "foo"]], [["s", "!=", "foo"], ["s", "!=", ""]]]' \ '[{"s": ["set", [""]]}, {"s": ["set", ["foo"]]}, {"s": ["set", ["xxx"]]}, {"s": ["set", []]}]']], [dnl condition 0: T--- condition 1: -TTT condition 2: T--- condition 3: -TTT condition 4: -T-- condition 5: T-TT condition 6: -T-- condition 7: T-TT condition 8: --TT], [condition]) OVSDB_CHECK_POSITIVE([evaluating conditions on optional reals], [[evaluate-conditions \ '{"columns": {"r": {"type": {"key": "real", "min": 0, "max": 1}}}}' \ '[[["r", "<", 5.0]], [["r", "<=", 5.0]], [["r", "==", 5.0]], [["r", "!=", 5.0]], [["r", ">=", 5.0]], [["r", ">", 5.0]], [["r", "includes", 5.0]], [["r", "excludes", 5.0]], [["r", "!=", 0], ["r", "!=", 5.1]], [["r", "==", ["set", []]]], [["r", "!=", ["set", []]]], [["r", "includes", ["set", []]]], [["r", "excludes", ["set", []]]]]' \ '[{"r": ["set", [0]]}, {"r": ["set", [5.0]]}, {"r": ["set", [5.1]]}, {"r": ["set", []]}]']], [dnl condition 0: T--- condition 1: TT-- condition 2: -T-- condition 3: T-TT condition 4: -TT- condition 5: --T- condition 6: -T-- condition 7: T-TT condition 8: -T-T condition 9: ---T condition 10: TTT- condition 11: TTTT condition 12: TTTT], [condition]) OVSDB_CHECK_POSITIVE([evaluating false boolean condition], [[evaluate-conditions-any \ '{"columns": {"i": {"type": "integer"}}}' \ '[[false,["i","==",1]]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [condition 0: -T-]) OVSDB_CHECK_POSITIVE([evaluating true boolean condition], [[evaluate-conditions-any \ '{"columns": {"i": {"type": "integer"}}}' \ '[[true,["i","==",1]]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [condition 0: TTT]) OVSDB_CHECK_POSITIVE([compare condition], [[compare-conditions \ '{"columns": {"i": {"type": "integer"}}}' \ '[[true,["i","==",1],["i","==",2],["i","==",3]], [["i","==",1],["i","==",3],["i","==",2],true], [["i","==",1]], [["i",">=",1]]']]], [condition 0-1: 0 condition 1-2: 1 condition 2-3: -1]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-data.at000066400000000000000000001020201514270232600224670ustar00rootroot00000000000000AT_BANNER([OVSDB -- default values]) OVSDB_CHECK_POSITIVE_CPY([default atoms], [default-atoms], [[integer: OK real: OK boolean: OK string: OK uuid: OK]]) OVSDB_CHECK_POSITIVE_CPY([default data], [default-data], [[key integer, value void, n_min 0: OK key integer, value integer, n_min 0: OK key integer, value real, n_min 0: OK key integer, value boolean, n_min 0: OK key integer, value string, n_min 0: OK key integer, value uuid, n_min 0: OK key real, value void, n_min 0: OK key real, value integer, n_min 0: OK key real, value real, n_min 0: OK key real, value boolean, n_min 0: OK key real, value string, n_min 0: OK key real, value uuid, n_min 0: OK key boolean, value void, n_min 0: OK key boolean, value integer, n_min 0: OK key boolean, value real, n_min 0: OK key boolean, value boolean, n_min 0: OK key boolean, value string, n_min 0: OK key boolean, value uuid, n_min 0: OK key string, value void, n_min 0: OK key string, value integer, n_min 0: OK key string, value real, n_min 0: OK key string, value boolean, n_min 0: OK key string, value string, n_min 0: OK key string, value uuid, n_min 0: OK key uuid, value void, n_min 0: OK key uuid, value integer, n_min 0: OK key uuid, value real, n_min 0: OK key uuid, value boolean, n_min 0: OK key uuid, value string, n_min 0: OK key uuid, value uuid, n_min 0: OK key integer, value void, n_min 1: OK key integer, value integer, n_min 1: OK key integer, value real, n_min 1: OK key integer, value boolean, n_min 1: OK key integer, value string, n_min 1: OK key integer, value uuid, n_min 1: OK key real, value void, n_min 1: OK key real, value integer, n_min 1: OK key real, value real, n_min 1: OK key real, value boolean, n_min 1: OK key real, value string, n_min 1: OK key real, value uuid, n_min 1: OK key boolean, value void, n_min 1: OK key boolean, value integer, n_min 1: OK key boolean, value real, n_min 1: OK key boolean, value boolean, n_min 1: OK key boolean, value string, n_min 1: OK key boolean, value uuid, n_min 1: OK key string, value void, n_min 1: OK key string, value integer, n_min 1: OK key string, value real, n_min 1: OK key string, value boolean, n_min 1: OK key string, value string, n_min 1: OK key string, value uuid, n_min 1: OK key uuid, value void, n_min 1: OK key uuid, value integer, n_min 1: OK key uuid, value real, n_min 1: OK key uuid, value boolean, n_min 1: OK key uuid, value string, n_min 1: OK key uuid, value uuid, n_min 1: OK]]) AT_BANNER([OVSDB -- atoms without constraints]) OVSDB_CHECK_POSITIVE_CPY([integer atom from JSON], [[parse-atoms '["integer"]' \ '[0]' \ '[-1]' \ '[1e3]' \ '[9223372036854775807]' \ '[-9223372036854775808]' ]], [0 -1 1000 9223372036854775807 -9223372036854775808]) OVSDB_CHECK_POSITIVE([integer atom from string], [[parse-atom-strings -- '["integer"]' \ '0' \ '-1' \ '+1000' \ '9223372036854775807' \ '-9223372036854775808' \ '0-1000' \ '-1000-+1000' \ '-1000--10' \ '+10-+1000' \ '1-4096' \ '-4096--1' \ '-2000-2095']], [0 -1 1000 9223372036854775807 -9223372036854775808 0-1000 -1000-1000 -1000--10 10-1000 1-4096 -4096--1 -2000-2095]) OVSDB_CHECK_POSITIVE_CPY([real atom from JSON], [[parse-atoms '["real"]' \ '[0]' \ '[0.0]' \ '[-0.0]' \ '[-1.25]' \ '[1e3]' \ '[1e37]' \ '[0.00390625]' ]], [0 0 0 -1.25 1000 1e+37 0.00390625]) OVSDB_CHECK_POSITIVE([real atom from string], [[parse-atom-strings -- '["real"]' \ '0' \ '0.0' \ '-0.0' \ '-1.25' \ '1e3' \ '1e37' \ '0.00390625' ]], [0 0 0 -1.25 1000 1e+37 0.00390625]) OVSDB_CHECK_POSITIVE_CPY([boolean atom from JSON], [[parse-atoms '["boolean"]' '[true]' '[false]' ]], [true false]) OVSDB_CHECK_POSITIVE([boolean atom from string], [[parse-atom-strings '["boolean"]' 'true' 'false' ]], [true false]) OVSDB_CHECK_POSITIVE_CPY([string atom from JSON], [[parse-atoms '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']], ["" "true" "\"\\/\b\f\n\r\t"]) OVSDB_CHECK_POSITIVE([string atom from string], [[parse-atom-strings '["string"]' \ 'unquoted' \ '"quoted-string"' \ '"needs quotes"' \ '""' \ '"true"' \ '"\"\\\/\b\f\n\r\t"']], [unquoted quoted-string "needs quotes" "" "true" "\"\\/\b\f\n\r\t"]) OVSDB_CHECK_POSITIVE_CPY([uuid atom from JSON], [[parse-atoms '["uuid"]' '["uuid", "550e8400-e29b-41d4-a716-446655440000"]']], [[["uuid","550e8400-e29b-41d4-a716-446655440000"]]]) OVSDB_CHECK_POSITIVE([uuid atom from string], [[parse-atom-strings '["uuid"]' '550e8400-e29b-41d4-a716-446655440000']], [550e8400-e29b-41d4-a716-446655440000]) OVSDB_CHECK_POSITIVE_CPY([integer atom sorting], [[sort-atoms '["integer"]' '[55,0,-1,2,1]']], [[[-1,0,1,2,55]]]) OVSDB_CHECK_POSITIVE_CPY([real atom sorting], [[sort-atoms '["real"]' '[1.25,1.23,0.0,-0.0,-1e99]']], [[[-1e+99,0,0,1.23,1.25]]]) OVSDB_CHECK_POSITIVE_CPY([boolean atom sorting], [[sort-atoms '["boolean"]' '[true,false,true,false,false]']], [[[false,false,false,true,true]]]) OVSDB_CHECK_POSITIVE_CPY([string atom sorting], [[sort-atoms '["string"]' '["abd","abc","\b","xxx"]']], [[["\b","abc","abd","xxx"]]]) OVSDB_CHECK_POSITIVE_CPY([uuid atom sorting], [[sort-atoms '["uuid"]' '[ ["uuid", "00000000-0000-0000-0000-000000000001"], ["uuid", "00000000-1000-0000-0000-000000000000"], ["uuid", "00000000-0000-1000-0000-000000000000"], ["uuid", "00010000-0000-0000-0000-000000000000"], ["uuid", "00000000-0000-0000-0000-000000000100"], ["uuid", "00000000-0000-0000-0000-000100000000"], ["uuid", "00000000-0000-0010-0000-000000000000"], ["uuid", "00000100-0000-0000-0000-000000000000"], ["uuid", "00000000-0000-0001-0000-000000000000"], ["uuid", "00000000-0000-0000-0000-000001000000"], ["uuid", "01000000-0000-0000-0000-000000000000"], ["uuid", "00000000-0000-0000-0000-000000001000"], ["uuid", "00000000-0000-0000-0000-000010000000"], ["uuid", "00000000-0000-0000-0000-010000000000"], ["uuid", "00000000-0000-0100-0000-000000000000"], ["uuid", "10000000-0000-0000-0000-000000000000"], ["uuid", "00000000-0000-0000-0000-000000000010"], ["uuid", "00000000-0100-0000-0000-000000000000"], ["uuid", "00000000-0000-0000-0100-000000000000"], ["uuid", "00000000-0000-0000-0001-000000000000"], ["uuid", "00000010-0000-0000-0000-000000000000"], ["uuid", "00000000-0000-0000-0010-000000000000"], ["uuid", "00000000-0000-0000-0000-000000010000"], ["uuid", "00000000-0000-0000-1000-000000000000"], ["uuid", "00000000-0000-0000-0000-100000000000"], ["uuid", "00000000-0000-0000-0000-001000000000"], ["uuid", "00000000-0000-0000-0000-000000100000"], ["uuid", "00000000-0000-0000-0000-000000000000"], ["uuid", "00000000-0010-0000-0000-000000000000"], ["uuid", "00100000-0000-0000-0000-000000000000"], ["uuid", "00000000-0001-0000-0000-000000000000"], ["uuid", "00000001-0000-0000-0000-000000000000"], ["uuid", "00001000-0000-0000-0000-000000000000"]]']], [[[["uuid","00000000-0000-0000-0000-000000000000"],["uuid","00000000-0000-0000-0000-000000000001"],["uuid","00000000-0000-0000-0000-000000000010"],["uuid","00000000-0000-0000-0000-000000000100"],["uuid","00000000-0000-0000-0000-000000001000"],["uuid","00000000-0000-0000-0000-000000010000"],["uuid","00000000-0000-0000-0000-000000100000"],["uuid","00000000-0000-0000-0000-000001000000"],["uuid","00000000-0000-0000-0000-000010000000"],["uuid","00000000-0000-0000-0000-000100000000"],["uuid","00000000-0000-0000-0000-001000000000"],["uuid","00000000-0000-0000-0000-010000000000"],["uuid","00000000-0000-0000-0000-100000000000"],["uuid","00000000-0000-0000-0001-000000000000"],["uuid","00000000-0000-0000-0010-000000000000"],["uuid","00000000-0000-0000-0100-000000000000"],["uuid","00000000-0000-0000-1000-000000000000"],["uuid","00000000-0000-0001-0000-000000000000"],["uuid","00000000-0000-0010-0000-000000000000"],["uuid","00000000-0000-0100-0000-000000000000"],["uuid","00000000-0000-1000-0000-000000000000"],["uuid","00000000-0001-0000-0000-000000000000"],["uuid","00000000-0010-0000-0000-000000000000"],["uuid","00000000-0100-0000-0000-000000000000"],["uuid","00000000-1000-0000-0000-000000000000"],["uuid","00000001-0000-0000-0000-000000000000"],["uuid","00000010-0000-0000-0000-000000000000"],["uuid","00000100-0000-0000-0000-000000000000"],["uuid","00001000-0000-0000-0000-000000000000"],["uuid","00010000-0000-0000-0000-000000000000"],["uuid","00100000-0000-0000-0000-000000000000"],["uuid","01000000-0000-0000-0000-000000000000"],["uuid","10000000-0000-0000-0000-000000000000"]]]]) OVSDB_CHECK_POSITIVE_CPY([real not acceptable integer JSON atom], [[parse-atoms '["integer"]' '[0.5]' ]], [syntax "0.5": syntax error: expected integer]) dnl is not allowed anywhere in a UTF-8 string. dnl is a surrogate and not allowed in UTF-8. OVSDB_CHECK_POSITIVE([no invalid UTF-8 sequences in strings], [parse-atoms '[["string"]]' \ '@<:@"m4_esyscmd([printf "\300"])"@:>@' \ '@<:@"m4_esyscmd([printf "\355\240\200"])"@:>@' \ ], [constraint violation: not a valid UTF-8 string: invalid UTF-8 sequence 0xc0 constraint violation: not a valid UTF-8 string: invalid UTF-8 sequence 0xed 0xa0]) dnl Python won't let invalid UTF-8 (its idea of invalid UTF-8, anyway) into it dnl at all, so this test never gets as far as a constraint violation. It's dnl just a JSON parse error. dnl dnl is not allowed anywhere in a UTF-8 string. dnl ( is not allowed in UTF-8 but Python doesn't care.) dnl is not allowed in UTF-8. OVSDB_CHECK_POSITIVE_PY3([no invalid UTF-8 sequences in strings - Python], [parse-atoms '[["string"]]' \ '@<:@"m4_esyscmd([printf "\300"])"@:>@' \ '@<:@"m4_esyscmd([printf "\355\200\177"])"@:>@' \ ], ["not a valid UTF-8 string: invalid UTF-8 sequence 0xc0" "not a valid UTF-8 string: invalid UTF-8 sequence 0xed 0x80"]) OVSDB_CHECK_NEGATIVE([real not acceptable integer string atom], [[parse-atom-strings '["integer"]' '0.5' ]], ["0.5" is not a valid integer]) OVSDB_CHECK_NEGATIVE([inverted range is not acceptable integer string atom positive and negative], [[parse-atom-strings -- '["integer"]' '10--10' ]], ["10--10" is not a valid range. Range end cannot be before start.]) OVSDB_CHECK_NEGATIVE([inverted range is not acceptable integer string atom negative], [[parse-atom-strings -- '["integer"]' '-10--100' ]], ["-10--100" is not a valid range. Range end cannot be before start.]) OVSDB_CHECK_NEGATIVE([inverted range is not acceptable integer string atom positive], [[parse-atom-strings -- '["integer"]' '100-10' ]], ["100-10" is not a valid range. Range end cannot be before start.]) OVSDB_CHECK_NEGATIVE([too big range is not acceptable integer string atom positive and negative], [[parse-atom-strings -- '["integer"]' '-2000-2096' ]], [Range "-2000-2096" is too big.]) OVSDB_CHECK_NEGATIVE([too big range is not acceptable integer string atom negative], [[parse-atom-strings -- '["integer"]' '-4097--1' ]], [Range "-4097--1" is too big.]) OVSDB_CHECK_NEGATIVE([too big range is not acceptable integer string atom positive], [[parse-atom-strings -- '["integer"]' '1-4097' ]], [Range "1-4097" is too big.]) OVSDB_CHECK_POSITIVE_CPY([string "true" not acceptable boolean JSON atom], [[parse-atoms '["boolean"]' '["true"]' ]], [syntax ""true"": syntax error: expected boolean]) OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean string atom], [[parse-atom-strings '["boolean"]' '"true"' ]], [""true"" is not a valid boolean (use "true" or "false")]) OVSDB_CHECK_POSITIVE_CPY([integer not acceptable string JSON atom], [[parse-atoms '["string"]' '[1]']], [syntax "1": syntax error: expected string]) OVSDB_CHECK_POSITIVE_CPY([uuid atom must be expressed as JSON array], [[parse-atoms '["uuid"]' '["550e8400-e29b-41d4-a716-446655440000"]']], [[syntax ""550e8400-e29b-41d4-a716-446655440000"": syntax error: expected ["uuid", ]]]) OVSDB_CHECK_POSITIVE_CPY([named-uuid requires symbol table], [parse-atoms '[["uuid"]]' '[["named-uuid", "x"]]'], [[syntax "["named-uuid","x"]": syntax error: expected ["uuid", ]]]) OVSDB_CHECK_NEGATIVE([empty string atom must be quoted], [[parse-atom-strings '["string"]' '']], [An empty string is not valid as input; use "" to represent the empty string]) OVSDB_CHECK_NEGATIVE([quotes must be balanced], [parse-atom-strings '[["string"]]' '"asdf'], ["asdf: missing quote at end of quoted string]) OVSDB_CHECK_NEGATIVE([quoted string must not contain unescaped quote], [parse-atom-strings '[["string"]]' '"as"df"'], ["as"df": quoted string may not include unescaped "]) OVSDB_CHECK_NEGATIVE([quoted string must not end with backslash], [parse-atom-strings '[["string"]]' '"asdf\"'], ["asdf\": quoted string may not end with backslash]) OVSDB_CHECK_NEGATIVE([uuids must be valid], [parse-atom-strings '[["uuid"]]' '1234-5678'], ["1234-5678" is not a valid UUID]) AT_BANNER([OVSDB -- atoms with enum constraints]) OVSDB_CHECK_POSITIVE_CPY([integer atom enum], [[parse-atoms '[{"type": "integer", "enum": ["set", [1, 6, 8, 10]]}]' \ '[0]' \ '[1]' \ '[2]' \ '[3]' \ '[6]' \ '[7]' \ '[8]' \ '[9]' \ '[10]' \ '[11]']], [[constraint violation: 0 is not one of the allowed values ([1, 6, 8, 10]) 1 constraint violation: 2 is not one of the allowed values ([1, 6, 8, 10]) constraint violation: 3 is not one of the allowed values ([1, 6, 8, 10]) 6 constraint violation: 7 is not one of the allowed values ([1, 6, 8, 10]) 8 constraint violation: 9 is not one of the allowed values ([1, 6, 8, 10]) 10 constraint violation: 11 is not one of the allowed values ([1, 6, 8, 10])]]) OVSDB_CHECK_POSITIVE([integer atom enum from string], [[parse-atom-strings '[{"type": "integer", "enum": ["set", [1, 6, 8, 10, 20, 21, 22, 23, 24, 25]]}]' \ '1' \ '6' \ '8' \ '10' \ '20-25']], [[1 6 8 10 20-25]]) OVSDB_CHECK_NEGATIVE([integer not in enum set from string], [[parse-atom-strings '[{"type": "integer", "enum": ["set", [1, 6, 8, 10]]}]' '0' ]], [[constraint violation: 0 is not one of the allowed values ([1, 6, 8, 10])]]) OVSDB_CHECK_NEGATIVE([integer range not in enum set from string], [[parse-atom-strings '[{"type": "integer", "enum": ["set", [1, 6, 8, 10]]}]' '8-10' ]], [[constraint violation: 9 is not one of the allowed values ([1, 6, 8, 10])]]) OVSDB_CHECK_POSITIVE_CPY([real atom enum], [[parse-atoms '[{"type": "real", "enum": ["set", [-1.5, 1.5]]}]' \ '[-2]' \ '[-1]' \ '[-1.5]' \ '[0]' \ '[1]' \ '[1.5]' \ '[2]']], [[constraint violation: -2 is not one of the allowed values ([-1.5, 1.5]) constraint violation: -1 is not one of the allowed values ([-1.5, 1.5]) -1.5 constraint violation: 0 is not one of the allowed values ([-1.5, 1.5]) constraint violation: 1 is not one of the allowed values ([-1.5, 1.5]) 1.5 constraint violation: 2 is not one of the allowed values ([-1.5, 1.5])]]) OVSDB_CHECK_POSITIVE_CPY([boolean atom enum], [[parse-atoms '[{"type": "boolean", "enum": false}]' \ '[false]' \ '[true]']], [[false constraint violation: true is not one of the allowed values ([false])]]) OVSDB_CHECK_POSITIVE_CPY([string atom enum], [[parse-atoms '[{"type": "string", "enum": ["set", ["abc", "def"]]}]' \ '[""]' \ '["ab"]' \ '["abc"]' \ '["def"]' \ '["defg"]' \ '["DEF"]']], [[constraint violation: "" is not one of the allowed values ([abc, def]) constraint violation: ab is not one of the allowed values ([abc, def]) "abc" "def" constraint violation: defg is not one of the allowed values ([abc, def]) constraint violation: DEF is not one of the allowed values ([abc, def])]]) OVSDB_CHECK_POSITIVE_CPY([uuid atom enum], [[parse-atoms '[{"type": "uuid", "enum": ["set", [["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"], ["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]]]}]' \ '["uuid", "6d53a6dd-2da7-4924-9927-97f613812382"]' \ '["uuid", "52cbc842-137a-4db5-804f-9f34106a0ba3"]' \ '["uuid", "dab2a6b2-6094-4f43-a7ef-4c0f0608f176"]']], [[["uuid","6d53a6dd-2da7-4924-9927-97f613812382"] ["uuid","52cbc842-137a-4db5-804f-9f34106a0ba3"] constraint violation: dab2a6b2-6094-4f43-a7ef-4c0f0608f176 is not one of the allowed values ([52cbc842-137a-4db5-804f-9f34106a0ba3, 6d53a6dd-2da7-4924-9927-97f613812382])]]) AT_BANNER([OVSDB -- atoms with other constraints]) OVSDB_CHECK_POSITIVE_CPY([integers >= 5], [[parse-atoms '[{"type": "integer", "minInteger": 5}]' \ '[0]' \ '[4]' \ '[5]' \ '[6]' \ '[12345]']], [constraint violation: 0 is less than minimum allowed value 5 constraint violation: 4 is less than minimum allowed value 5 5 6 12345]) OVSDB_CHECK_POSITIVE_CPY([integers <= -1], [[parse-atoms '[{"type": "integer", "maxInteger": -1}]' \ '[0]' \ '[-1]' \ '[-2]' \ '[-123]']], [constraint violation: 0 is greater than maximum allowed value -1 -1 -2 -123]) OVSDB_CHECK_POSITIVE_CPY([integers in range -10 to 10], [[parse-atoms '[{"type": "integer", "minInteger": -10, "maxInteger": 10}]' \ '[-20]' \ '[-11]' \ '[-10]' \ '[-9]' \ '[1]' \ '[9]' \ '[10]' \ '[11]' \ '[123576]']], [constraint violation: -20 is not in the valid range -10 to 10 (inclusive) constraint violation: -11 is not in the valid range -10 to 10 (inclusive) -10 -9 1 9 10 constraint violation: 11 is not in the valid range -10 to 10 (inclusive) constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)]) OVSDB_CHECK_POSITIVE_CPY([reals >= 5], [[parse-atoms '[{"type": "real", "minReal": 5}]' \ '[0]' \ '[4]' \ '[5]' \ '[6]' \ '[12345]']], [constraint violation: 0 is less than minimum allowed value 5 constraint violation: 4 is less than minimum allowed value 5 5 6 12345]) OVSDB_CHECK_POSITIVE_CPY([reals <= -1], [[parse-atoms '[{"type": "real", "maxReal": -1}]' \ '[0]' \ '[-1]' \ '[-2]' \ '[-123]']], [constraint violation: 0 is greater than maximum allowed value -1 -1 -2 -123]) OVSDB_CHECK_POSITIVE_CPY([reals in range -10 to 10], [[parse-atoms '[{"type": "real", "minReal": -10, "maxReal": 10}]' \ '[-20]' \ '[-11]' \ '[-10]' \ '[-9]' \ '[1]' \ '[9]' \ '[10]' \ '[11]' \ '[123576]']], [constraint violation: -20 is not in the valid range -10 to 10 (inclusive) constraint violation: -11 is not in the valid range -10 to 10 (inclusive) -10 -9 1 9 10 constraint violation: 11 is not in the valid range -10 to 10 (inclusive) constraint violation: 123576 is not in the valid range -10 to 10 (inclusive)]) OVSDB_CHECK_POSITIVE_CPY([strings at least 2 characters long], [[parse-atoms '{"type": "string", "minLength": 2}' \ '[""]' \ '["a"]' \ '["ab"]' \ '["abc"]' \ '["\ud834\udd1e"]']], [[constraint violation: "" length 0 is less than minimum allowed length 2 constraint violation: "a" length 1 is less than minimum allowed length 2 "ab" "abc" constraint violation: "𝄞" length 1 is less than minimum allowed length 2]]) OVSDB_CHECK_POSITIVE_CPY([strings no more than 2 characters long], [[parse-atoms '{"type": "string", "maxLength": 2}' \ '[""]' \ '["a"]' \ '["ab"]' \ '["abc"]' \ '["de"]']], [["" "a" "ab" constraint violation: "abc" length 3 is greater than maximum allowed length 2 "de"]]) AT_BANNER([OVSDB -- simple data]) OVSDB_CHECK_POSITIVE_CPY([integer JSON datum], [[parse-data '["integer"]' '[0]' '["set",[1]]' '[-1]']], [0 1 -1]) OVSDB_CHECK_POSITIVE([integer string datum], [[parse-data-strings -- '["integer"]' '0' '1' '-1' '+1']], [0 1 -1 1]) OVSDB_CHECK_POSITIVE_CPY([real JSON datum], [[parse-data '["real"]' '[0]' '["set",[1.0]]' '[-1.25]']], [0 1 -1.25]) OVSDB_CHECK_POSITIVE([real string datum], [[parse-data-strings -- '["real"]' '0' '1.0' '-1.25']], [0 1 -1.25]) OVSDB_CHECK_POSITIVE_CPY([boolean JSON datum], [[parse-data '["boolean"]' '["set", [true]]' '[false]' ]], [true false]) OVSDB_CHECK_POSITIVE([boolean string datum], [[parse-data-strings '["boolean"]' 'true' 'false' ]], [true false]) OVSDB_CHECK_POSITIVE_CPY([string JSON datum], [[parse-data '["string"]' '["set",[""]]' '["true"]' '["\"\\\/\b\f\n\r\t"]']], ["" "true" "\"\\/\b\f\n\r\t"]) OVSDB_CHECK_POSITIVE([string string datum], [[parse-data-strings '["string"]' '"x"' '""' '"true"' '"\"\\\/\b\f\n\r\t"']], [x "" "true" "\"\\/\b\f\n\r\t"]) AT_BANNER([OVSDB -- set data]) OVSDB_CHECK_POSITIVE_CPY([JSON optional boolean], [[parse-data '{"key": "boolean", "min": 0}' \ '[true]' \ '["set", [false]]' \ '["set", []]']], [[true false ["set",[]]]], [set]) OVSDB_CHECK_POSITIVE([string optional boolean], [[parse-data-strings '{"key": "boolean", "min": 0}' \ 'true' \ 'false' \ '[]']], [[true false []]], [set]) OVSDB_CHECK_POSITIVE_CPY([JSON set of 0 or more integers], [[parse-data '{"key": "integer", "min": 0, "max": "unlimited"}' \ '["set", [0]]' \ '[1]' \ '["set", [0, 1]]' \ '["set", [0, 1, 2]]' \ '["set", [0, 1, 2, 3, 4, 5]]' \ '["set", [0, 1, 2, 3, 4, 5, 6, 7, 8]]' \ '["set", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]']], [[0 1 ["set",[0,1]] ["set",[0,1,2]] ["set",[0,1,2,3,4,5]] ["set",[0,1,2,3,4,5,6,7,8]] ["set",[0,1,2,3,4,5,6,7,8,9,10]]]]) OVSDB_CHECK_POSITIVE([string set of 0 or more integers], [[parse-data-strings '{"key": "integer", "min": 0, "max": "unlimited"}' \ '0' \ '0,1' \ '0, 1, 2' \ '[0, 1,2, 3, 4, 5]' \ '0, 1,2, 3,4, 5, 6, 7, 8' \ '[0, 1, 2, 3, 4,5, 6,7, 8, 9, 10]' \ '0-8' \ '[0-10']]], [[[0] [0, 1] [0, 1, 2] [0, 1, 2, 3, 4, 5] [0, 1, 2, 3, 4, 5, 6, 7, 8] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [0, 1, 2, 3, 4, 5, 6, 7, 8] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]]) OVSDB_CHECK_POSITIVE_CPY([JSON set of 1 to 3 uuids], [[parse-data '{"key": "uuid", "min": 1, "max": 3}' \ '["set", [["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]' \ '["uuid", "b5078be0-7664-4299-b836-8bcc03ef941f"]' \ '["set", [["uuid", "c5051240-30ff-43ed-b4b9-93cf3f050813"], ["uuid", "90558331-09af-4d2f-a572-509cad2e9088"], ["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]']], [[["uuid","550e8400-e29b-41d4-a716-446655440000"] ["uuid","b5078be0-7664-4299-b836-8bcc03ef941f"] ["set",[["uuid","550e8400-e29b-41d4-a716-446655440000"],["uuid","90558331-09af-4d2f-a572-509cad2e9088"],["uuid","c5051240-30ff-43ed-b4b9-93cf3f050813"]]]]]) OVSDB_CHECK_POSITIVE([string set of 1 to 3 uuids], [[parse-data-strings '{"key": "uuid", "min": 1, "max": 3}' \ '[550e8400-e29b-41d4-a716-446655440000]' \ '[c5051240-30ff-43ed-b4b9-93cf3f050813, 90558331-09af-4d2f-a572-509cad2e9088, 550e8400-e29b-41d4-a716-446655440000]']], [[[550e8400-e29b-41d4-a716-446655440000] [550e8400-e29b-41d4-a716-446655440000, 90558331-09af-4d2f-a572-509cad2e9088, c5051240-30ff-43ed-b4b9-93cf3f050813]]]) OVSDB_CHECK_POSITIVE_CPY([JSON set of 0 to 3 strings], [[parse-data '{"key": "string", "min": 0, "max": 3}' \ '["set", []]' \ '["a longer string"]' \ '["set", ["a relatively long string"]]' \ '["set", ["short string", "a relatively long string"]]' \ '["set", ["zzz", "short string", "a relatively long string"]]']], [[["set",[]] "a longer string" "a relatively long string" ["set",["a relatively long string","short string"]] ["set",["a relatively long string","short string","zzz"]]]]) OVSDB_CHECK_POSITIVE([string set of 0 to 3 strings], [[parse-data-strings '{"key": "string", "min": 0, "max": 3}' \ '[]' \ '"a relatively long string"' \ '["short string", "a relatively long string"]' \ '"zzz","short string","a relatively long string"']], [[[] ["a relatively long string"] ["a relatively long string", "short string"] ["a relatively long string", "short string", zzz]]]) OVSDB_CHECK_NEGATIVE_CPY([duplicate boolean not allowed in JSON set], [[parse-data '{"key": "boolean", "max": 5}' '["set", [true, true]]']], [ovsdb error: set contains duplicate]) OVSDB_CHECK_NEGATIVE([duplicate boolean not allowed in string set], [[parse-data-strings '{"key": "boolean", "max": 5}' 'true, true']], [set contains duplicate value]) OVSDB_CHECK_NEGATIVE_CPY([duplicate integer not allowed in JSON set], [[parse-data '{"key": "integer", "max": 5}' '["set", [1, 2, 3, 1]]']], [ovsdb error: set contains duplicate]) OVSDB_CHECK_NEGATIVE([duplicate integer not allowed in string set], [[parse-data-strings '{"key": "integer", "max": 5}' '[1, 2, 3, 1]']], [set contains duplicate value]) OVSDB_CHECK_NEGATIVE_CPY([duplicate real not allowed in JSON set], [[parse-data '{"key": "real", "max": 5}' '["set", [0.0, -0.0]]']], [ovsdb error: set contains duplicate]) OVSDB_CHECK_NEGATIVE([duplicate real not allowed in string set], [[parse-data-strings '{"key": "real", "max": 5}' '0.0, -0.0']], [set contains duplicate value]) OVSDB_CHECK_NEGATIVE_CPY([duplicate string not allowed in JSON set], [[parse-data '{"key": "string", "max": 5}' '["set", ["asdf", "ASDF", "asdf"]]']], [ovsdb error: set contains duplicate]) OVSDB_CHECK_NEGATIVE([duplicate string not allowed in string set], [[parse-data-strings '{"key": "string", "max": 5}' 'asdf, ASDF, "asdf"']], [set contains duplicate value]) OVSDB_CHECK_NEGATIVE_CPY([duplicate uuid not allowed in JSON set], [[parse-data '{"key": "uuid", "max": 5}' \ '["set", [["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"], ["uuid", "355ad037-f1da-40aa-b47c-ff9c7e8c6a38"], ["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"]]]']], [ovsdb error: set contains duplicate]) OVSDB_CHECK_NEGATIVE([duplicate uuid not allowed in string set], [[parse-data-strings '{"key": "uuid", "max": 5}' \ '7ef21525-0088-4a28-a418-5518413e43ea, 355ad037-f1da-40aa-b47c-ff9c7e8c6a38, 7ef21525-0088-4a28-a418-5518413e43ea']], [set contains duplicate value]) AT_BANNER([OVSDB -- map data]) OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 integer to boolean], [[parse-data '{"key": "integer", "value": "boolean"}' \ '["map", [[1, true]]]']], [[["map",[[1,true]]]]]) OVSDB_CHECK_POSITIVE([string map of 1 integer to boolean], [[parse-data-strings '{"key": "integer", "value": "boolean"}' \ '1=true']], [[1=true]]) OVSDB_CHECK_POSITIVE_CPY([JSON map of at least 1 integer to boolean], [[parse-data '{"key": "integer", "value": "boolean", "max": "unlimited"}' \ '["map", [[1, true]]]' \ '["map", [[0, true], [1, false], [2, true], [3, true], [4, true]]]' \ '["map", [[3, false], [0, true], [4, false]]]']], [[["map",[[1,true]]] ["map",[[0,true],[1,false],[2,true],[3,true],[4,true]]] ["map",[[0,true],[3,false],[4,false]]]]]) OVSDB_CHECK_POSITIVE([string map of at least 1 integer to boolean], [[parse-data-strings '{"key": "integer", "value": "boolean", "max": "unlimited"}' \ '1=true' \ '0=true 1=false 2=true, 3=true 4=true,' \ '3=false,0=true ,4=false']], [[{1=true} {0=true, 1=false, 2=true, 3=true, 4=true} {0=true, 3=false, 4=false}]]) OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 boolean to integer], [[parse-data '{"key": "boolean", "value": "integer"}' \ '["map", [[true, 1]]]']], [[["map",[[true,1]]]]]) OVSDB_CHECK_POSITIVE([string map of 1 boolean to integer], [[parse-data-strings '{"key": "boolean", "value": "integer"}' \ 'true=1']], [[true=1]]) OVSDB_CHECK_POSITIVE_CPY([JSON map of 1 uuid to real], [[parse-data '{"key": "uuid", "value": "real", "min": 1, "max": 5}' \ '["map", [[["uuid", "cad8542b-6ee1-486b-971b-7dcbf6e14979"], 1.0], [["uuid", "6b94b968-2702-4f64-9457-314a34d69b8c"], 2.0], [["uuid", "d2c4a168-24de-47eb-a8a3-c1abfc814979"], 3.0], [["uuid", "25bfa475-d072-4f60-8be1-00f48643e9cb"], 4.0], [["uuid", "1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"], 5.0]]]']], [[["map",[[["uuid","1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"],5],[["uuid","25bfa475-d072-4f60-8be1-00f48643e9cb"],4],[["uuid","6b94b968-2702-4f64-9457-314a34d69b8c"],2],[["uuid","cad8542b-6ee1-486b-971b-7dcbf6e14979"],1],[["uuid","d2c4a168-24de-47eb-a8a3-c1abfc814979"],3]]]]]) OVSDB_CHECK_POSITIVE([string map of 1 uuid to real], [[parse-data-strings '{"key": "uuid", "value": "real", "min": 1, "max": 5}' \ 'cad8542b-6ee1-486b-971b-7dcbf6e14979=1.0, 6b94b968-2702-4f64-9457-314a34d69b8c=2.0, d2c4a168-24de-47eb-a8a3-c1abfc814979=3.0, 25bfa475-d072-4f60-8be1-00f48643e9cb=4.0, 1c92b8ca-d5e4-4628-a85d-1dc2d099a99a=5.0']], [[{1c92b8ca-d5e4-4628-a85d-1dc2d099a99a=5, 25bfa475-d072-4f60-8be1-00f48643e9cb=4, 6b94b968-2702-4f64-9457-314a34d69b8c=2, cad8542b-6ee1-486b-971b-7dcbf6e14979=1, d2c4a168-24de-47eb-a8a3-c1abfc814979=3}]]) OVSDB_CHECK_POSITIVE_CPY([JSON map of 10 string to string], [[parse-data '{"key": "string", "value": "string", "min": 1, "max": 10}' \ '["map", [["2 gills", "1 chopin"], ["2 chopins", "1 pint"], ["2 pints", "1 quart"], ["2 quarts", "1 pottle"], ["2 pottles", "1 gallon"], ["2 gallons", "1 peck"], ["2 pecks", "1 demibushel"], ["2 demibushel", "1 firkin"], ["2 firkins", "1 kilderkin"], ["2 kilderkins", "1 barrel"]]]']], [[["map",[["2 chopins","1 pint"],["2 demibushel","1 firkin"],["2 firkins","1 kilderkin"],["2 gallons","1 peck"],["2 gills","1 chopin"],["2 kilderkins","1 barrel"],["2 pecks","1 demibushel"],["2 pints","1 quart"],["2 pottles","1 gallon"],["2 quarts","1 pottle"]]]]]) OVSDB_CHECK_POSITIVE([string map of 10 string to string], [[parse-data-strings '{"key": "string", "value": "string", "min": 1, "max": 10}' \ '{"2 gills"="1 chopin", "2 chopins"= "1 pint", "2 pints"= "1 quart", "2 quarts"= "1 pottle", "2 pottles"= "1 gallon", "2 gallons"= "1 peck", "2 pecks"= "1 demibushel", "2 demibushel"= "1 firkin", "2 firkins"= "1 kilderkin", "2 kilderkins"= "1 barrel"}']], [[{"2 chopins"="1 pint", "2 demibushel"="1 firkin", "2 firkins"="1 kilderkin", "2 gallons"="1 peck", "2 gills"="1 chopin", "2 kilderkins"="1 barrel", "2 pecks"="1 demibushel", "2 pints"="1 quart", "2 pottles"="1 gallon", "2 quarts"="1 pottle"}]]) OVSDB_CHECK_NEGATIVE_CPY([duplicate integer key not allowed in JSON map], [[parse-data '{"key": "integer", "value": "boolean", "max": 5}' \ '["map", [[1, true], [2, false], [1, false]]]']], [ovsdb error: map contains duplicate key]) OVSDB_CHECK_NEGATIVE([duplicate integer key not allowed in string map], [[parse-data-strings '{"key": "integer", "value": "boolean", "max": 5}' \ '1=true 2=false 1=false']], [map contains duplicate key]) OVSDB_CHECK_POSITIVE([generate and apply diff -- integer], [[diff-data '["integer"]' '[0]' '[2]']], [[diff: 2 apply diff: 2 apply diff in place: 2 OK]]) OVSDB_CHECK_POSITIVE([generate and apply diff -- boolean], [[diff-data '["boolean"]' '[true]' '[false]']], [[diff: false apply diff: false apply diff in place: false OK]]) OVSDB_CHECK_POSITIVE([generate and apply diff -- string], [[diff-data '["string"]' '["AAA"]' '["BBB"]']], [[diff: "BBB" apply diff: "BBB" apply diff in place: "BBB" OK]]) dnl Test set modifications. OVSDB_CHECK_POSITIVE([generate and apply diff -- set], [[diff-data '{"key": "integer", "min":0, "max": 3}' \ '["set", [0, 1]]' '["set", [1,2]]' \ '["set", [0, 1]]' '["set", [1]]' \ '["set", []]' '["set", [0, 1]]' \ '["set", [0, 1]]' '["set", []]' ]], [[diff: ["set",[0,2]] apply diff: ["set",[1,2]] apply diff in place: ["set",[1,2]] OK diff: 0 apply diff: 1 apply diff in place: 1 OK diff: ["set",[0,1]] apply diff: ["set",[0,1]] apply diff in place: ["set",[0,1]] OK diff: ["set",[0,1]] apply diff: ["set",[]] apply diff in place: ["set",[]] OK]]) dnl Test set modifications causes data to violate set size constrain. OVSDB_CHECK_NEGATIVE([generate and apply diff -- set -- size error], [[diff-data '{"key": "integer", "min":0, "max": 3}' \ '["set", [0, 1]]' '["set", [1, 2, 3, 4]]']], [[ovsdb error: Datum crated by diff has size error]]) dnl Test set modifications. OVSDB_CHECK_POSITIVE([generate and apply diff -- map], [[diff-data '{"key": "string", "value": "string", "min":0, "max": 3}' \ '["map", [["2 gills", "1 chopin"]]]' '["map", [["2 pints", "1 quart"]]]' \ '["map", [["2 gills", "1 chopin"]]]' '["map", [["2 gills", "1 chopin"]]]' \ '["map", [["2 gills", "1 chopin"]]]' '["map", []]' \ '["map", []]' '["map", [["2 pints", "1 quart"]]]' \ '["map", [["2 gills", "1 chopin"]]]' '["map", [["2 gills", "1 gallon"]]]' \ ]], [[diff: ["map",[["2 gills","1 chopin"],["2 pints","1 quart"]]] apply diff: ["map",[["2 pints","1 quart"]]] apply diff in place: ["map",[["2 pints","1 quart"]]] OK diff: ["map",[]] apply diff: ["map",[["2 gills","1 chopin"]]] apply diff in place: ["map",[["2 gills","1 chopin"]]] OK diff: ["map",[["2 gills","1 chopin"]]] apply diff: ["map",[]] apply diff in place: ["map",[]] OK diff: ["map",[["2 pints","1 quart"]]] apply diff: ["map",[["2 pints","1 quart"]]] apply diff in place: ["map",[["2 pints","1 quart"]]] OK diff: ["map",[["2 gills","1 gallon"]]] apply diff: ["map",[["2 gills","1 gallon"]]] apply diff in place: ["map",[["2 gills","1 gallon"]]] OK]]) OVSDB_CHECK_NEGATIVE([generate and apply diff with map -- size error], [[diff-data '{"key": "string", "value": "string", "min":0, "max": 3}' \ '["map", [["2 gills", "1 chopin"]]]' \ '["map", [["2 gills", "1 gallon"], ["2 pints", "1 quart"], ["2 quarts", "1 pottle"], ["2 gallons", "1 peck"]]]' \ ]], [[ovsdb error: Datum crated by diff has size error]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-execution.at000066400000000000000000001334631514270232600236000ustar00rootroot00000000000000AT_BANNER([OVSDB -- execution]) m4_divert_push([PREPARE_TESTS]) [ ordinal_schema () { cat <<'EOF' {"name": "ordinals", "tables": { "ordinals": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"], ["number", "name"]]}}, "version": "5.1.3", "cksum": "12345678 9"} EOF } constraint_schema () { cat << 'EOF' {"name": "constraints", "tables": { "a": { "columns": { "a": {"type": "integer"}, "a2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, "min": 0, "max": "unlimited"}}, "a2b": {"type": {"key": {"type": "uuid", "refTable": "b"}, "min": 0, "max": "unlimited"}}}}, "b": { "columns": { "b": {"type": "integer"}, "b2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, "min": 0, "max": "unlimited"}}, "b2b": {"type": {"key": {"type": "uuid", "refTable": "b"}, "min": 0, "max": "unlimited"}}, "x": {"type": {"key": "integer", "min": 1, "max": 2}}}}, "constrained": { "columns": { "positive": {"type": {"key": {"type": "integer", "minInteger": 1}}}}, "maxRows": 1}}} EOF } weak_schema () { cat <<'EOF' {"name": "weak", "tables": { "a": { "columns": { "a": {"type": "integer"}, "a2a": {"type": {"key": {"type": "uuid", "refTable": "a", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "a2a1": {"type": {"key": {"type": "uuid", "refTable": "a", "refType": "weak"}}}, "a2b": {"type": {"key": {"type": "uuid", "refTable": "b", "refType": "weak"}}}}}, "b": { "columns": { "b": {"type": "integer"}, "b2a": {"type": {"key": {"type": "uuid", "refTable": "a", "refType": "weak"}, "min": 0, "max": "unlimited"}}}}}} EOF } gc_schema () { cat <<'EOF' {"name": "gc", "tables": { "root": { "columns": { "a": {"type": {"key": {"type": "uuid", "refTable": "a"}, "min": 0, "max": "unlimited"}}}, "isRoot": true}, "a": { "columns": { "a": {"type": "integer"}, "a2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, "min": 0, "max": "unlimited"}}, "a2b": {"type": {"key": {"type": "uuid", "refTable": "b"}, "min": 0, "max": "unlimited"}}, "wa2a": {"type": {"key": {"type": "uuid", "refTable": "a", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "wa2b": {"type": {"key": {"type": "uuid", "refTable": "b", "refType": "weak"}, "min": 0, "max": "unlimited"}}}}, "b": { "columns": { "b": {"type": "integer"}, "b2a": {"type": {"key": {"type": "uuid", "refTable": "a"}, "min": 0, "max": "unlimited"}}, "wb2a": {"type": {"key": {"type": "uuid", "refTable": "a", "refType": "weak"}, "min": 0, "max": "unlimited"}}}, "isRoot": false}}} EOF } immutable_schema () { cat <<'EOF' {"name": "immutable", "tables": { "a": { "columns": {"i": {"type": "integer", "mutable": false}}}}} EOF } ] m4_divert_pop([PREPARE_TESTS]) # # OVSDB_CHECK_EXECUTION_RO(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Runs "test-ovsdb execute-readonly" with the given SCHEMA and each of the # TRANSACTIONS (which should be a quoted list of quoted strings). # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION_RO], [AT_SETUP([ovsdb - $1]) AT_KEYWORDS([ovsdb execute execution positive $5]) AT_CHECK([test-ovsdb execute-readonly "`$2`" m4_foreach([txn], [$3], [ 'txn'])], [0], [stdout], []) AT_CHECK([uuidfilt stdout], [0], [$4]) AT_CLEANUP]) OVSDB_CHECK_EXECUTION_RO([block insert on read only DB], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {}}]]]], [[[{"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}] ]]) OVSDB_CHECK_EXECUTION_RO([allow select on read only DB], [ordinal_schema], [[[["ordinals", {"op": "select", "table": "ordinals", "where": []}]]]], [[[{"rows":[]}] ]]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Runs "test-ovsdb execute" with the given SCHEMA and each of the # TRANSACTIONS (which should be a quoted list of quoted strings). # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([ovsdb - $1]) AT_KEYWORDS([ovsdb execute execution positive $5]) AT_CHECK([test-ovsdb execute "`$2`" m4_foreach([txn], [$3], [ 'txn'])], [0], [stdout], []) AT_CHECK([uuidfilt stdout], [0], [$4]) AT_CLEANUP]) OVSDB_CHECK_EXECUTION([uuid-name must be ], [constraint_schema], [[[["constraints", {"op": "insert", "table": "a", "row": {}, "uuid-name": "0"}]]]], [[[{"details":"Parsing ovsdb operation 1 of 1 failed: Type mismatch for member 'uuid-name'.","error":"syntax error","syntax":"{\"op\":\"insert\",\"row\":{},\"table\":\"a\",\"uuid-name\":\"0\"}"}] ]]) OVSDB_CHECK_EXECUTION([named-uuid must be ], [constraint_schema], [[[["constraints", {"op": "insert", "table": "a", "row": {"a2a": ["named-uuid", "0"]}}]]]], [[[{"details":"named-uuid string is not a valid ","error":"syntax error","syntax":"[\"named-uuid\",\"0\"]"}] ]]) OVSDB_CHECK_EXECUTION([duplicate uuid-name not allowed], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {}, "uuid-name": "x"}, {"op": "insert", "table": "ordinals", "row": {}, "uuid-name": "x"}]]]], [[[{"uuid":["uuid","<0>"]},{"details":"This \"uuid-name\" appeared on an earlier \"insert\" operation.","error":"duplicate uuid-name","syntax":"\"x\""}] ]]) m4_define([ONE_EXECUTION_EXAMPLE], [dnl dnl At one point the "commit" code ignored new rows with all-default values, dnl so this checks for that problem. OVSDB_CHECK_EXECUTION([insert default row, query table], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {}}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": []}]]]], [[[{"uuid":["uuid","<0>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"","number":0}]}] ]]) ]) m4_define([EXECUTION_EXAMPLES], [ ONE_EXECUTION_EXAMPLE OVSDB_CHECK_EXECUTION([insert row, query table], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": []}]]]], [[[{"uuid":["uuid","<0>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]}] ]]) OVSDB_CHECK_EXECUTION([insert row with uuid, query table], [ordinal_schema], dnl Insert initial row. [[[["ordinals", {"op": "insert", "uuid": "ffffffff-971b-4cba-bf42-520515973b7e", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]], dnl Query row back. [[["ordinals", {"op": "select", "table": "ordinals", "where": []}]]], dnl Attempt to insert second row with same UUID (fails). [[["ordinals", {"op": "insert", "uuid": "ffffffff-971b-4cba-bf42-520515973b7e", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]]], [[[{"uuid":["uuid","ffffffff-971b-4cba-bf42-520515973b7e"]}] [{"rows":[{"_uuid":["uuid","ffffffff-971b-4cba-bf42-520515973b7e"],"_version":["uuid","<0>"],"name":"zero","number":0}]}] [{"details":"This UUID would duplicate a UUID already present within the table or deleted within the same transaction.","error":"duplicate uuid","syntax":"\"ffffffff-971b-4cba-bf42-520515973b7e\""}] ]]) OVSDB_CHECK_EXECUTION([insert rows, query by index], [ordinal_schema], dnl Insert initial rows. [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]]], [[["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", 1]]}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 3, "name": "three"}}]]], dnl Query the deleted row. [[["ordinals", {"op": "select", "table": "ordinals", "where": [["number", "==", 1]]}]]], dnl Query a non-deleted row with first index. [[["ordinals", {"op": "select", "table": "ordinals", "where": [["number", "==", 3]]}]]], dnl Query with second index. [[["ordinals", {"op": "select", "table": "ordinals", "where": [["name", "==", "two"], ["number", "==", 2]]}]]], dnl Delete a row and query in the same transaction. [[["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", 2]]}, {"op": "select", "table": "ordinals", "where": [["number", "==", 2]]}]]], dnl Mutate a row and query in the same transaction. [[["ordinals", {"op": "mutate", "table": "ordinals", "mutations": [["number", "+=", 2]], "where": [["number", "==", 0]]}, {"op": "select", "table": "ordinals", "where": [["number", "==", 0]]}, {"op": "select", "table": "ordinals", "where": [["number", "==", 2]]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":1}] [{"uuid":["uuid","<2>"]}] [{"uuid":["uuid","<3>"]}] [{"rows":[]}] [{"rows":[{"_uuid":["uuid","<3>"],"_version":["uuid","<4>"],"name":"three","number":3}]}] [{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<5>"],"name":"two","number":2}]}] [{"count":1},{"rows":[]}] [{"count":1},{"rows":[]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<6>"],"name":"zero","number":2}]}] ]]) OVSDB_CHECK_EXECUTION([reinsert indexed rows mid flight], [ordinal_schema], dnl Insert initial rows. [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "orig"} }]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [["number", "==", 0]] }, {"op": "insert", "table": "ordinals", "uuid-name": "dup", "row": {"number": 0, "name": "dup"} }, {"op": "select", "table": "ordinals", "sort": ["name"], "where": [["number", "==", 0]] }, {"op": "delete", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "dup"]]] }, {"op": "select", "table": "ordinals", "where": [["number", "==", 0]] }]]] ], [[[{"uuid":["uuid","<0>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"orig","number":0}]},{"uuid":["uuid","<2>"]},{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<3>"],"name":"dup","number":0},{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"orig","number":0}]},{"count":1},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"orig","number":0}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, query by value], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [["name", "==", "zero"]]}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [["name", "==", "one"]]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]}] [{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, query by named-uuid], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "second"}, {"op": "select", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "first"]]]}, {"op": "select", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "second"]]]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]},{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, update rows by value], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "update", "table": "ordinals", "where": [["name", "==", "zero"]], "row": {"name": "nought"}}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [], "sort": ["number"]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":1}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"nought","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, mutate rows], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [["name", "==", "zero"]], "mutations": [["number", "+=", 2]]}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [], "sort": ["number"]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":1}] [{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<2>"],"name":"one","number":1},{"_uuid":["uuid","<0>"],"_version":["uuid","<3>"],"name":"zero","number":2}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, delete by named-uuid], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "second"}, {"op": "delete", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "first"]]]}, {"op": "select", "table": "ordinals", "where": [], "columns": ["name","number"]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":1},{"rows":[{"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, delete rows by value], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "delete", "table": "ordinals", "where": [["name", "==", "zero"]]}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": []}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":1}] [{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<2>"],"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, delete by (non-matching) value], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "delete", "table": "ordinals", "where": [["name", "==", "nought"]]}]]], [[["ordinals", {"op": "select", "table": "ordinals", "where": [], "sort": ["number"]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":0}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]]) OVSDB_CHECK_EXECUTION([insert rows, delete all], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "second"}, {"op": "delete", "table": "ordinals", "where": []}, {"op": "select", "table": "ordinals", "where": [], "columns": ["name","number"]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":2},{"rows":[]}] ]]) OVSDB_CHECK_EXECUTION([insert row, query table, commit], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "select", "table": "ordinals", "where": []}, {"op": "commit", "durable": false}]]]], [[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}] ]]) OVSDB_CHECK_EXECUTION([insert row, query table, commit durably], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "select", "table": "ordinals", "where": []}, {"op": "commit", "durable": true}]]]], [[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}] ]]) OVSDB_CHECK_EXECUTION([equality wait with correct rows], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}] ]]) OVSDB_CHECK_EXECUTION([equality wait with extra row], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"where\" clause test failed","error":"timed out"}] ]]) OVSDB_CHECK_EXECUTION([equality wait with missing row], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "one", "number": 1}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"where\" clause test failed","error":"timed out"}] ]]) OVSDB_CHECK_EXECUTION([inequality wait with correct rows], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "!=", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"where\" clause test failed","error":"timed out"}] ]]) OVSDB_CHECK_EXECUTION([inequality wait with extra row], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "!=", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}] ]]) OVSDB_CHECK_EXECUTION([inequality wait with missing row], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 0, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "!=", "rows": [{"name": "one", "number": 1}]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}] ]]) OVSDB_CHECK_EXECUTION([insert and update constraints], [constraint_schema], [[[["constraints", {"op": "insert", "table": "constrained", "row": {}}]]], [[["constraints", {"op": "insert", "table": "constrained", "row": {"positive": -1}}]]], [[["constraints", {"op": "update", "table": "constrained", "where": [], "row": {"positive": -2}}]]], [[["constraints", {"op": "insert", "table": "constrained", "row": {"positive": 1}}]]], [[["constraints", {"op": "insert", "table": "constrained", "row": {"positive": 2}}]]]], [[[{"details":"0 is less than minimum allowed value 1","error":"constraint violation"}] [{"details":"-1 is less than minimum allowed value 1","error":"constraint violation"}] [{"details":"-2 is less than minimum allowed value 1","error":"constraint violation"}] [{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]},{"details":"transaction causes \"constrained\" table to contain 2 rows, greater than the schema-defined limit of 1 row(s)","error":"constraint violation"}] ]]) OVSDB_CHECK_EXECUTION([index uniqueness checking], [ordinal_schema], dnl Insert initial row. [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]]], dnl Try to insert row with identical value (fails). [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "another one"}}]]], dnl Remove initial row and insert new row with identical value in a single dnl transaction (succeeds). [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "another one"}}, {"op": "delete", "table": "ordinals", "where": [["name", "==", "one"]]}]]], dnl Remove row and insert two new rows with identical value in a single dnl transaction (fails). [[["ordinals", {"op": "delete", "table": "ordinals", "where": []}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "still another one"}}]]], dnl Add new row with different value (succeeds). [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]], dnl Change rows so values collide (fails). [[["ordinals", {"op": "update", "table": "ordinals", "where": [], "row": {"number": 3}}]]], dnl Swap rows' values (succeeds). [[["ordinals", {"op": "update", "table": "ordinals", "where": [["number", "==", 1]], "row": {"number": 2, "name": "old two"}}, {"op": "update", "table": "ordinals", "where": [["name", "==", "two"]], "row": {"number": 1, "name": "old one"}}]]], dnl Change all rows' values to values not used before and insert values that dnl collide (only) with their previous values (succeeds). [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [], "mutations": [["number", "*=", 10]]}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "new one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "new two"}}, {"op": "select", "table": "ordinals", "where": [], "columns": ["number", "name"], "sort": ["number"]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]},{"details":"Transaction causes multiple rows in \"ordinals\" table to have identical values (1) for index on column \"number\". First row, with UUID <0>, existed in the database before this transaction and was not modified by the transaction. Second row, with UUID <1>, was inserted by this transaction.","error":"constraint violation"}] [{"uuid":["uuid","<2>"]},{"count":1}] [{"count":1},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]},{"details":"Transaction causes multiple rows in \"ordinals\" table to have identical values (1) for index on column \"number\". First row, with UUID <4>, was inserted by this transaction. Second row, with UUID <3>, was inserted by this transaction.","error":"constraint violation"}] [{"uuid":["uuid","<5>"]}] [{"count":2},{"details":"Transaction causes multiple rows in \"ordinals\" table to have identical values (3) for index on column \"number\". First row, with UUID <5>, had the following index values before the transaction: 2. Second row, with UUID <2>, had the following index values before the transaction: 1.","error":"constraint violation"}] [{"count":1},{"count":1}] [{"count":2},{"uuid":["uuid","<6>"]},{"uuid":["uuid","<7>"]},{"rows":[{"name":"new one","number":1},{"name":"new two","number":2},{"name":"old one","number":10},{"name":"old two","number":20}]}] ]]) OVSDB_CHECK_EXECUTION([size constraints on sets], [constraint_schema], [ [[["constraints", {"op": "insert", "table": "b", "row": {"b": 1} }]]], [[["constraints", {"op": "mutate", "table": "b", "where": [], "mutations": [["x", "delete", 0]] }]]], [[["constraints", {"op": "mutate", "table": "b", "where": [], "mutations": [["x", "insert", 1]] }]]], [[["constraints", {"op": "update", "table": "b", "where": [], "row": {"x": ["set", [3, 4]]} }]]], [[["constraints", {"op": "mutate", "table": "b", "where": [], "mutations": [["x", "insert", 5]] }]]], [[["constraints", {"op": "mutate", "table": "b", "where": [], "mutations": [["x", "delete", 4], ["x", "insert", 5]] }]]] ], [[[{"uuid":["uuid","<0>"]}] [{"details":"Attempted to store 0 elements in set of 1 to 2 integers.","error":"constraint violation"}] [{"count":1}] [{"count":1}] [{"details":"Attempted to store 3 elements in set of 1 to 2 integers.","error":"constraint violation"}] [{"count":1}] ]]) OVSDB_CHECK_EXECUTION([referential integrity -- simple], [constraint_schema], [[[["constraints", {"op": "insert", "table": "b", "row": {"b": 1}, "uuid-name": "brow"}, {"op": "insert", "table": "a", "row": {"a": 0, "a2b": ["set", [["named-uuid", "brow"]]]}}, {"op": "insert", "table": "a", "row": {"a": 1, "a2b": ["set", [["named-uuid", "brow"]]]}}, {"op": "insert", "table": "a", "row": {"a": 2, "a2b": ["set", [["named-uuid", "brow"]]]}}]]], [[["constraints", {"op": "delete", "table": "b", "where": []}]]], [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}]]], [[["constraints", {"op": "delete", "table": "b", "where": []}]]], [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 1]]}]]], [[["constraints", {"op": "delete", "table": "b", "where": []}]]], [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 2]]}]]], [[["constraints", {"op": "delete", "table": "b", "where": []}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}] [{"count":1},{"details":"cannot delete b row <0> because of 3 remaining reference(s)","error":"referential integrity violation"}] [{"count":1}] [{"count":1},{"details":"cannot delete b row <0> because of 2 remaining reference(s)","error":"referential integrity violation"}] [{"count":1}] [{"count":1},{"details":"cannot delete b row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}] [{"count":1}] [{"count":1}] ]]) OVSDB_CHECK_EXECUTION([referential integrity -- mutual references], [constraint_schema], [[[["constraints", {"op": "insert", "table": "a", "row": {"a": 0, "a2b": ["set", [["named-uuid", "row2"]]], "a2a": ["set", [["named-uuid", "row1"]]]}, "uuid-name": "row1"}, {"op": "insert", "table": "b", "row": {"b": 1, "b2b": ["set", [["named-uuid", "row2"]]], "b2a": ["set", [["named-uuid", "row1"]]]}, "uuid-name": "row2"}]]], [[["constraints", {"op": "insert", "table": "a", "row": {"a2b": ["set", [["uuid", "b516b960-5b19-4fc2-bb82-fe1cbd6d0241"]]]}}]]], [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}]]], [[["constraints", {"op": "delete", "table": "b", "where": [["b", "==", 1]]}]]], dnl Try the deletions again to make sure that the refcounts got rolled back. [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}]]], [[["constraints", {"op": "delete", "table": "b", "where": [["b", "==", 1]]}]]], [[["constraints", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}, {"op": "delete", "table": "b", "where": [["b", "==", 1]]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}] [{"uuid":["uuid","<2>"]},{"details":"Table a column a2b row <2> references nonexistent row <3> in table b.","error":"referential integrity violation"}] [{"count":1},{"details":"cannot delete a row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}] [{"count":1},{"details":"cannot delete b row <1> because of 1 remaining reference(s)","error":"referential integrity violation"}] [{"count":1},{"details":"cannot delete a row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}] [{"count":1},{"details":"cannot delete b row <1> because of 1 remaining reference(s)","error":"referential integrity violation"}] [{"count":1},{"count":1}] ]]) OVSDB_CHECK_EXECUTION([weak references], [weak_schema], [[[["weak", {"op": "insert", "table": "a", "row": {"a": 0, "a2a": ["set", [["named-uuid", "row1"], ["named-uuid", "row2"], ["uuid", "0e767b36-6822-4044-8307-d58467e04669"]]], "a2a1": ["named-uuid", "row1"], "a2b": ["named-uuid", "row3"]}, "uuid-name": "row1"}, {"op": "insert", "table": "a", "row": {"a": 1, "a2a": ["set", [["named-uuid", "row1"], ["named-uuid", "row2"]]], "a2a1": ["named-uuid", "row2"], "a2b": ["named-uuid", "row3"]}, "uuid-name": "row2"}, {"op": "insert", "table": "a", "row": {"a": 2, "a2a": ["set", [["named-uuid", "row1"], ["named-uuid", "row2"]]], "a2a1": ["named-uuid", "row2"], "a2b": ["named-uuid", "row4"]}}, {"op": "insert", "table": "b", "row": {"b": 2, "b2a": ["named-uuid", "row1"]}, "uuid-name": "row3"}, {"op": "insert", "table": "b", "row": {"b": 3, "b2a": ["named-uuid", "row2"]}, "uuid-name": "row4"}]]], dnl Check that the nonexistent row UUID we added to row a0 was deleted, dnl and that other rows were inserted as requested. [[["weak", {"op": "select", "table": "a", "where": [], "columns": ["_uuid", "a2a", "a2a1", "a2b"], "sort": ["a"]}]]], [[["weak", {"op": "select", "table": "b", "where": [], "columns": ["_uuid", "b", "b2a"], "sort": ["b"]}]]], dnl Try to insert invalid all-zeros weak reference (the default) into dnl "a2b", which requires exactly one value. [[["weak", {"op": "insert", "table": "a", "row": {"a2a1": ["named-uuid", "me"]}, "uuid-name": "me"}]]], dnl Try to delete row from "b" that is referred to by weak references dnl from "a" table "a2b" column that requires exactly one value. [[["weak", {"op": "delete", "table": "b", "where": [["b", "==", 3]]}]]], dnl Try to delete row from "a" that is referred to by weak references dnl from "a" table "a2a1" column that requires exactly one value. [[["weak", {"op": "delete", "table": "a", "where": [["a", "==", 1]]}]]], dnl Delete the row that had the reference that caused the previous dnl deletion to fail, then check that other rows are unchanged. [[["weak", {"op": "delete", "table": "a", "where": [["a", "==", 2]]}]]], [[["weak", {"op": "select", "table": "a", "where": [], "columns": ["_uuid", "a2a", "a2a1", "a2b"], "sort": ["a"]}]]], [[["weak", {"op": "select", "table": "b", "where": [], "columns": ["_uuid", "b", "b2a"], "sort": ["b"]}]]], dnl Delete row a0 then check that references to it were removed. [[["weak", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}]]], [[["weak", {"op": "select", "table": "a", "where": [], "columns": ["_uuid", "a2a", "a2a1", "a2b"], "sort": ["a"]}]]], [[["weak", {"op": "select", "table": "b", "where": [], "columns": ["_uuid", "b", "b2a"], "sort": ["b"]}]]], dnl Delete row a1 then check that references to it were removed. [[["weak", {"op": "delete", "table": "a", "where": [["a", "==", 1]]}]]], [[["weak", {"op": "select", "table": "a", "where": [], "columns": ["_uuid", "a2a", "a2a1", "a2b"], "sort": ["a"]}]]], [[["weak", {"op": "select", "table": "b", "where": [], "columns": ["_uuid", "b", "b2a"], "sort": ["b"]}]]]], [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<2>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<4>"]}]}] [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}] [{"uuid":["uuid","<5>"]},{"details":"Weak reference column \"a2b\" in \"a\" row <5> (inserted within this transaction) contained all-zeros UUID (probably as the default value for this column) but deleting this value caused a constraint volation because this column is not allowed to be empty.","error":"constraint violation"}] [{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2b\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}] [{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2a1\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}] [{"count":1}] [{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}] [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}] [{"count":1}] [{"rows":[{"_uuid":["uuid","<1>"],"a2a":["uuid","<1>"],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}] [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}] [{"count":1}] [{"rows":[]}] [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}] ]]) OVSDB_CHECK_EXECUTION([immutable columns], [immutable_schema], [[[["immutable", {"op": "insert", "table": "a", "row": {"i": 5}, "uuid-name": "row1"}]]], [[["immutable", {"op": "update", "table": "a", "row": {"i": 10}, "where": []}]]], [[["immutable", {"op": "update", "table": "a", "row": {"i": 5}, "where": []}]]], [[["immutable", {"op": "mutate", "table": "a", "where": [], "mutations": [["i", "-=", 5]]}]]], [[["immutable", {"op": "mutate", "table": "a", "where": [], "mutations": [["i", "*=", 1]]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"details":"Cannot update immutable column i in table a.","error":"constraint violation","syntax":"{\"op\":\"update\",\"row\":{\"i\":10},\"table\":\"a\",\"where\":[]}"}] [{"details":"Cannot update immutable column i in table a.","error":"constraint violation","syntax":"{\"op\":\"update\",\"row\":{\"i\":5},\"table\":\"a\",\"where\":[]}"}] [{"details":"Cannot mutate immutable column i in table a.","error":"constraint violation","syntax":"[\"i\",\"-=\",5]"}] [{"details":"Cannot mutate immutable column i in table a.","error":"constraint violation","syntax":"[\"i\",\"*=\",1]"}] ]]) OVSDB_CHECK_EXECUTION([garbage collection], [gc_schema], [dnl Check that inserting a row without any references is a no-op. [[["gc", {"op": "insert", "table": "a", "row": {"a": 0}}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]], dnl Check that inserting a chain of rows that reference each other dnl in turn is also a no-op. [[["gc", {"op": "insert", "table": "a", "row": {"a": 0, "a2a": ["named-uuid", "row1"]}, "uuid-name": "row0"}, {"op": "insert", "table": "a", "row": {"a": 1, "a2a": ["named-uuid", "row2"]}, "uuid-name": "row1"}, {"op": "insert", "table": "a", "row": {"a": 2, "a2a": ["named-uuid", "row3"]}, "uuid-name": "row2"}, {"op": "insert", "table": "a", "row": {"a": 3}, "uuid-name": "row3"}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]], dnl Check that inserting a pair of rows that mutually reference each dnl other causes the rows to be retained. [[["gc", {"op": "insert", "table": "a", "row": {"a": 4, "a2a": ["named-uuid", "row5"]}, "uuid-name": "row4"}, {"op": "insert", "table": "a", "row": {"a": 5, "a2a": ["named-uuid", "row4"]}, "uuid-name": "row5"}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"], "sort": ["a"]}]]], dnl Check that unreferencing one of the rows causes the other to be deleted. [[["gc", {"op": "update", "table": "a", "where": [["a", "==", 4]], "row": {"a2a": ["set", []]}}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]], dnl Check that inserting a pair of rows that mutually weak reference each dnl other is a no-op. [[["gc", {"op": "insert", "table": "a", "row": {"a": 6, "wa2a": ["named-uuid", "row7"]}, "uuid-name": "row6"}, {"op": "insert", "table": "a", "row": {"a": 7, "wa2a": ["named-uuid", "row6"]}, "uuid-name": "row7"}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]], dnl Check that a circular chain of rows is retained. [[["gc", {"op": "insert", "table": "a", "row": {"a": 8, "a2a": ["named-uuid", "row9"]}, "uuid-name": "row8"}, {"op": "insert", "table": "a", "row": {"a": 9, "a2a": ["named-uuid", "row10"]}, "uuid-name": "row9"}, {"op": "insert", "table": "a", "row": {"a": 10, "a2a": ["named-uuid", "row11"]}, "uuid-name": "row10"}, {"op": "insert", "table": "a", "row": {"a": 11, "a2a": ["named-uuid", "row8"]}, "uuid-name": "row11"}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"], "sort": ["a"]}]]], dnl Check that breaking the chain causes all of the rows to be deleted. [[["gc", {"op": "update", "table": "a", "where": [["a", "==", 9]], "row": {"a2a": ["set", []]}}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]], dnl Check that inserting a row only referenced by itself is a no-op. [[["gc", {"op": "insert", "table": "a", "row": {"a": 12, "a2a": ["named-uuid", "self"]}, "uuid-name": "self"}]]], [[["gc", {"op": "select", "table": "a", "where": [], "columns": ["a"]}]]]], [[[{"uuid":["uuid","<0>"]}] [{"rows":[]}] [{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}] [{"rows":[]}] [{"uuid":["uuid","<5>"]},{"uuid":["uuid","<6>"]}] [{"rows":[{"a":4},{"a":5}]}] [{"count":1}] [{"rows":[]}] [{"uuid":["uuid","<7>"]},{"uuid":["uuid","<8>"]}] [{"rows":[]}] [{"uuid":["uuid","<9>"]},{"uuid":["uuid","<10>"]},{"uuid":["uuid","<11>"]},{"uuid":["uuid","<12>"]}] [{"rows":[{"a":8},{"a":9},{"a":10},{"a":11}]}] [{"count":1}] [{"rows":[]}] [{"uuid":["uuid","<13>"]}] [{"rows":[]}] ]])]) OVSDB_CHECK_EXECUTION([insert rows, count with mutation], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [["name", "==", "zero"]], "mutations": []}]]], [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [["name", "==", "one"]], "mutations": []}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "one"}, "uuid-name": "first"}]]], [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [["name", "==", "one"]], "mutations": []}]]], [[["ordinals", {"op": "delete", "table": "ordinals", "where": [["name", "==", "zero"]]}]]], [[["ordinals", {"op": "mutate", "table": "ordinals", "where": [], "mutations": []}]]]], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"count":1}] [{"count":1}] [{"uuid":["uuid","<2>"]}] [{"count":2}] [{"count":1}] [{"count":2}] ]]) EXECUTION_EXAMPLES openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-idl.at000066400000000000000000003627231514270232600223500ustar00rootroot00000000000000AT_BANNER([OVSDB -- interface description language (IDL)]) m4_divert_text([PREPARE_TESTS], [ # ovsdb_cluster_leader [REMOTES] [DATABASE] # # Returns the leader of the DATABASE cluster. ovsdb_cluster_leader () { remotes=$(echo $1 | tr "," "\n") for remote in $remotes; do ovsdb-client dump $remote _Server Database name leader | grep $2 | grep -q true if [[ $? == 0 ]]; then port=$(echo $remote | cut -d':' -f 3) log=$(grep --include=s\*.log -rlnw -e "listening on port $port" ./) pid=$(echo $log | sed 's/\(.*\.\)log/\1pid/' ) echo "${remote}|${pid}" return fi done }]) # OVSDB_START_IDLTEST([REMOTE], [SCHEMA]) # # Creates a database using SCHEMA (default: idltest.ovsschema) and # starts a database server listening on punix:socket and REMOTE (if # specified). m4_define([OVSDB_START_IDLTEST], [ AT_CHECK([ovsdb-tool create db dnl m4_if([$2], [], [$abs_srcdir/idltest.ovsschema], [$2])]) PKIDIR=$abs_top_builddir/tests AT_CHECK([ovsdb-server -vconsole:warn -vfile:dbg --log-file dnl --detach --no-chdir --pidfile --remote=punix:socket dnl m4_if(m4_substr($1, 0, 5), [pssl:], [--private-key=$PKIDIR/testpki-privkey2.pem dnl --certificate=$PKIDIR/testpki-cert2.pem dnl --ca-cert=$PKIDIR/testpki-cacert.pem], []) dnl m4_if([$1], [], [], [--remote=$1]) db dnl ]) on_exit 'kill `cat ovsdb-server.pid`' ]) # OVSDB_CLUSTER_START_IDLTEST([N], [REMOTE]) # # Creates a clustered database using idltest.ovsschema and starts a database # cluster of N servers listening on punix:socket and REMOTE (if specified). m4_define([OVSDB_CLUSTER_START_IDLTEST], [n=$1 AT_CHECK([ovsdb-tool create-cluster s1.db \ $abs_srcdir/idltest.ovsschema unix:s1.raft]) cid=$(ovsdb-tool db-cid s1.db) schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) for i in $(seq 2 $n); do AT_CHECK([ovsdb-tool join-cluster s$i.db \ $schema_name unix:s$i.raft unix:s1.raft]) done on_exit 'kill $(cat s*.pid)' for i in $(seq $n); do AT_CHECK([ovsdb-server -vraft -vconsole:warn -vfile:dbg --detach \ --no-chdir --log-file=s$i.log --pidfile=s$i.pid \ --unixctl=s$i --remote=punix:s$i.ovsdb \ m4_if([$2], [], [], [--remote=$2]) s$i.db]) done for i in $(seq $n); do OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s$i cluster/status ${schema_name} \ | grep -q 'Status: cluster member']) done ]) # OVSDB_CHECK_IDL_C(TITLE, [PRE-IDL-TXN], TRANSACTIONS, OUTPUT, [KEYWORDS], # [FILTER]) # # Creates a database with a schema derived from idltest.ovsidl, runs # each PRE-IDL-TXN (if any), starts an ovsdb-server on that database, # and runs "test-ovsdb idl" passing each of the TRANSACTIONS along. # # Checks that the overall output is OUTPUT. Before comparison, the # output is sorted (using "sort") and UUIDs in the output are replaced # by markers of the form where N is a number. The first unique # UUID is replaced by <0>, the next by <1>, and so on. If a given # UUID appears more than once it is always replaced by the same # marker. If FILTER is supplied then the output is also filtered # through the specified program. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_IDL_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses OVSDB_IDL_WRITE_CHANGED_ONLY. m4_define([OVSDB_CHECK_IDL_WRITE_CHANGED_ONLY_C], [AT_SETUP([ovsdb-idl - $1 - write-changed-only - C]) AT_KEYWORDS([ovsdb server idl positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 --write-changed-only idl unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses tcp. m4_define([OVSDB_CHECK_IDL_TCP_C], [AT_SETUP([ovsdb-idl - $1 - C - tcp]) AT_KEYWORDS([ovsdb server idl positive tcp socket $5]) OVSDB_START_IDLTEST(["ptcp:0:127.0.0.1"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:127.0.0.1:$TCP_PORT $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses tcp6. m4_define([OVSDB_CHECK_IDL_TCP6_C], [AT_SETUP([ovsdb-idl - $1 - C - tcp6]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test $HAVE_IPV6 = no]) AT_KEYWORDS([ovsdb server idl positive tcp6 socket $5]) OVSDB_START_IDLTEST(["ptcp:0:[[::1]]"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact tcp:[[::1]]:$TCP_PORT $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:[[::1]]:$TCP_PORT $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses the Python IDL implementation. m4_define([OVSDB_CHECK_IDL_PY], [AT_SETUP([ovsdb-idl - $1 - Python3]) AT_KEYWORDS([ovsdb server idl positive Python $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_REGISTER_COLUMNS_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - register_columns]) AT_KEYWORDS([ovsdb server idl positive Python register_columns $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) m4_define([REGISTER], m4_joinall([?], [], [simple:b,ba,i,ia,r,ra,s,sa,u,ua], [simple3:name,uset,uref], [simple4:name], [simple6:name,weak_ref], [link1:i,k,ka,l2], [link2:i,l1], [indexed:i], [singleton:name])) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema \ unix:socket REGISTER $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses the Python IDL implementation with tcp m4_define([OVSDB_CHECK_IDL_TCP_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - tcp]) AT_KEYWORDS([ovsdb server idl positive Python with tcp socket $5]) OVSDB_START_IDLTEST(["ptcp:0:127.0.0.1"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema tcp:127.0.0.1:$TCP_PORT $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses the Python IDL implementation with tcp # with multiple remotes with only one remote reachable m4_define([OVSDB_CHECK_IDL_TCP_MULTIPLE_REMOTES_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 (multiple remotes) - tcp]) AT_KEYWORDS([ovsdb server idl positive Python with tcp socket $5]) OVSDB_START_IDLTEST(["ptcp:0:127.0.0.1"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) WRONG_PORT_1=$((TCP_PORT + 101)) WRONG_PORT_2=$((TCP_PORT + 102)) remote=tcp:127.0.0.1:$WRONG_PORT_1,tcp:127.0.0.1:$TCP_PORT,tcp:127.0.0.1:$WRONG_PORT_2 m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t20 idl $srcdir/idltest.ovsschema $remote $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses the Python IDL implementation with tcp6 m4_define([OVSDB_CHECK_IDL_TCP6_PY], [AT_SETUP([ovsdb-idl $1 - Python3 - tcp6]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test $HAVE_IPV6 = no]) AT_KEYWORDS([ovsdb server idl positive Python with tcp6 socket $5]) OVSDB_START_IDLTEST(["ptcp:0:[[::1]]"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) echo "TCP_PORT=$TCP_PORT" m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact "tcp:[[::1]]:$TCP_PORT" $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema tcp:[[::1]]:$TCP_PORT $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_TCP6_MULTIPLE_REMOTES_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - tcp6]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test $HAVE_IPV6 = no]) AT_KEYWORDS([ovsdb server idl positive Python with tcp6 socket $5]) OVSDB_START_IDLTEST(["ptcp:0:[[::1]]"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) WRONG_PORT_1=$((TCP_PORT + 101)) WRONG_PORT_2=$((TCP_PORT + 102)) remote="tcp:[[::1]]:$WRONG_PORT_1,tcp:[[::1]]:$TCP_PORT,tcp:[[::1]]:$WRONG_PORT_2" m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact "tcp:[[::1]]:$TCP_PORT" $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t20 idl $srcdir/idltest.ovsschema $remote $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) # same as OVSDB_CHECK_IDL but uses the Python IDL implementation with SSL/TLS m4_define([OVSDB_CHECK_IDL_SSL_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - SSL/TLS]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) $PYTHON3 -c "import ssl" SSL_PRESENT=$? AT_SKIP_IF([test $SSL_PRESENT != 0]) AT_KEYWORDS([ovsdb server idl positive Python with ssl tls socket $5]) AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema], [0], [stdout], [ignore]) PKIDIR=$abs_top_builddir/tests AT_CHECK([ovsdb-server -vconsole:warn --log-file --detach --no-chdir \ --pidfile \ --private-key=$PKIDIR/testpki-privkey2.pem \ --certificate=$PKIDIR/testpki-cert2.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ --remote=pssl:0:127.0.0.1 db]) on_exit 'kill `cat ovsdb-server.pid`' PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_if([$2], [], [], [AT_CHECK([ovsdb-client \ --private-key=$PKIDIR/testpki-privkey2.pem \ --certificate=$PKIDIR/testpki-cert2.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ transact "ssl:127.0.0.1:$TCP_PORT" $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema \ ssl:127.0.0.1:$TCP_PORT $PKIDIR/testpki-privkey.pem \ $PKIDIR/testpki-cert.pem $PKIDIR/testpki-cacert.pem $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN([" /unexpected SSL\/TLS connection close/d /Protocol error/d "]) AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL], [OVSDB_CHECK_IDL_C($@) OVSDB_CHECK_IDL_WRITE_CHANGED_ONLY_C($@) OVSDB_CHECK_IDL_TCP_C($@) OVSDB_CHECK_IDL_TCP6_C($@) OVSDB_CHECK_IDL_PY($@) OVSDB_CHECK_IDL_REGISTER_COLUMNS_PY($@) OVSDB_CHECK_IDL_TCP_PY($@) OVSDB_CHECK_IDL_TCP_MULTIPLE_REMOTES_PY($@) OVSDB_CHECK_IDL_TCP6_PY($@) OVSDB_CHECK_IDL_TCP6_MULTIPLE_REMOTES_PY($@) OVSDB_CHECK_IDL_SSL_PY($@)]) # This test uses the Python IDL implementation with passive tcp m4_define([OVSDB_CHECK_IDL_PASSIVE_TCP_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - ptcp]) AT_KEYWORDS([ovsdb server idl positive Python with tcp socket $5]) # find free TCP port OVSDB_START_IDLTEST(["ptcp:0:127.0.0.1"]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) OVSDB_SERVER_SHUTDOWN rm -f db # start OVSDB server in passive mode OVSDB_START_IDLTEST(["tcp:127.0.0.1:$TCP_PORT"]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl_passive $srcdir/idltest.ovsschema ptcp:127.0.0.1:$TCP_PORT $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP ]) OVSDB_CHECK_IDL_PASSIVE_TCP_PY([simple passive idl, initially empty, select empty], [], [['["idltest",{"op":"select","table":"link1","where":[]}]']], [[000: empty 001: {"error":null,"result":[{"rows":[]}]} 002: done ]]) OVSDB_CHECK_IDL([simple idl, initially empty, no ops], [], [], [000: empty 001: done ]) OVSDB_CHECK_IDL([simple idl, initially empty, various ops], [], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": true}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"r": 123.5}}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": -1, "r": 125, "b": false, "s": "", "ia": ["set", [1]], "ra": ["set", [1.5]], "ba": ["set", [false]], "sa": ["set", []], "ua": ["set", []]}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [["i", "<", 1]], "row": {"s": "newstring"}}]' \ '["idltest", {"op": "delete", "table": "simple", "where": [["i", "==", 0]]}]' \ 'reconnect']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: table simple: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 003: {"error":null,"result":[{"count":2}]} 004: table simple: i=0 r=0 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 005: {"error":null,"result":[{"count":2}]} 006: table simple: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]} 008: table simple: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 008: table simple: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 008: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 009: {"error":null,"result":[{"count":2}]} 010: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 010: table simple: i=0 r=123.5 b=true s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 010: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 011: {"error":null,"result":[{"count":1}]} 012: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 012: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 013: reconnect 014: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 014: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 015: done ]]) OVSDB_CHECK_IDL([simple idl, initially populated], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]']], [['["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": true}}]']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 000: table simple: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> 001: {"error":null,"result":[{"count":2}]} 002: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> 003: done ]]) OVSDB_CHECK_IDL([simple idl, writing via IDL], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]']], [['verify 0 b, verify 1 r, set 0 b 1, set 1 r 3.5' \ 'insert 2, verify 2 i, verify 1 b, delete 1']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 000: table simple: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> 001: commit, status=success 002: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=3.5 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> 003: commit, status=success 004: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<6> 005: done ]]) OVSDB_CHECK_IDL([simple idl, writing via IDL with unicode], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "(╯°□°)╯︵ ┻━┻"}}]']], [['set 0 b 1, insert 1, set 1 s "¯\_(ツ)_/¯"']], [[000: table simple: i=0 r=0 b=false s=(╯°□°)╯︵ ┻━┻ u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: commit, status=success 002: table simple: i=0 r=0 b=true s=(╯°□°)╯︵ ┻━┻ u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=0 b=false s="¯\_(ツ)_/¯" u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 003: done ]]) OVSDB_CHECK_IDL([simple idl, inserting without modifying a column, insert_no_columns_changed], [], [['insert_no_columns_changed']], [[000: empty 001: commit, status=success 002: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: done ]]) m4_define([OVSDB_CHECK_IDL_PY_WITH_EXPOUT], [AT_SETUP([ovsdb-idl - $1 - Python3]) AT_KEYWORDS([ovsdb server idl positive Python $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket $3], [0], [stdout], [ignore]) echo "$4" > expout AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [expout]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_PY_WITH_EXPOUT([simple idl, writing large data via IDL with unicode], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "'$(printf "测试超过四千零九十六个字节的中文字符串以使解码出现问题。%.0s" {1..50})'"}}]']], [['set 0 b 1, insert 1, set 1 s '$(printf "测试超过四千零九十六个字节的中文字符串以使解码出现问题。%.0s" {1..100})'']], [[000: table simple: i=0 r=0 b=false s=$(printf "测试超过四千零九十六个字节的中文字符串以使解码出现问题。%.0s" {1..50}) u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: commit, status=success 002: table simple: i=0 r=0 b=true s=$(printf "测试超过四千零九十六个字节的中文字符串以使解码出现问题。%.0s" {1..50}) u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=0 b=false s=$(printf "测试超过四千零九十六个字节的中文字符串以使解码出现问题。%.0s" {1..100}) u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 003: done]]) OVSDB_CHECK_IDL([simple idl, handling verification failure], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0}}, {"op": "insert", "table": "simple", "row": {}}]']], [['set 0 b 1' \ '+["idltest", {"op": "update", "table": "simple", "where": [["i", "==", 1]], "row": {"r": 5.0}}]' \ '+verify 1 r, set 1 r 3' \ 'verify 1 r, set 1 r 3' \ ]], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 000: table simple: i=1 r=2 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 001: commit, status=success 002: {"error":null,"result":[{"count":1}]} 003: commit, status=try again 004: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: i=1 r=5 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 005: commit, status=success 006: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: table simple: i=1 r=3 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 007: done ]]) OVSDB_CHECK_IDL([simple idl, increment operation], [['["idltest", {"op": "insert", "table": "simple", "row": {}}]']], [['set 0 r 2.0, increment 0']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: commit, status=success, increment=1 002: table simple: i=1 r=2 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: done ]]) OVSDB_CHECK_IDL([simple idl, aborting], [['["idltest", {"op": "insert", "table": "simple", "row": {}}]']], [['set 0 r 2.0, abort' \ '+set 0 b 1']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: commit, status=aborted 002: commit, status=success 003: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: done ]]) OVSDB_CHECK_IDL([simple idl, destroy without commit or abort], [['["idltest", {"op": "insert", "table": "simple", "row": {}}]']], [['set 0 r 2.0, destroy' \ '+set 0 b 1']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: destroy 002: commit, status=success 003: table simple: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, false condition], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}]']], [['condition simple []' \ 'condition simple [true]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, true condition], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}]']], [['condition simple []' \ 'condition simple [true]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: done ]]) dnl This test ensures that the first explicitly set monitor condition dnl is sent to the server. OVSDB_CHECK_IDL([simple idl, conditional, wait for condition], [], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}]' \ 'condition simple [true]' \ '^["idltest", {"op": "insert", "table": "simple", "row": {"i": 2, "r": 4.0, "b": true}}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table simple: i=1 r=2 b=true s= u=<1> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 003: simple: conditions unchanged 004: {"error":null,"result":[{"uuid":["uuid","<2>"]}]} 005: table simple: i=1 r=2 b=true s= u=<1> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 005: table simple: i=2 r=4 b=true s= u=<1> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 006: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, multiple clauses in condition], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}, {"op": "insert", "table": "simple", "row": {"i": 2, "r": 3.0, "b": true}}]']], [['condition simple []' \ 'condition simple [["i","==",1],["i","==",2]]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: table simple: i=2 r=3 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 004: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, modify as insert due to condition], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}]']], [['condition simple []' \ 'condition simple [["i","==",1]]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, modify as delete due to condition], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}]']], [['condition simple []' \ 'condition simple [["i","==",1],["i","==",2]]' \ 'condition simple [["i","==",2]]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": 2, "r": 3.0, "b": true}}]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: simple: change conditions 005: empty 006: {"error":null,"result":[{"uuid":["uuid","<2>"]}]} 007: table simple: i=2 r=3 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 008: done ]]) OVSDB_CHECK_IDL([simple idl, conditional, multiple tables], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true}}, {"op": "insert", "table": "link1", "row": {"i": 0, "k": ["named-uuid", "self"]}, "uuid-name": "self"}, {"op": "insert", "table": "link2", "row": {"i": 2}, "uuid-name": "row0"}]']], [['condition simple [];link1 [];link2 []' \ 'condition simple [["i","==",1]]' \ 'condition link1 [["i","==",0]]' \ 'condition link2 [["i","==",3]]' \ '+["idltest", {"op": "insert", "table": "link2", "row": {"i": 3}, "uuid-name": "row0"}]']], [[000: link1: change conditions 000: link2: change conditions 000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: link1: change conditions 005: table link1: i=0 k=0 ka=[] l2= uuid=<2> 005: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: link2: change conditions 007: {"error":null,"result":[{"uuid":["uuid","<3>"]}]} 008: table link1: i=0 k=0 ka=[] l2= uuid=<2> 008: table link2: i=3 l1= uuid=<3> 008: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 009: done ]]) OVSDB_CHECK_IDL([indexed idl, modification and removal], [], [['["idltest", {"op": "insert", "table": "indexed", "row": {"i": 123 }}]' \ '["idltest", {"op": "update", "table": "indexed", "where": [["i", "==", 123]], "row": {"i": 456}}]' \ '["idltest", {"op": "delete", "table": "indexed", "where": [["i", "==", 456]]}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table indexed: i=123 uuid=<0> 003: {"error":null,"result":[{"count":1}]} 004: table indexed: i=456 uuid=<0> 005: {"error":null,"result":[{"count":1}]} 006: empty 007: done ]]) OVSDB_CHECK_IDL([self-linking idl, consistent ops], [], [['["idltest", {"op": "insert", "table": "link1", "row": {"i": 0, "k": ["named-uuid", "self"]}, "uuid-name": "self"}]' \ '["idltest", {"op": "insert", "table": "link1", "row": {"i": 1, "k": ["named-uuid", "row2"]}, "uuid-name": "row1"}, {"op": "insert", "table": "link1", "row": {"i": 2, "k": ["named-uuid", "row1"]}, "uuid-name": "row2"}]' \ '["idltest", {"op": "update", "table": "link1", "where": [["i", "==", 1]], "row": {"k": ["uuid", "#1#"]}}]' \ '["idltest", {"op": "update", "table": "link1", "where": [], "row": {"k": ["uuid", "#0#"]}}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table link1: i=0 k=0 ka=[] l2= uuid=<0> 003: {"error":null,"result":[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]}]} 004: table link1: i=0 k=0 ka=[] l2= uuid=<0> 004: table link1: i=1 k=2 ka=[] l2= uuid=<1> 004: table link1: i=2 k=1 ka=[] l2= uuid=<2> 005: {"error":null,"result":[{"count":1}]} 006: table link1: i=0 k=0 ka=[] l2= uuid=<0> 006: table link1: i=1 k=1 ka=[] l2= uuid=<1> 006: table link1: i=2 k=1 ka=[] l2= uuid=<2> 007: {"error":null,"result":[{"count":3}]} 008: table link1: i=0 k=0 ka=[] l2= uuid=<0> 008: table link1: i=1 k=0 ka=[] l2= uuid=<1> 008: table link1: i=2 k=0 ka=[] l2= uuid=<2> 009: done ]]) OVSDB_CHECK_IDL([self-linking idl, inconsistent ops], [], [['["idltest", {"op": "insert", "table": "link1", "row": {"i": 0, "k": ["uuid", "cf197cc5-c8c9-42f5-82d5-c71a9f2cb96b"]}}]' \ '+["idltest", {"op": "insert", "table": "link1", "uuid-name": "one", "row": {"i": 1, "k": ["named-uuid", "one"]}}, {"op": "insert", "table": "link1", "row": {"i": 2, "k": ["named-uuid", "one"]}}]' \ '["idltest", {"op": "update", "table": "link1", "where": [], "row": {"k": ["uuid", "c2fca39a-e69a-42a4-9c56-5eca85839ce9"]}}]' \ '+["idltest", {"op": "delete", "table": "link1", "where": [["_uuid", "==", ["uuid", "#1#"]]]}]' \ '+["idltest", {"op": "delete", "table": "link1", "where": [["_uuid", "==", ["uuid", "#2#"]]]}]' \ '["idltest", {"op": "delete", "table": "link1", "where": []}]' \ ]], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"details":"Table link1 column k row <0> references nonexistent row <1> in table link1.","error":"referential integrity violation"}]} 002: {"error":null,"result":[{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]} 003: table link1: i=1 k=1 ka=[] l2= uuid=<2> 003: table link1: i=2 k=1 ka=[] l2= uuid=<3> 004: {"error":null,"result":[{"count":2},{"details":"Table link1 column k row references nonexistent row <4> in table link1.","error":"referential integrity violation"}]} 005: {"error":null,"result":[{"count":1},{"details":"cannot delete link1 row <2> because of 1 remaining reference(s)","error":"referential integrity violation"}]} 006: {"error":null,"result":[{"count":1}]} 007: table link1: i=1 k=1 ka=[] l2= uuid=<2> 008: {"error":null,"result":[{"count":1}]} 009: empty 010: done ]], [], [[sed -e '/004:/s/row <[23]> references/row references/']]) OVSDB_CHECK_IDL([self-linking idl, sets], [], [['["idltest", {"op": "insert", "table": "link1", "row": {"i": 0, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i0"]]]}, "uuid-name": "i0"}, {"op": "insert", "table": "link1", "row": {"i": 1, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i1"]]]}, "uuid-name": "i1"}, {"op": "insert", "table": "link1", "row": {"i": 2, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i2"]]]}, "uuid-name": "i2"}, {"op": "insert", "table": "link1", "row": {"i": 3, "k": ["named-uuid", "i0"], "ka": ["set", [["named-uuid", "i3"]]]}, "uuid-name": "i3"}]' \ '["idltest", {"op": "update", "table": "link1", "where": [], "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "#1#"], ["uuid", "#2#"], ["uuid", "#3#"]]]}}]' \ '["idltest", {"op": "update", "table": "link1", "where": [["i", "==", 2]], "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "88702e78-845b-4a6e-ad08-cf68922ae84a"], ["uuid", "#2#"]]]}}]' \ '+["idltest", {"op": "delete", "table": "link1", "where": []}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]} 002: table link1: i=0 k=0 ka=[0] l2= uuid=<0> 002: table link1: i=1 k=0 ka=[1] l2= uuid=<1> 002: table link1: i=2 k=0 ka=[2] l2= uuid=<2> 002: table link1: i=3 k=0 ka=[3] l2= uuid=<3> 003: {"error":null,"result":[{"count":4}]} 004: table link1: i=0 k=0 ka=[0 1 2 3] l2= uuid=<0> 004: table link1: i=1 k=0 ka=[0 1 2 3] l2= uuid=<1> 004: table link1: i=2 k=0 ka=[0 1 2 3] l2= uuid=<2> 004: table link1: i=3 k=0 ka=[0 1 2 3] l2= uuid=<3> 005: {"error":null,"result":[{"count":1},{"details":"Table link1 column ka row <2> references nonexistent row <4> in table link1.","error":"referential integrity violation"}]} 006: {"error":null,"result":[{"count":4}]} 007: empty 008: done ]]) OVSDB_CHECK_IDL([external-linking idl, consistent ops], [], [['["idltest", {"op": "insert", "table": "link2", "row": {"i": 0}, "uuid-name": "row0"}, {"op": "insert", "table": "link1", "row": {"i": 1, "k": ["named-uuid", "row1"], "l2": ["set", [["named-uuid", "row0"]]]}, "uuid-name": "row1"}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: table link1: i=1 k=1 ka=[] l2=0 uuid=<1> 002: table link2: i=0 l1= uuid=<0> 003: done ]]) OVSDB_CHECK_IDL([singleton idl, constraints], [], [['["idltest", {"op": "insert", "table": "singleton", "row": {"name": "foo"}}]' \ '["idltest", {"op": "insert", "table": "singleton", "row": {"name": "bar"}}]' \ '+["idltest", {"op": "delete", "table": "singleton", "where": [["_uuid", "==", ["uuid", "#0#"]]]}, {"op": "insert", "table": "singleton", "row": {"name": "bar"}}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table singleton: name=foo uuid=<0> 003: {"error":null,"result":[{"uuid":["uuid","<1>"]},{"details":"transaction causes \"singleton\" table to contain 2 rows, greater than the schema-defined limit of 1 row(s)","error":"constraint violation"}]} 004: {"error":null,"result":[{"count":1},{"uuid":["uuid","<2>"]}]} 005: table singleton: name=bar uuid=<2> 006: done ]]) dnl This test creates a database with references and checks that deleting both dnl source and destination rows of a reference in a single update doesn't leak dnl rows that got orphaned when processing the update. OVSDB_CHECK_IDL([simple idl, references, multiple deletes], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "weak_row0"}, {"op": "insert", "table": "simple6", "row": {"name": "first_row", "weak_ref": ["set", [["named-uuid", "weak_row0"]] ]}}]']], [['["idltest", {"op": "delete", "table": "simple", "where": [["s", "==", "row0_s"]]}, {"op": "delete", "table": "simple6", "where": [["name", "==", "first_row"]]}]']], [[000: table simple6: name=first_row weak_ref=[<0>] uuid=<1> 000: table simple: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 001: {"error":null,"result":[{"count":1},{"count":1}]} 002: empty 003: done ]]) OVSDB_CHECK_IDL_PY([external-linking idl, insert ops], [], [['linktest']], [[000: empty 001: commit, status=success 002: table link1: i=1 k=1 ka=[1] l2= uuid=<0> 002: table link1: i=2 k=1 ka=[1 2] l2= uuid=<1> 003: done ]]) OVSDB_CHECK_IDL_PY([getattr idl, insert ops], [], [['getattrtest']], [[000: empty 001: commit, status=success 002: table link1: i=2 k=2 ka=[] l2= uuid=<0> 003: done ]]) OVSDB_CHECK_IDL_PY([row-from-json idl, whats this], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1}}, {"op": "insert", "table": "simple", "row": {}}]']], [['notifytest insert 2, notifytest set 1 b 1, notifytest delete 0']], [[000: table simple: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 000: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 001: commit, status=success, events=create|2|None, delete|0|None, update|1|b 002: table simple: i=1 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 002: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 003: done ]]) AT_SETUP([ovsdb-idl - handling of missing tables and columns - C]) AT_KEYWORDS([ovsdb server idl positive]) # idltest2.ovsschema is the same as idltest.ovsschema, except that # table link2 and column l2 have been deleted. But the IDL still # expects them to be there, so this test checks that it properly # tolerates them being missing. OVSDB_START_IDLTEST([], ["$abs_srcdir/idltest2.ovsschema"]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket ['["idltest", {"op": "insert", "table": "link1", "row": {"i": 0, "k": ["named-uuid", "self"]}, "uuid-name": "self"}]' \ '["idltest", {"op": "insert", "table": "link1", "row": {"i": 1, "k": ["named-uuid", "row2"]}, "uuid-name": "row1"}, {"op": "insert", "table": "link1", "row": {"i": 2, "k": ["named-uuid", "row1"]}, "uuid-name": "row2"}]' \ '["idltest", {"op": "update", "table": "link1", "where": [["i", "==", 1]], "row": {"k": ["uuid", "#1#"]}}]' \ '["idltest", {"op": "update", "table": "link1", "where": [], "row": {"k": ["uuid", "#0#"]}}]']], [0], [stdout], [stderr]) AT_CHECK([sort stdout | uuidfilt], [0], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table link1: i=0 k=0 ka=[] l2= uuid=<0> 003: {"error":null,"result":[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]}]} 004: table link1: i=0 k=0 ka=[] l2= uuid=<0> 004: table link1: i=1 k=2 ka=[] l2= uuid=<1> 004: table link1: i=2 k=1 ka=[] l2= uuid=<2> 005: {"error":null,"result":[{"count":1}]} 006: table link1: i=0 k=0 ka=[] l2= uuid=<0> 006: table link1: i=1 k=1 ka=[] l2= uuid=<1> 006: table link1: i=2 k=1 ka=[] l2= uuid=<2> 007: {"error":null,"result":[{"count":3}]} 008: table link1: i=0 k=0 ka=[] l2= uuid=<0> 008: table link1: i=1 k=0 ka=[] l2= uuid=<1> 008: table link1: i=2 k=0 ka=[] l2= uuid=<2> 009: done ]]) # Check that ovsdb-idl figured out that table link2 and column l2 are missing. AT_CHECK([grep ovsdb_idl stderr | sort], [0], [dnl test-ovsdb|ovsdb_idl|idltest database lacks indexed table (database needs upgrade?) test-ovsdb|ovsdb_idl|idltest database lacks link2 table (database needs upgrade?) test-ovsdb|ovsdb_idl|idltest database lacks simple5 table (database needs upgrade?) test-ovsdb|ovsdb_idl|idltest database lacks simple6 table (database needs upgrade?) test-ovsdb|ovsdb_idl|idltest database lacks singleton table (database needs upgrade?) test-ovsdb|ovsdb_idl|link1 table in idltest database lacks l2 column (database needs upgrade?) test-ovsdb|ovsdb_idl|simple7 table in idltest database lacks id column (database needs upgrade?) ]) # Check that ovsdb-idl sent on "monitor" request and that it didn't # mention that table or column, and (for paranoia) that it did mention another # table and column. AT_CHECK([grep -c '"monitor\|monitor_cond"' stderr], [0], [2 ]) AT_CHECK([grep '"monitor\|monitor_cond"' stderr | grep link2], [1]) AT_CHECK([grep '"monitor\|monitor_cond"' stderr | grep l2], [1]) AT_CHECK([grep '"monitor\|monitor_cond"' stderr | grep -c '"link1"'], [0], [1 ]) AT_CHECK([grep '"monitor\|monitor_cond"' stderr | grep -c '"ua"'], [0], [1 ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP m4_define([OVSDB_CHECK_IDL_FETCH_COLUMNS_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - fetch]) AT_KEYWORDS([ovsdb server idl positive Python increment fetch $6]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket [$3] $4], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), [0], [$5]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_FETCH_COLUMNS], [OVSDB_CHECK_IDL_FETCH_COLUMNS_PY($@)]) OVSDB_CHECK_IDL_FETCH_COLUMNS([simple idl, initially populated], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]']], [?simple:i,r!], ['fetch 0 r'], [[000: table simple: i=0 uuid=<0> 000: table simple: i=1 uuid=<1> 001: commit, status=success 002: table simple: i=0 r=0 uuid=<0> 002: table simple: i=1 uuid=<1> 003: done ]]) m4_define([OVSDB_CHECK_IDL_WO_MONITOR_COND_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl monitor $4]) OVSDB_START_IDLTEST AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/disable-monitor-cond]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket $2], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$5],,, [[| $5]]), [0], [$3]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_WO_MONITOR_COND_PY], [AT_SETUP([ovsdb-idl - $1 - Python3]) AT_KEYWORDS([ovsdb server idl Python monitor $4]) OVSDB_START_IDLTEST AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/disable-monitor-cond]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket $2], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$5],,, [[| $5]]), [0], [$3]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_WO_MONITOR_COND], [OVSDB_CHECK_IDL_WO_MONITOR_COND_C($@) OVSDB_CHECK_IDL_WO_MONITOR_COND_PY($@)]) OVSDB_CHECK_IDL_WO_MONITOR_COND([simple idl disable monitor-cond], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": true}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"r": 123.5}}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": -1, "r": 125, "b": false, "s": "", "ia": ["set", [1]], "ra": ["set", [1.5]], "ba": ["set", [false]], "sa": ["set", []], "ua": ["set", []]}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [["i", "<", 1]], "row": {"s": "newstring"}}]' \ '["idltest", {"op": "delete", "table": "simple", "where": [["i", "==", 0]]}]' \ 'reconnect']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: table simple: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 003: {"error":null,"result":[{"count":2}]} 004: table simple: i=0 r=0 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 005: {"error":null,"result":[{"count":2}]} 006: table simple: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]} 008: table simple: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 008: table simple: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 008: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 009: {"error":null,"result":[{"count":2}]} 010: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 010: table simple: i=0 r=123.5 b=true s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 010: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 011: {"error":null,"result":[{"count":1}]} 012: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 012: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 013: reconnect 014: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 014: table simple: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 015: done ]]) m4_define([OVSDB_CHECK_IDL_TRACK_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl tracking positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_TRACK_WRITE_CHANGED_ONLY_C], [AT_SETUP([ovsdb-idl - $1 - write-changed-only - C]) AT_KEYWORDS([ovsdb server idl tracking positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c --write-changed-only idl unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_TRACK], [OVSDB_CHECK_IDL_TRACK_C($@) OVSDB_CHECK_IDL_TRACK_WRITE_CHANGED_ONLY_C($@)]) OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]']], [['["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": true}}]']], [[000: table simple: inserted row: i=1 r=2 b=true s=mystring u=<0> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<1> <2>] uuid=<3> 000: table simple: updated columns: b ba i ia r ra s sa u ua 001: {"error":null,"result":[{"count":2}]} 002: table simple: i=0 r=0 b=true s= u=<4> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<5> 002: table simple: i=1 r=2 b=true s=mystring u=<0> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<1> <2>] uuid=<3> 002: table simple: updated columns: b 003: done ]]) OVSDB_CHECK_IDL_TRACK([track, indexed idl, modification and removal], [], [['["idltest", {"op": "insert", "table": "indexed", "row": {"i": 123 }}]' \ '["idltest", {"op": "update", "table": "indexed", "where": [["i", "==", 123]], "row": {"i": 456}}]' \ '["idltest", {"op": "delete", "table": "indexed", "where": [["i", "==", 456]]}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: table indexed: inserted row: i=123 uuid=<0> 002: table indexed: updated columns: i 003: {"error":null,"result":[{"count":1}]} 004: table indexed: i=456 uuid=<0> 004: table indexed: updated columns: i 005: {"error":null,"result":[{"count":1}]} 006: empty 007: done ]]) dnl This test creates database with weak references and checks that orphan dnl rows created for weak references are not available for iteration via dnl list of tracked changes. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, orphan weak references], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "weak_row0"}, {"op": "insert", "table": "simple", "row": {"s": "row1_s"}, "uuid-name": "weak_row1"}, {"op": "insert", "table": "simple", "row": {"s": "row2_s"}, "uuid-name": "weak_row2"}, {"op": "insert", "table": "simple6", "row": {"name": "first_row", "weak_ref": ["set", [["named-uuid", "weak_row0"], ["named-uuid", "weak_row1"], ["named-uuid", "weak_row2"]] ]}}]']], [['condition simple []' \ 'condition simple [["s","==","row1_s"]]' \ '["idltest", {"op": "update", "table": "simple6", "where": [], "row": {"name": "new_name"}}]' \ '["idltest", {"op": "delete", "table": "simple6", "where": []}]']], [[000: simple: change conditions 001: table simple6: inserted row: name=first_row weak_ref=[] uuid=<0> 001: table simple6: updated columns: name weak_ref 002: simple: change conditions 003: table simple6: name=first_row weak_ref=[<1>] uuid=<0> 003: table simple: inserted row: i=0 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: table simple: updated columns: s 004: {"error":null,"result":[{"count":1}]} 005: table simple6: name=new_name weak_ref=[<1>] uuid=<0> 005: table simple6: updated columns: name 006: {"error":null,"result":[{"count":1}]} 007: table simple: i=0 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 008: done ]]) dnl This test creates database with weak references and checks that the dnl content of orphaned rows created for weak references after monitor dnl condition change are not leaked when the row is reinserted and deleted. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, orphan rows, conditional], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "weak_row0"}, {"op": "insert", "table": "simple", "row": {"s": "row1_s"}, "uuid-name": "weak_row1"}, {"op": "insert", "table": "simple6", "row": {"name": "first_row", "weak_ref": ["set", [["named-uuid", "weak_row0"]] ]}}]']], [['condition simple []' \ 'condition simple [["s","==","row0_s"]]' \ 'condition simple [["s","==","row1_s"]]' \ 'condition simple [["s","==","row0_s"]]' \ '["idltest", {"op": "delete", "table": "simple6", "where": []}]']], [[000: simple: change conditions 001: table simple6: inserted row: name=first_row weak_ref=[] uuid=<0> 001: table simple6: updated columns: name weak_ref 002: simple: change conditions 003: table simple6: name=first_row weak_ref=[<1>] uuid=<0> 003: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: table simple: updated columns: s 004: simple: change conditions 005: table simple6: name=first_row weak_ref=[] uuid=<0> 005: table simple: deleted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 005: table simple: inserted row: i=0 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 005: table simple: updated columns: s 006: simple: change conditions 007: table simple6: name=first_row weak_ref=[<1>] uuid=<0> 007: table simple: deleted row: i=0 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 007: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 007: table simple: updated columns: s 008: {"error":null,"result":[{"count":1}]} 009: table simple: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 010: done ]]) dnl This test checks that deleting the destination of a weak reference dnl without deleting the source, through monitor condition change, updates dnl the source tracked record. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, references, conditional delete], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s", "i": 0}, "uuid-name": "weak_row0"}, {"op": "insert", "table": "simple", "row": {"s": "row1_s", "i": 1}, "uuid-name": "weak_row1"}, {"op": "insert", "table": "simple6", "row": {"name": "first_row", "weak_ref": ["set", [["named-uuid", "weak_row0"], ["named-uuid", "weak_row1"]] ]}}]']], [['condition simple []' \ 'condition simple [["s","==","row0_s"]]' \ 'condition simple [["s","==","row1_s"]]' \ '["idltest", {"op": "update", "table": "simple6", "where": [], "row": {"name": "new_name"}}]' \ '["idltest", {"op": "delete", "table": "simple6", "where": []}]']], [[000: simple: change conditions 001: table simple6: inserted row: name=first_row weak_ref=[] uuid=<0> 001: table simple6: updated columns: name weak_ref 002: simple: change conditions 003: table simple6: name=first_row weak_ref=[<1>] uuid=<0> 003: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 003: table simple: updated columns: s 004: simple: change conditions 005: table simple6: name=first_row weak_ref=[<3>] uuid=<0> 005: table simple: deleted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 005: table simple: inserted row: i=1 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 005: table simple: updated columns: i s 006: {"error":null,"result":[{"count":1}]} 007: table simple6: name=new_name weak_ref=[<3>] uuid=<0> 007: table simple6: updated columns: name 008: {"error":null,"result":[{"count":1}]} 009: table simple: i=1 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 010: done ]]) dnl This test checks that deleting the destination of a reference updates the dnl source tracked record. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, references, single delete], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "uuid_row0_s"}, {"op": "insert", "table": "simple6", "row": {"name": "row0_s6", "weak_ref": ["set", [["named-uuid", "uuid_row0_s"]] ]}}]']], [['condition simple [true];simple6 [true]' \ '["idltest", {"op": "delete", "table": "simple", "where": []}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}}]']], [[000: simple6: conditions unchanged 000: simple: conditions unchanged 001: table simple6: inserted row: name=row0_s6 weak_ref=[<0>] uuid=<1> 001: table simple6: updated columns: name weak_ref 001: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 001: table simple: updated columns: s 002: {"error":null,"result":[{"count":1}]} 003: table simple6: name=row0_s6 weak_ref=[] uuid=<1> 003: table simple6: updated columns: weak_ref 003: table simple: deleted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 004: {"error":null,"result":[{"uuid":["uuid","<3>"]}]} 005: table simple6: name=row0_s6 weak_ref=[] uuid=<1> 005: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 005: table simple: updated columns: s 006: done ]]) OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, weak references, insert+delete batch], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "uuid_row0_s"}, {"op": "insert", "table": "simple6", "row": {"name": "row0_s6", "weak_ref": ["set", [["named-uuid", "uuid_row0_s"]] ]}}]']], [['condition simple [true];simple6 [true]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row1_s"}, "uuid-name": "uuid_row1_s"}, {"op": "mutate", "table": "simple6", "where": [["name", "==", "row0_s6"]], "mutations": [["weak_ref", "insert", ["set", [["named-uuid", "uuid_row1_s"]]]]]}]' \ '+["idltest", {"op": "delete", "table": "simple", "where": [["s", "==", "row1_s"]]}]' \ '+sleep' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row2_s"}}]']], [[000: simple6: conditions unchanged 000: simple: conditions unchanged 001: table simple6: inserted row: name=row0_s6 weak_ref=[<0>] uuid=<1> 001: table simple6: updated columns: name weak_ref 001: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 001: table simple: updated columns: s 002: {"error":null,"result":[{"uuid":["uuid","<3>"]},{"count":1}]} 003: {"error":null,"result":[{"count":1}]} 004: sleep 005: table simple6: name=row0_s6 weak_ref=[<0>] uuid=<1> 005: table simple6: updated columns: weak_ref 005: table simple: inserted/deleted row: i=0 r=0 b=false s=row1_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 005: table simple: updated columns: s 006: {"error":null,"result":[{"uuid":["uuid","<4>"]}]} 007: table simple6: name=row0_s6 weak_ref=[<0>] uuid=<1> 007: table simple: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 007: table simple: inserted row: i=0 r=0 b=false s=row2_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<4> 007: table simple: updated columns: s 008: done ]]) dnl This test checks that deleting both the destination and source of the dnl reference doesn't remove the reference in the source tracked record. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, weak references, multiple deletes], [['["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}, "uuid-name": "uuid_row0_s"}, {"op": "insert", "table": "simple6", "row": {"name": "row0_s6", "weak_ref": ["set", [["named-uuid", "uuid_row0_s"]] ]}}]']], [['condition simple [true];simple6 [true]' \ '["idltest", {"op": "delete", "table": "simple", "where": []}, {"op": "delete", "table": "simple6", "where": []}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}}]']], [[000: simple6: conditions unchanged 000: simple: conditions unchanged 001: table simple6: inserted row: name=row0_s6 weak_ref=[<0>] uuid=<1> 001: table simple6: updated columns: name weak_ref 001: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 001: table simple: updated columns: s 002: {"error":null,"result":[{"count":1},{"count":1}]} 003: table simple6: deleted row: name=row0_s6 weak_ref=[<0>] uuid=<1> 003: table simple: deleted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<0> 004: {"error":null,"result":[{"uuid":["uuid","<3>"]}]} 005: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 005: table simple: updated columns: s 006: done ]]) dnl This test checks that deleting both the destination and source of the dnl reference doesn't remove the reference in the source tracked record. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, strong references, multiple deletes], [['["idltest", {"op": "insert", "table": "simple4", "row": {"name": "row0_s4"}, "uuid-name": "uuid_row0_s4"}, {"op": "insert", "table": "simple3", "row": {"name": "row0_s3", "uref": ["set", [["named-uuid", "uuid_row0_s4"]] ]}}]']], [['condition simple [true];simple3 [true];simple4 [true]' \ '["idltest", {"op": "delete", "table": "simple3", "where": []}, {"op": "delete", "table": "simple4", "where": []}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}}]']], [[000: simple3: conditions unchanged 000: simple4: conditions unchanged 000: simple: conditions unchanged 001: table simple3: inserted row: name=row0_s3 uset=[] uref=[<0>] uuid=<1> 001: table simple3: updated columns: name uref 001: table simple4: inserted row: name=row0_s4 uuid=<0> 001: table simple4: updated columns: name 002: {"error":null,"result":[{"count":1},{"count":1}]} 003: table simple3: deleted row: name=row0_s3 uset=[] uref=[<0>] uuid=<1> 003: table simple4: deleted row: name=row0_s4 uuid=<0> 004: {"error":null,"result":[{"uuid":["uuid","<2>"]}]} 005: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<3> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 005: table simple: updated columns: s 006: done ]]) dnl This test checks that changing conditions to not include the target of dnl a strong reference also updates the source row when change tracking is dnl enabled. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially populated, strong references, conditional], [['["idltest", {"op": "insert", "table": "simple4", "row": {"name": "row0_s4"}, "uuid-name": "uuid_row0_s4"}, {"op": "insert", "table": "simple3", "row": {"name": "row0_s3", "uref": ["set", [["named-uuid", "uuid_row0_s4"]] ]}}]']], [['condition simple [true];simple3 [true];simple4 [true]' \ 'condition simple4 []' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}}]']], [[000: simple3: conditions unchanged 000: simple4: conditions unchanged 000: simple: conditions unchanged 001: table simple3: inserted row: name=row0_s3 uset=[] uref=[<0>] uuid=<1> 001: table simple3: updated columns: name uref 001: table simple4: inserted row: name=row0_s4 uuid=<0> 001: table simple4: updated columns: name 002: simple4: change conditions 003: table simple3: name=row0_s3 uset=[] uref=[] uuid=<1> 003: table simple4: deleted row: name=row0_s4 uuid=<0> 004: {"error":null,"result":[{"uuid":["uuid","<2>"]}]} 005: table simple3: name=row0_s3 uset=[] uref=[] uuid=<1> 005: table simple: inserted row: i=0 r=0 b=false s=row0_s u=<3> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 005: table simple: updated columns: s 006: done ]]) dnl This test checks that changing conditions to not include the target of dnl a strong reference also updates the source row when change tracking is dnl disabled. OVSDB_CHECK_IDL([simple idl, initially populated, strong references, conditional], [['["idltest", {"op": "insert", "table": "simple4", "row": {"name": "row0_s4"}, "uuid-name": "uuid_row0_s4"}, {"op": "insert", "table": "simple3", "row": {"name": "row0_s3", "uref": ["set", [["named-uuid", "uuid_row0_s4"]] ]}}]']], [['condition simple [true];simple3 [true];simple4 [true]' \ 'condition simple4 []' \ '["idltest", {"op": "insert", "table": "simple", "row": {"s": "row0_s"}}]']], [[000: simple3: conditions unchanged 000: simple4: conditions unchanged 000: simple: conditions unchanged 001: table simple3: name=row0_s3 uset=[] uref=[<0>] uuid=<1> 001: table simple4: name=row0_s4 uuid=<0> 002: simple4: change conditions 003: table simple3: name=row0_s3 uset=[] uref=[] uuid=<1> 004: {"error":null,"result":[{"uuid":["uuid","<2>"]}]} 005: table simple3: name=row0_s3 uset=[] uref=[] uuid=<1> 005: table simple: i=0 r=0 b=false s=row0_s u=<3> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 006: done ]]) dnl This test checks that deleting a row that refers to a row that was inserted dnl in the current IDL run works properly. OVSDB_CHECK_IDL_TRACK([track, simple idl, initially empty, strong references, insert and delete], [], [['["idltest", {"op": "insert", "table": "link1", "uuid-name": "uuid_l1", "row": {"i": 1, "k": ["named-uuid", "uuid_l1"]}}, {"op": "insert", "table": "link2", "row": {"i": 2, "l1": ["set", [["named-uuid", "uuid_l1"]]]}} ]' \ '+["idltest", {"op": "delete", "table": "link2", "where": [["i", "==", 2]]}]' \ '+sleep' ]], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: {"error":null,"result":[{"count":1}]} 003: sleep 004: table link1: inserted row: i=1 k=1 ka=[] l2= uuid=<0> 004: table link1: updated columns: i k 005: done ]]) OVSDB_CHECK_IDL_TRACK([track, simple idl, initially empty, various ops], [], [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": true}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"r": 123.5}}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": -1, "r": 125, "b": false, "s": "", "ia": ["set", [1]], "ra": ["set", [1.5]], "ba": ["set", [false]], "sa": ["set", []], "ua": ["set", []]}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [["i", "<", 1]], "row": {"s": "newstring"}}]' \ '["idltest", {"op": "delete", "table": "simple", "where": [["i", "==", 0]]}]' \ 'reconnect']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: table simple: inserted row: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<0> 002: table simple: updated columns: b ba i ia r ra s sa u ua 003: {"error":null,"result":[{"count":2}]} 004: table simple: i=0 r=0 b=true s= u=<5> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: updated columns: b 005: {"error":null,"result":[{"count":2}]} 006: table simple: i=0 r=123.5 b=true s= u=<5> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: table simple: i=1 r=123.5 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<0> 006: table simple: updated columns: r 006: table simple: updated columns: r 007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]} 008: table simple: inserted row: i=-1 r=125 b=false s= u=<5> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 008: table simple: updated columns: ba i ia r ra 009: {"error":null,"result":[{"count":2}]} 010: table simple: i=-1 r=125 b=false s=newstring u=<5> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 010: table simple: i=0 r=123.5 b=true s=newstring u=<5> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 010: table simple: updated columns: s 010: table simple: updated columns: s 011: {"error":null,"result":[{"count":1}]} 012: table simple: deleted row: i=0 r=123.5 b=true s=newstring u=<5> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 013: reconnect 014: table simple: inserted row: i=-1 r=125 b=false s=newstring u=<5> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 014: table simple: inserted row: i=1 r=123.5 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<0> 014: table simple: updated columns: b ba i ia r ra s sa u ua 014: table simple: updated columns: ba i ia r ra s 015: done ]]) m4_define([OVSDB_CHECK_IDL_PARTIAL_UPDATE_MAP_COLUMN], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl partial update map column positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-partial-update-map-column unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_PARTIAL_UPDATE_MAP_COLUMN([map, simple2 idl-partial-update-map-column, initially populated], [['["idltest", {"op":"insert", "table":"simple2", "row":{"name":"myString1","smap":["map",[["key1","value1"],["key2","value2"]]]} }]'] ], [], [[000: Getting records 001: name=myString1 smap=[[key1 : value1],[key2 : value2]] imap=[] 002: After insert element 003: name=String2 smap=[[key1 : myList1],[key2 : value2]] imap=[[3 : myids2]] 004: After insert duplicated element 005: name=String2 smap=[[key1 : myList1],[key2 : value2]] imap=[[3 : myids2]] 006: After delete element 007: name=String2 smap=[[key2 : value2]] imap=[[3 : myids2]] 008: After trying to delete a deleted element 009: name=String2 smap=[[key2 : value2]] imap=[[3 : myids2]] 010: After Create element, update smap and Delete element 011: name=String2 smap=[[key2 : value2]] imap=[[3 : myids2]] 012: After update smap and Delete element 014: End test ]]) OVSDB_CHECK_IDL_PY([partial-map idl], [['["idltest", {"op":"insert", "table":"simple2", "row":{"name":"myString1","smap":["map",[["key1","value1"],["key2","value2"]]]} }]'] ], [?simple2:name,smap,imap 'partialmapinsertelement' 'partialmapinsertmultipleelements' 'partialmapdelelements' 'partialmapmutatenew'], [[000: table simple2: name=myString1 smap=[(key1 value1) (key2 value2)] imap=[] uuid=<0> 001: commit, status=success 002: table simple2: name=String2 smap=[(key1 myList1) (key2 value2)] imap=[(3 myids2)] uuid=<0> 003: commit, status=success 004: table simple2: name=String2 smap=[(key1 myList1) (key2 myList2) (key3 myList3) (key4 myList4)] imap=[(3 myids2)] uuid=<0> 005: commit, status=success 006: table simple2: name=String2 smap=[(key2 myList2)] imap=[(3 myids2)] uuid=<0> 007: commit, status=success 008: table simple2: name=String2 smap=[(key2 myList2)] imap=[(3 myids2)] uuid=<0> 008: table simple2: name=String2New smap=[(key1 newList1) (key2 newList2)] imap=[] uuid=<1> 009: done ]]) OVSDB_CHECK_IDL_PY([partial-map update set refmap idl], [['["idltest", {"op":"insert", "table":"simple3", "row":{"name":"myString1"}}, {"op":"insert", "table":"simple5", "row":{"name":"myString2"}}]']], ['partialmapmutateirefmap'], [[000: table simple3: name=myString1 uset=[] uref=[] uuid=<0> 000: table simple5: name=myString2 irefmap=[] uuid=<1> 001: commit, status=success 002: table simple3: name=myString1 uset=[] uref=[] uuid=<0> 002: table simple5: name=myString2 irefmap=[(1 <0>)] uuid=<1> 003: done ]]) m4_define([OVSDB_CHECK_IDL_PARTIAL_UPDATE_SET_COLUMN], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl partial update set column positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-partial-update-set-column unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_PARTIAL_UPDATE_SET_COLUMN([set, simple3 idl-partial-update-set-column, initially populated], [['["idltest", {"op":"insert", "table":"simple3", "row":{"name":"mySet1","uset":["set", [[ "uuid", "0005b872-f9e5-43be-ae02-3184b9680e75" ], [ "uuid", "000d2f6a-76af-412f-b59d-e7bcd3e84eff" ]]]} }]'] ], [], [[000: Getting records 001: table simple3: name=mySet1 uset=[<0>,<1>] uref=[] uuid=<2> 002: After rename+add new value 003: table simple3: name=String2 uset=[<0>,<1>,<3>] uref=[] uuid=<2> 004: After add new value 005: table simple3: name=String2 uset=[<0>,<1>,<3>,<4>] uref=[] uuid=<2> 006: After delete value 007: table simple3: name=String2 uset=[<0>,<1>,<4>] uref=[] uuid=<2> 008: After trying to delete a deleted value 009: table simple3: name=String2 uset=[<0>,<1>,<4>] uref=[] uuid=<2> 010: After add to other table + set of strong ref 011: table simple3: name=String2 uset=[<0>,<1>,<4>] uref=[<5>] uuid=<2> 012: After Create element, update set and Delete element 013: table simple3: name=String2 uset=[<0>,<1>,<4>] uref=[<5>] uuid=<2> 014: End test ]]) OVSDB_CHECK_IDL_PY([partial-set idl], [['["idltest", {"op":"insert", "table":"simple3", "uuid-name":"newrow", "row":{"name":"mySet1","uset":["set", [[ "uuid", "0005b872-f9e5-43be-ae02-3184b9680e75" ]]]} }, {"op":"insert", "table":"simple4", "row":{"name":"seed"}}, {"op":"mutate", "table":"simple3", "where":[["_uuid", "==", ["named-uuid", "newrow"]]], "mutations": [["uset", "insert", ["set", [["uuid", "000d2f6a-76af-412f-b59d-e7bcd3e84eff"]]]]]}]'] ], ['partialrenamesetadd' 'partialduplicateadd' 'partialsetdel' 'partialsetref' 'partialsetoverrideops' 'partialsetadddelete' 'partialsetmutatenew'], [[000: table simple3: name=mySet1 uset=[<0> <1>] uref=[] uuid=<2> 001: commit, status=success 002: table simple3: name=String2 uset=[<0> <1> <3>] uref=[] uuid=<2> 003: commit, status=success 004: table simple3: name=String2 uset=[<0> <1> <3> <4>] uref=[] uuid=<2> 005: commit, status=success 006: table simple3: name=String2 uset=[<0> <1> <4>] uref=[] uuid=<2> 007: commit, status=success 008: table simple3: name=String2 uset=[<0> <1> <4>] uref=[<5>] uuid=<2> 008: table simple4: name=test uuid=<5> 009: commit, status=success 010: table simple3: name=String2 uset=[<4>] uref=[<5>] uuid=<2> 010: table simple4: name=test uuid=<5> 011: commit, status=success 012: table simple3: name=String2 uset=[<6> <7>] uref=[<5>] uuid=<2> 012: table simple4: name=test uuid=<5> 013: commit, status=success 014: table simple3: name=String2 uset=[<6> <7>] uref=[<5>] uuid=<2> 014: table simple3: name=String3 uset=[<8>] uref=[] uuid=<9> 014: table simple4: name=test uuid=<5> 015: done ]]) m4_define([OVSDB_CHECK_IDL_NOTIFY], [OVSDB_CHECK_IDL_PY([$1], [], [$2], [$3], [notify $4], [$5]) OVSDB_CHECK_IDL_SSL_PY([$1], [], [$2], [$3], [notify $4], [$5])]) OVSDB_CHECK_IDL_NOTIFY([simple link idl verify notify], [['track-notify' \ '["idltest", {"op": "insert", "table": "link1", "row": {"i": 1, "k": ["named-uuid", "l1row"], "l2": ["set", [["named-uuid", "l2row"]]]}, "uuid-name": "l1row"}, {"op": "insert", "table": "link2", "uuid-name": "l2row", "row": {"i": 2, "l1": ["set", [["named-uuid", "l1row"]]]}}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: event:create, row={i=1 l2=[<1>]}, uuid=<0>, updates=None 002: event:create, row={i=2 l1=[<0>]}, uuid=<1>, updates=None 002: table link1: i=1 k=1 ka=[] l2=2 uuid=<0> 002: table link2: i=2 l1=1 uuid=<1> 003: done ]]) OVSDB_CHECK_IDL_NOTIFY([simple idl verify notify], [['track-notify' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "mystring", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"b": false}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [], "row": {"r": 123.5}}]' \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": -1, "r": 125, "b": false, "s": "", "ia": ["set", [1]], "ra": ["set", [1.5]], "ba": ["set", [false]], "sa": ["set", []], "ua": ["set", []]}}]' \ '["idltest", {"op": "update", "table": "simple", "where": [["i", "<", 1]], "row": {"s": "newstring"}}]' \ '["idltest", {"op": "delete", "table": "simple", "where": [["i", "==", 0]]}]' \ 'reconnect']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]} 002: event:create, row={i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[]}, uuid=<1>, updates=None 002: event:create, row={i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>]}, uuid=<0>, updates=None 002: table simple: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 003: {"error":null,"result":[{"count":2}]} 004: event:update, row={i=1 r=2 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>]}, uuid=<0>, updates={b=true} 004: table simple: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: table simple: i=1 r=2 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 005: {"error":null,"result":[{"count":2}]} 006: event:update, row={i=0 r=123.5 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[]}, uuid=<1>, updates={r=0} 006: event:update, row={i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>]}, uuid=<0>, updates={r=2} 006: table simple: i=0 r=123.5 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 006: table simple: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]} 008: event:create, row={i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[]}, uuid=<6>, updates=None 008: table simple: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 008: table simple: i=0 r=123.5 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 008: table simple: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 009: {"error":null,"result":[{"count":2}]} 010: event:update, row={i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[]}, uuid=<6>, updates={s=} 010: event:update, row={i=0 r=123.5 b=false s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[]}, uuid=<1>, updates={s=} 010: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 010: table simple: i=0 r=123.5 b=false s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 010: table simple: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 011: {"error":null,"result":[{"count":1}]} 012: event:delete, row={i=0 r=123.5 b=false s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[]}, uuid=<1>, updates=None 012: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 012: table simple: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 013: reconnect 014: event:create, row={i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[]}, uuid=<6>, updates=None 014: event:create, row={i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>]}, uuid=<0>, updates=None 014: table simple: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6> 014: table simple: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0> 015: done ]]) OVSDB_CHECK_IDL_NOTIFY([indexed idl, modification and removal notify], [['track-notify' \ '["idltest", {"op": "insert", "table": "indexed", "row": {"i": 123 }}]' \ '["idltest", {"op": "update", "table": "indexed", "where": [["i", "==", 123]], "row": {"i": 456}}]' \ '["idltest", {"op": "delete", "table": "indexed", "where": [["i", "==", 456]]}]']], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]} 002: event:create, row={i=123}, uuid=<0>, updates=None 002: table indexed: i=123 uuid=<0> 003: {"error":null,"result":[{"count":1}]} 004: event:update, row={i=456}, uuid=<0>, updates={i=123} 004: table indexed: i=456 uuid=<0> 005: {"error":null,"result":[{"count":1}]} 006: empty 006: event:delete, row={i=456}, uuid=<0>, updates=None 007: done ]]) # Tests to verify the functionality of the one column compound index. # It tests index for one column string and integer indexes. # The run of test-ovsdb generates the output of the display of data using the different indexes defined in # the program. # Then, some at_checks are used to verify the correctness of the corresponding index as well as the existence # of all the rows involved in the test. m4_define([OVSDB_CHECK_IDL_COMPOUND_INDEX_SINGLE_COLUMN_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl compound_index_single_column compound_index positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) # Generate the data to be tested. AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-compound-index unix:socket $3], [0], [stdout], [ignore]) # Filter the rows of data that corresponds to the string index eliminating the extra columns of data. # This is done to verifiy that the output data is in the correct and expected order. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sed -e 's/ i=.*//g']], [0], [$4]) # Here, the data is filtered and sorted in order to have all the rows in the index and be # able to determined that all the involved rows are present. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sort -k 1,1n -k 2,2 -k 3,3]], [0], [$5]) # Filter the rows of data that corresponds to the integer index eliminating the extra columns of data. # This is done to verifiy that the output data is in the correct and expected order. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sed -e 's/ s=.*//g']], [0], [$6]) # Here again, the data is filtered and sorted in order to have all the rows in the index and be # able to determined that all the involved rows are present. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sort -k 1,1n -k 2,2 -k 3,3]], [0], [$7]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_COMPOUND_INDEX_SINGLE_COLUMN_C([Compound_index, single column test ], [['["idltest", {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 1, "b":true, "r":101.0}}, {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 2, "b":false, "r":102.0}}, {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 10, "b":true, "r":110.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 1, "b":false, "r":110.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":120.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":122.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 4, "b":true, "r":130.0}}, {"op": "insert", "table": "simple", "row": {"s":"List005", "i": 5, "b":true, "r":130.0}}, {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 20, "b":true, "r":220.0}}, {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 19, "b":true, "r":219.0}} ]']], [idl_compound_index_single_column], [001: s=List000 001: s=List000 001: s=List000 001: s=List001 001: s=List001 001: s=List001 001: s=List001 001: s=List005 001: s=List020 001: s=List020 003: s=List001 003: s=List001 003: s=List001 003: s=List001 ], [001: s=List000 i=1 b=True r=101.000000 001: s=List000 i=10 b=True r=110.000000 001: s=List000 i=2 b=False r=102.000000 001: s=List001 i=1 b=False r=110.000000 001: s=List001 i=2 b=True r=120.000000 001: s=List001 i=2 b=True r=122.000000 001: s=List001 i=4 b=True r=130.000000 001: s=List005 i=5 b=True r=130.000000 001: s=List020 i=19 b=True r=219.000000 001: s=List020 i=20 b=True r=220.000000 003: s=List001 i=1 b=False r=110.000000 003: s=List001 i=2 b=True r=120.000000 003: s=List001 i=2 b=True r=122.000000 003: s=List001 i=4 b=True r=130.000000 ], [002: i=1 002: i=1 002: i=2 002: i=2 002: i=2 002: i=4 002: i=5 002: i=10 002: i=19 002: i=20 004: i=5 005: i=4 005: i=5 006: i=5 006: i=10 006: i=19 006: i=20 006: i=54 007: i=5 007: i=19 007: i=20 007: i=30 007: i=54 008: i=1 008: i=1 008: i=2 008: i=2 008: i=2 008: i=5 008: i=19 008: i=20 008: i=30 008: i=54 ], [002: i=1 s=List000 b=True r=101.000000 002: i=1 s=List001 b=False r=110.000000 002: i=10 s=List000 b=True r=110.000000 002: i=19 s=List020 b=True r=219.000000 002: i=2 s=List000 b=False r=102.000000 002: i=2 s=List001 b=True r=120.000000 002: i=2 s=List001 b=True r=122.000000 002: i=20 s=List020 b=True r=220.000000 002: i=4 s=List001 b=True r=130.000000 002: i=5 s=List005 b=True r=130.000000 004: i=5 s=List005 b=True r=130.000000 005: i=4 s=List001 b=True r=130.000000 005: i=5 s=List005 b=True r=130.000000 006: i=10 s=List000 b=True r=110.000000 006: i=19 s=List020 b=True r=219.000000 006: i=20 s=List020 b=True r=220.000000 006: i=5 s=List005 b=True r=130.000000 006: i=54 s=Lista054 b=False r=0.000000 007: i=19 s=List020 b=True r=219.000000 007: i=20 s=List020 b=True r=220.000000 007: i=30 s=List000 b=True r=110.000000 007: i=5 s=List005 b=True r=130.000000 007: i=54 s=Lista054 b=False r=0.000000 008: i=1 s=List000 b=True r=101.000000 008: i=1 s=List001 b=False r=110.000000 008: i=19 s=List020 b=True r=219.000000 008: i=2 s=List000 b=False r=102.000000 008: i=2 s=List001 b=True r=120.000000 008: i=2 s=List001 b=True r=122.000000 008: i=20 s=List020 b=True r=220.000000 008: i=30 s=List000 b=True r=110.000000 008: i=5 s=List005 b=True r=130.000000 008: i=54 s=Lista054 b=False r=0.000000 ]) # Tests to verify the functionality of two column compound index. # It tests index for two columns using string and integer fields. # The run of test-ovsdb generates the output of the display of data using the different indexes defined in # the program. # Then, some at_checks are used to verify the correctness of the corresponding index as well as the existence # of all the rows involved in the test. m4_define([OVSDB_CHECK_IDL_COMPOUND_INDEX_DOUBLE_COLUMN_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl compound_index_double_column compound_index positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) # Generate the data to be tested. AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-compound-index unix:socket $3], [0], [stdout], [ignore]) # Filter the rows of data that corresponds to the string-integer index eliminating the extra columns of data. # This is done to verifiy that the output data is in the correct and expected order. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sed -e 's/ b=.*//g']], [0], [$4]) # Here, the data is filtered and sorted in order to have all the rows in the index and be # able to determined that all the involved rows are present. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sort -k 1,1n -k 2,2 -k 3,3]], [0], [$5]) # Filter the rows of data that corresponds to the integer index eliminating the extra columns of data. # This is done to verifiy that the output data is in the correct and expected order. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sed -e 's/ b=.*//g']], [0], [$6]) # Here again, the data is filtered and sorted in order to have all the rows in the index and be # able to determined that all the involved rows are present. AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sort -k 1,1n -k 2,2 -k 3,3]], [0], [$7]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_COMPOUND_INDEX_DOUBLE_COLUMN_C([Compound_index, double column test ], [['["idltest", {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 1, "b":true, "r":101.0}}, {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 2, "b":false, "r":102.0}}, {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 10, "b":true, "r":110.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 1, "b":false, "r":110.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":120.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":122.0}}, {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 4, "b":true, "r":130.0}}, {"op": "insert", "table": "simple", "row": {"s":"List005", "i": 5, "b":true, "r":130.0}}, {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 20, "b":true, "r":220.0}}, {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 19, "b":true, "r":219.0}} ]']], [idl_compound_index_double_column], [001: s=List000 i=1 001: s=List000 i=2 001: s=List000 i=10 001: s=List001 i=1 001: s=List001 i=2 001: s=List001 i=2 001: s=List001 i=4 001: s=List005 i=5 001: s=List020 i=19 001: s=List020 i=20 002: s=List000 i=10 002: s=List000 i=2 002: s=List000 i=1 002: s=List001 i=4 002: s=List001 i=2 002: s=List001 i=2 002: s=List001 i=1 002: s=List005 i=5 002: s=List020 i=20 002: s=List020 i=19 003: s=List000 i=10 004: s=List001 i=1 004: s=List001 i=2 004: s=List001 i=2 004: s=List001 i=4 004: s=List005 i=5 ], [001: s=List000 i=1 b=True r=101.000000 001: s=List000 i=10 b=True r=110.000000 001: s=List000 i=2 b=False r=102.000000 001: s=List001 i=1 b=False r=110.000000 001: s=List001 i=2 b=True r=120.000000 001: s=List001 i=2 b=True r=122.000000 001: s=List001 i=4 b=True r=130.000000 001: s=List005 i=5 b=True r=130.000000 001: s=List020 i=19 b=True r=219.000000 001: s=List020 i=20 b=True r=220.000000 002: s=List000 i=1 b=True r=101.000000 002: s=List000 i=10 b=True r=110.000000 002: s=List000 i=2 b=False r=102.000000 002: s=List001 i=1 b=False r=110.000000 002: s=List001 i=2 b=True r=120.000000 002: s=List001 i=2 b=True r=122.000000 002: s=List001 i=4 b=True r=130.000000 002: s=List005 i=5 b=True r=130.000000 002: s=List020 i=19 b=True r=219.000000 002: s=List020 i=20 b=True r=220.000000 003: s=List000 i=10 b=True r=110.000000 004: s=List001 i=1 b=False r=110.000000 004: s=List001 i=2 b=True r=120.000000 004: s=List001 i=2 b=True r=122.000000 004: s=List001 i=4 b=True r=130.000000 004: s=List005 i=5 b=True r=130.000000 ], [005: i=1 s=List000 005: i=1 s=List001 005: i=2 s=List000 005: i=2 s=List001 005: i=2 s=List001 005: i=4 s=List001 005: i=5 s=List005 005: i=10 s=List000 005: i=19 s=List020 005: i=20 s=List020 006: i=20 s=List020 006: i=19 s=List020 006: i=10 s=List000 006: i=5 s=List005 006: i=4 s=List001 006: i=2 s=List000 006: i=2 s=List001 006: i=2 s=List001 006: i=1 s=List000 006: i=1 s=List001 ], [005: i=1 s=List000 b=True r=101.000000 005: i=1 s=List001 b=False r=110.000000 005: i=10 s=List000 b=True r=110.000000 005: i=19 s=List020 b=True r=219.000000 005: i=2 s=List000 b=False r=102.000000 005: i=2 s=List001 b=True r=120.000000 005: i=2 s=List001 b=True r=122.000000 005: i=20 s=List020 b=True r=220.000000 005: i=4 s=List001 b=True r=130.000000 005: i=5 s=List005 b=True r=130.000000 006: i=1 s=List000 b=True r=101.000000 006: i=1 s=List001 b=False r=110.000000 006: i=10 s=List000 b=True r=110.000000 006: i=19 s=List020 b=True r=219.000000 006: i=2 s=List000 b=False r=102.000000 006: i=2 s=List001 b=True r=120.000000 006: i=2 s=List001 b=True r=122.000000 006: i=20 s=List020 b=True r=220.000000 006: i=4 s=List001 b=True r=130.000000 006: i=5 s=List005 b=True r=130.000000 ]) m4_define([OVSDB_CHECK_IDL_COMPOUND_INDEX_WITH_REF], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl compound_index compound_index_with_ref positive $5]) OVSDB_START_IDLTEST m4_if([$2], [], [], [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-compound-index-with-ref unix:socket $3], [0], [stdout], [ignore]) AT_CHECK([sort stdout | uuidfilt]m4_if([$6],,, [[| $6]]), [0], [$4]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) OVSDB_CHECK_IDL_COMPOUND_INDEX_WITH_REF([set, simple3 idl-compound-index-with-ref, initially populated], [], [], [[000: After add to other table + set of strong ref 001: table simple3: name= uset=[] uref=[<0>] uuid=<1> 002: check simple4: not empty 003: Query using index with reference 004: table simple3: name= uset=[] uref=[<0>] uuid=<1> 005: After delete 007: check simple4: empty 008: End test ]]) m4_define([CHECK_STREAM_OPEN_BLOCK], [AT_SETUP([ovsdb-idl - Check stream open block - $1 - $3]) AT_SKIP_IF([test "$3" = "tcp6" && test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test "$3" = "tcp6" && test "$HAVE_IPV6" = "no"]) AT_SKIP_IF([test "$3" = "ssl6" && test "$IS_WIN32" = "yes"]) AT_SKIP_IF([test "$3" = "ssl6" && test "$HAVE_IPV6" = "no"]) AT_SKIP_IF([test "$3" = "ssl" && test "$HAVE_OPENSSL" = "no"]) $PYTHON3 -c "import ssl" SSL_PRESENT=$? AT_SKIP_IF([test "$3" = "ssl" && test $SSL_PRESENT != 0]) AT_SKIP_IF([test "$3" = "ssl6" && test "$HAVE_OPENSSL" = "no"]) AT_SKIP_IF([test "$3" = "ssl6" && test $SSL_PRESENT != 0]) AT_KEYWORDS([ovsdb server stream open_block $3]) PKIDIR=$abs_top_builddir/tests m4_define([PROTOCOL], [m4_substr([$3], [0], [3])]) OVSDB_START_IDLTEST([m4_join([], [p], PROTOCOL, [:0:], $4)]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) WRONG_PORT=$(($TCP_PORT + 101)) SSL_KEY_ARGS="$PKIDIR/testpki-privkey.pem $PKIDIR/testpki-cert.pem $PKIDIR/testpki-cacert.pem" AT_CHECK([$2 PROTOCOL:$4:$TCP_PORT $SSL_KEY_ARGS], [0], [ignore]) AT_CHECK([$2 PROTOCOL:$4:$WRONG_PORT $SSL_KEY_ARGS], [1], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN([" /unexpected SSL\/TLS connection close/d /Protocol error/d "]) AT_CHECK([$2 PROTOCOL:$4:$TCP_PORT $SSL_KEY_ARGS], [1], [ignore], [ignore]) AT_CLEANUP]) CHECK_STREAM_OPEN_BLOCK([C], [test-stream], [tcp], [127.0.0.1]) CHECK_STREAM_OPEN_BLOCK([C], [test-stream], [tcp6], [[[::1]]]) CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], [tcp], [127.0.0.1]) CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], [tcp6], [[[::1]]]) CHECK_STREAM_OPEN_BLOCK([C], [test-stream], [ssl], [127.0.0.1]) CHECK_STREAM_OPEN_BLOCK([C], [test-stream], [ssl6], [[[::1]]]) CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], [ssl], [127.0.0.1]) CHECK_STREAM_OPEN_BLOCK([Python3], [$PYTHON3 $srcdir/test-stream.py], [ssl6], [[[::1]]]) dnl OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS(LOG) dnl dnl Looks up transaction IDs in the log of OVSDB client application. dnl All-zero UUID should not be sent within a monitor request more than once, dnl unless some database requests were lost (not replied). m4_define([OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS], [ requests=$(grep -c 'send request' $1) replies=$(grep -c 'received reply' $1) if test "$requests" -eq "$replies"; then AT_CHECK([grep 'monitor_cond_since' $1 \ | grep -c "00000000-0000-0000-0000-000000000000" | tr -d '\n'], [0], [1]) fi ]) # same as OVSDB_CHECK_IDL but uses Python IDL implementation with tcp # with multiple remotes to assert the idl connects to the leader of the Raft cluster m4_define([OVSDB_CHECK_IDL_LEADER_ONLY_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 (leader only)]) AT_SKIP_IF([test "$IS_ARM64" = "yes"]) AT_KEYWORDS([ovsdb server idl Python leader_only with tcp socket]) m4_define([LPBK],[127.0.0.1]) OVSDB_CLUSTER_START_IDLTEST([$2], ["ptcp:0:"LPBK]) PARSE_LISTENING_PORT([s2.log], [TCP_PORT_1]) PARSE_LISTENING_PORT([s3.log], [TCP_PORT_2]) PARSE_LISTENING_PORT([s1.log], [TCP_PORT_3]) remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3 pids=$(cat s2.pid s3.pid s1.pid | tr '\n' ',') echo $pids AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t30 idl-cluster $srcdir/idltest.ovsschema $remotes $pids $3], [0], [stdout], [stderr]) remote=$(ovsdb_cluster_leader $remotes "idltest") leader=$(echo $remote | cut -d'|' -f 1) AT_CHECK([grep -F -- "${leader}" stdout], [0], [ignore]) OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) AT_CLEANUP]) OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL connects to leader], 3, ['remote']) OVSDB_CHECK_IDL_LEADER_ONLY_PY([Check Python IDL reconnects to leader], 3, ['remote' '+remotestop' 'remote']) # OVSDB_CHECK_CLUSTER_IDL_C(TITLE, N_SERVERS, [PRE-IDL-TXN], TRANSACTIONS, # OUTPUT, [KEYWORDS], [FILTER], [LOG_FILTER]) # # Creates a clustered database with a schema derived from idltest.ovsidl, runs # each PRE-IDL-TXN (if any), starts N_SERVERS ovsdb-server instances in RAFT, # on that database, and runs "test-ovsdb idl" passing each of the TRANSACTIONS # along. # # Checks that the overall output is OUTPUT. Before comparison, the # output is sorted (using "sort") and UUIDs in the output are replaced # by markers of the form where N is a number. The first unique # UUID is replaced by <0>, the next by <1>, and so on. If a given # UUID appears more than once it is always replaced by the same # marker. If FILTER is supplied then the output is also filtered # through the specified program. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. # # If LOG_FILTER is provided, checks that the contents of LOG_FILTER # are not matched by grep in the test-ovsdb logs. m4_define([OVSDB_CHECK_CLUSTER_IDL_C], [AT_SETUP([ovsdb-idl - $1 - C - tcp]) AT_KEYWORDS([ovsdb server idl tcp $6]) m4_define([LPBK],[127.0.0.1]) OVSDB_CLUSTER_START_IDLTEST([$2], ["ptcp:0:"LPBK]) PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1]) PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2]) PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3]) remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3 m4_if([$3], [], [], [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl tcp:LPBK:$TCP_PORT_1 $4], [0], [stdout], [stderr]) AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), [0], [$5]) m4_ifval([$8], [AT_CHECK([grep '$8' stderr], [1])], [], []) OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) AT_CLEANUP]) # Same as OVSDB_CHECK_CLUSTER_IDL_C but uses the Python IDL implementation. m4_define([OVSDB_CHECK_CLUSTER_IDL_PY], [AT_SETUP([ovsdb-idl - $1 - Python3 - tcp]) AT_KEYWORDS([ovsdb server idl tcp $6]) m4_define([LPBK],[127.0.0.1]) OVSDB_CLUSTER_START_IDLTEST([$2], ["ptcp:0:"LPBK]) PARSE_LISTENING_PORT([s1.log], [TCP_PORT_1]) PARSE_LISTENING_PORT([s2.log], [TCP_PORT_2]) PARSE_LISTENING_PORT([s3.log], [TCP_PORT_3]) remotes=tcp:LPBK:$TCP_PORT_1,tcp:LPBK:$TCP_PORT_2,tcp:LPBK:$TCP_PORT_3 m4_if([$3], [], [], [AT_CHECK([ovsdb-client transact $remotes $3], [0], [ignore], [ignore])]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema tcp:LPBK:$TCP_PORT_1 $4], [0], [stdout], [stderr]) AT_CHECK([sort stdout | uuidfilt]m4_if([$7],,, [[| $7]]), [0], [$5]) m4_if([$8], [AT_CHECK([grep '$8' stderr], [1])], [], []) OVSDB_CLUSTER_CHECK_MONITOR_COND_SINCE_TXN_IDS([stderr]) AT_CLEANUP]) m4_define([OVSDB_CHECK_CLUSTER_IDL], [OVSDB_CHECK_CLUSTER_IDL_C($@) OVSDB_CHECK_CLUSTER_IDL_PY($@)]) # Checks that monitor_cond_since works fine when disconnects happen # with cond_change requests in flight (i.e., IDL is properly updated). OVSDB_CHECK_CLUSTER_IDL([simple idl, monitor_cond_since, cluster disconnect], 3, [['["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 1.0, "b": true}}, {"op": "insert", "table": "simple", "row": {"i": 2, "r": 1.0, "b": true}}]']], [['condition simple []' \ 'condition simple [["i","==",2]]' \ 'condition simple [["i","==",1]]' \ '+reconnect' \ '?["idltest", {"op": "update", "table": "simple", "where": [["i", "==", 1]], "row": {"r": 2.0 }}]']], [[000: simple: change conditions 001: empty 002: simple: change conditions 003: table simple: i=2 r=1 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 004: simple: change conditions 005: reconnect 006: table simple 007: {"error":null,"result":[{"count":1}]} 008: table simple: i=1 r=2 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 009: done ]]) dnl This test checks that IDL keeps the existing connection to the server if dnl it's still on a list of remotes after update. OVSDB_CHECK_IDL_C([simple idl, initially empty, set remotes], [], [['set-remote unix:socket' \ '+set-remote unix:bad_socket,unix:socket' \ '+set-remote unix:bad_socket' \ '+set-remote unix:socket' \ 'set-remote unix:bad_socket,unix:socket' \ '+set-remote unix:socket' \ '+reconnect']], [[000: empty 001: new remotes: unix:socket, is connected: true 002: new remotes: unix:bad_socket,unix:socket, is connected: true 003: new remotes: unix:bad_socket, is connected: false 004: new remotes: unix:socket, is connected: false 005: empty 006: new remotes: unix:bad_socket,unix:socket, is connected: true 007: new remotes: unix:socket, is connected: true 008: reconnect 009: empty 010: done ]]) dnl This test checks that forceful reconnects triggered by the IDL dnl happen immediately (they should not use backoff). OVSDB_CHECK_CLUSTER_IDL([simple idl, initially empty, force reconnect], 3, [], [['+reconnect' \ 'reconnect' \ 'reconnect' \ 'reconnect']], [[000: reconnect 001: empty 002: reconnect 003: empty 004: reconnect 005: empty 006: reconnect 007: empty 008: done ]], [], [], reconnect.*waiting .* seconds before reconnect) AT_SETUP([idl table and column presence check]) AT_KEYWORDS([ovsdb server idl table column check]) OVSDB_START_IDLTEST([], ["$abs_srcdir/idltest2.ovsschema"]) AT_CHECK(ovsdb-tool create db2 $abs_srcdir/idltest.ovsschema) AT_CHECK(ovsdb-server -vconsole:warn --log-file=ovsdb-server2.log --detach dnl --no-chdir --pidfile=ovsdb-server2.pid --remote=punix:socket2 db2) on_exit 'kill `cat ovsdb-server2.pid`' dnl In this test, test-ovsdb first connects to the server with schema dnl idltest2.ovsschema and outputs the presence of tables and columns. dnl And then it connectes to the server with the schema idltest.ovsschema dnl and does the same. AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 dnl idl-table-column-check unix:socket unix:socket2], [0], [dnl unix:socket remote has table simple unix:socket remote has table link1 unix:socket remote doesn't have table link2 unix:socket remote doesn't have table simple5 unix:socket remote doesn't have col irefmap in table simple5 unix:socket remote doesn't have col l2 in table link1 unix:socket remote has col i in table link1, type: integer unix:socket remote doesn't have col id in table simple7 --- remote unix:socket done --- unix:socket2 remote has table simple unix:socket2 remote has table link1 unix:socket2 remote has table link2 unix:socket2 remote has table simple5 unix:socket2 remote has col irefmap in table simple5, type: map of (integer, uuid) pairs unix:socket2 remote has col l2 in table link1, type: set of up to 1 uuids unix:socket2 remote has col i in table link1, type: integer unix:socket2 remote has col id in table simple7, type: string --- remote unix:socket2 done --- ], [stderr]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP dnl This test checks that inserting and deleting the source of a reference dnl doesn't remove the reference in the (deleted) source tracked record. OVSDB_CHECK_IDL_TRACK([track, insert and delete, refs to link1], [], [['["idltest", {"op": "insert", "table": "link2", "uuid-name": "l2row0", "row": {"i": 1, "l1": ["set", [["named-uuid", "l1row0"]]]} }, {"op": "insert", "table": "link1", "uuid-name": "l1row0", "row": {"i": 1, "k": ["named-uuid", "l1row0"]} }, {"op": "insert", "table": "link2", "uuid-name": "l2row1", "row": {"i": 2, "l1": ["set", [["named-uuid", "l1row0"]]]} } ]' \ '+["idltest", {"op": "delete", "table": "link2", "where": [["i", "==", 2]]} ]' \ '+sleep' \ '["idltest", {"op": "delete", "table": "link2", "where": [["i", "==", 1]]} ]' ]], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]}]} 002: {"error":null,"result":[{"count":1}]} 003: sleep 004: table link1: inserted row: i=1 k=1 ka=[] l2= uuid=<1> 004: table link1: updated columns: i k 004: table link2: inserted row: i=1 l1=1 uuid=<0> 004: table link2: inserted/deleted row: i=2 l1=1 uuid=<2> 004: table link2: updated columns: i l1 004: table link2: updated columns: i l1 005: {"error":null,"result":[{"count":1}]} 006: table link1: i=1 k=1 ka=[] l2= uuid=<1> 007: done ]]) OVSDB_CHECK_IDL_TRACK([track, insert and delete, refs to link2], [], [['["idltest", {"op": "insert", "table": "link1", "uuid-name": "l1row0", "row": {"i": 1, "k": ["named-uuid", "l1row0"], "l2": ["set", [["named-uuid", "l2row0"]]]} }, {"op": "insert", "table": "link2", "uuid-name": "l2row0", "row": {"i": 1} }, {"op": "insert", "table": "link1", "uuid-name": "l1row1", "row": {"i": 2, "k": ["named-uuid", "l1row0"], "l2": ["set", [["named-uuid", "l2row0"]]]} } ]' \ '+["idltest", {"op": "delete", "table": "link1", "where": [["i", "==", 2]]} ]' \ '+sleep' \ '["idltest", {"op": "delete", "table": "link1", "where": [["i", "==", 1]]} ]' ]], [[000: empty 001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]}]} 002: {"error":null,"result":[{"count":1}]} 003: sleep 004: table link1: inserted row: i=1 k=1 ka=[] l2=1 uuid=<0> 004: table link1: inserted/deleted row: i=2 k=1 ka=[] l2=1 uuid=<2> 004: table link1: updated columns: i k l2 004: table link1: updated columns: i k l2 004: table link2: inserted row: i=1 l1= uuid=<1> 004: table link2: updated columns: i 005: {"error":null,"result":[{"count":1}]} 006: table link2: i=1 l1= uuid=<1> 007: done ]]) m4_define([OVSDB_CHECK_IDL_PERS_UUID_INSERT_C], [AT_SETUP([ovsdb-idl - $1 - C]) AT_KEYWORDS([ovsdb server idl persistent uuid insert]) OVSDB_START_IDLTEST([], ["$abs_srcdir/idltest.ovsschema"]) AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket $2], [0], [stdout], [stderr]) AT_CHECK([sort stdout], [0], [$3]) m4_if([$4], [], [], [AT_CHECK([grep $4 stderr], [0], [ignore])]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_PERS_UUID_INSERT_PY], [AT_SETUP([ovsdb-idl - $1 - Python3]) AT_KEYWORDS([ovsdb server idl python persistent uuid insert]) OVSDB_START_IDLTEST([], ["$abs_srcdir/idltest.ovsschema"]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket $2], [0], [stdout], [stderr]) AT_CHECK([sort stdout], [0], [$3]) m4_if([$4], [], [], [AT_CHECK([grep $4 stderr], [0], [ignore])]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_PERS_UUID_INSERT], [OVSDB_CHECK_IDL_PERS_UUID_INSERT_C($@) OVSDB_CHECK_IDL_PERS_UUID_INSERT_PY($@)]) OVSDB_CHECK_IDL_PERS_UUID_INSERT([simple idl, persistent uuid insert], [['insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df2222 2, insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df3333 3' \ 'insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df4444 4, insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df2222 5' \ 'insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df4444 4' \ 'delete 2' \ 'insert_uuid c5cc12f8-eaa1-43a7-8a73-bccd18df2222 5' ]], [[000: empty 001: commit, status=success 002: table simple: i=2 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222 002: table simple: i=3 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333 003: commit, status=error 004: table simple: i=2 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222 004: table simple: i=3 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333 005: commit, status=success 006: table simple: i=2 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222 006: table simple: i=3 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333 006: table simple: i=4 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df4444 007: commit, status=success 008: table simple: i=3 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333 008: table simple: i=4 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df4444 009: commit, status=success 010: table simple: i=3 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333 010: table simple: i=4 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df4444 010: table simple: i=5 r=0 b=false s= u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222 011: done ]], [['This UUID would duplicate a UUID already present within the table or deleted within the same transaction']]) OVSDB_CHECK_IDL_PERS_UUID_INSERT([simple idl, persistent uuid insert uref], [['insert_uuid_uref d7f2845f-2e8d-46a9-8330-f6d0b7d2ca36 689420a0-515b-4c0f-8eba-7ad59a344b54']], [[000: empty 001: commit, status=success 002: table simple3: name= uset=[] uref=[689420a0-515b-4c0f-8eba-7ad59a344b54] uuid=d7f2845f-2e8d-46a9-8330-f6d0b7d2ca36 002: table simple4: name= uuid=689420a0-515b-4c0f-8eba-7ad59a344b54 003: done ]]) OVSDB_CHECK_IDL_PY([simple idl, python, add_op], [], [['insert 1, insert 2, insert 3, insert 1' \ 'add_op {"op": "delete", "table": "simple", "where": [["i", "==", 1]]}' \ 'add_op {"op": "insert", "table": "simple", "row": {"i": 2}}, delete 3' \ 'insert 2, add_op {"op": "update", "table": "simple", "row": {"i": 1}, "where": [["i", "==", 2]]}' ]], [[000: empty 001: commit, status=success 002: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2> 002: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 002: table simple: i=3 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<4> 003: commit, status=success 004: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 004: table simple: i=3 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<4> 005: commit, status=success 006: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 006: table simple: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<5> 007: commit, status=success 008: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3> 008: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<5> 008: table simple: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<6> 009: done ]],[],sort) m4_define([OVSDB_CHECK_IDL_CHANGE_AWARE], [AT_SETUP([ovsdb-idl - simple idl, database change aware, online conversion - $1]) AT_KEYWORDS([ovsdb server idl db_change_aware conversion $1]) m4_if([$1], [clustered], [OVSDB_CLUSTER_START_IDLTEST([1], [punix:socket])], [OVSDB_START_IDLTEST]) dnl Add some data. AT_CHECK([[ovsdb-client transact unix:socket '["idltest", {"op": "insert", "table": "simple", "row": {"i": 1, "r": 2.0, "b": true, "s": "first row", "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"], "ia": ["set", [1, 2, 3]], "ra": ["set", [-0.5]], "ba": ["set", [true]], "sa": ["set", ["abc", "def"]], "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"], ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}}, {"op": "insert", "table": "simple", "row": {"b": false, "s": "second row"}}, {"op": "insert", "table": "simple", "row": {"b": true, "s": "third row"}} ]']], [0], [stdout]) dnl Create a new schema by adding 'extra_column' to the 'simple' table. AT_CHECK([sed 's/"ua": {/"extra_column":{"type": "string"},"ua": {/ s/1.2.3/1.2.4/' \ $abs_srcdir/idltest.ovsschema > new-idltest.ovsschema]) dnl Try "needs-conversion". AT_CHECK([ovsdb-client needs-conversion unix:socket $abs_srcdir/idltest.ovsschema], [0], [no ]) AT_CHECK([ovsdb-client needs-conversion unix:socket new-idltest.ovsschema], [0], [yes ]) dnl Conditionally exclude the second row from monitoring. m4_define([COND], [['condition simple [["b","==",true]]']]) dnl Start monitoring. OVS_DAEMONIZE([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t30 \ idl unix:socket COND monitor \ >idl-c.out 2>idl-c.err], [idl-c.pid]) AT_CAPTURE_FILE([idl-c.out]) AT_CAPTURE_FILE([idl-c.err]) OVS_DAEMONIZE([$PYTHON3 $srcdir/test-ovsdb.py -t30 \ idl $srcdir/idltest.ovsschema unix:socket COND monitor \ >idl-python.out 2>idl-python.err], [idl-python.pid]) AT_CAPTURE_FILE([idl-python.out]) AT_CAPTURE_FILE([idl-python.err]) dnl Wait for monitors to receive the data. OVS_WAIT_UNTIL([grep -q 'third row' idl-c.err]) OVS_WAIT_UNTIL([grep -q 'third row' idl-python.err]) dnl Convert the database. AT_CHECK([ovsdb-client convert unix:socket new-idltest.ovsschema]) dnl Check for the monitor cancellation and the data being requested again. m4_foreach([FILE], [[idl-c], [idl-python]], [OVS_WAIT_UNTIL([grep -q 'monitor_canceled' FILE.err]) OVS_WAIT_UNTIL([test 2 -eq $(grep -c 'send request, method="monitor_cond_since", params=."idltest"' FILE.err)]) dnl XXX: Checking for the new schema bits conditionally because standalone dnl databases are not updating the schema in the _Server database properly. m4_if([$1], [clustered], [OVS_WAIT_UNTIL([grep -q 'extra_column' FILE.err])]) dnl Check that there were no unexpected messages. AT_CHECK([! grep 'unexpected' FILE.err]) dnl Check that the data is received twice and the condition is working. AT_CHECK([sort FILE.out | uuidfilt], [0], [[000: simple: change conditions 001: table simple: i=0 r=0 b=true s=third row u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 001: table simple: i=1 r=2 b=true s=first row u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> 002: table simple: i=0 r=0 b=true s=third row u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1> 002: table simple: i=1 r=2 b=true s=first row u=<2> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<3> <4>] uuid=<5> ]])]) AT_CLEANUP]) OVSDB_CHECK_IDL_CHANGE_AWARE([standalone]) OVSDB_CHECK_IDL_CHANGE_AWARE([clustered]) AT_SETUP([ovsdb-idl - read only insert - C]) AT_KEYWORDS([ovsdb server idl read only assert]) OVSDB_START_IDLTEST AT_CHECK([[ovsdb-client transact unix:socket \ '["idltest", {"op": "insert", "table": "simple", "row": {}}]']], [0], [ignore], [ignore]) AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \ --assert-read-only idl unix:socket 'insert 1']], [ignore], [ignore], [stderr]) AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr]) AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-idl - read only set - C]) AT_KEYWORDS([ovsdb server idl read only assert]) OVSDB_START_IDLTEST AT_CHECK([[ovsdb-client transact unix:socket \ '["idltest", {"op": "insert", "table": "simple", "row": {}}]']], [0], [ignore], [ignore]) AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \ --assert-read-only idl unix:socket 'set 0 r 2.0']], [ignore], [ignore], [stderr]) AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr]) AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-idl - read only delete - C]) AT_KEYWORDS([ovsdb server idl read only assert]) OVSDB_START_IDLTEST AT_CHECK([[ovsdb-client transact unix:socket \ '["idltest", {"op": "insert", "table": "simple", "row": {"i": 1}}]']], [0], [ignore], [ignore]) AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \ --assert-read-only idl unix:socket 'delete 1']], [ignore], [ignore], [stderr]) AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr]) AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-lock.at000066400000000000000000000047411514270232600225210ustar00rootroot00000000000000AT_BANNER([OVSDB -- lock]) # OVSDB_CHECK_LOCK_SETUP(TITILE, KEYWORDS) # # Starts an OVSDB server and the default lock transaction, acquire "lock0", # using the ovsdb-client tool. Execute additional , # and compare output file catured from ovsdb-client tools to . m4_define([OVSDB_CHECK_LOCK_SETUP], [AT_SETUP([ovsdb lock -- $1]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([ovsdb lock $2]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore])]) # # Two sessions create two locks. Both sessions should be able to get their # own lock immediately. OVSDB_CHECK_LOCK_SETUP([lock], [positive]) AT_CHECK([ovsdb-client --detach --no-chdir lock unix:socket lock0 >c1-output 2>&1], [0], [], []) AT_CHECK([ovsdb-client --detach --no-chdir lock unix:socket lock1 >c2-output 2>&1], [0], [], []) OVSDB_SERVER_SHUTDOWN AT_CHECK([cat c1-output], 0, [{"locked":true} ], []) AT_CHECK([cat c2-output], 0, [{"locked":true} ], []) AT_CLEANUP # # Two session wait on the same lock. The first session should be able # to get the lock immediately, the second session will get a notification # after the first session unlocks. OVSDB_CHECK_LOCK_SETUP([unlock], [positive]) AT_CHECK([ovsdb-client --detach --no-chdir --pidfile lock unix:socket lock0 >c1-output 2>&1], [0], [], []) AT_CHECK([ovsdb-client --detach --no-chdir lock unix:socket lock0 >c2-output 2>&1], [0], [], []) AT_CHECK([ovs-appctl -t ovsdb-client unlock lock0], [0], [], []) OVSDB_SERVER_SHUTDOWN AT_CHECK([cat c1-output], 0, [{"locked":true} {} ]) AT_CHECK([cat c2-output], 0, [{"locked":false} locked [["lock0"]] ], []) AT_CLEANUP # # Two session waits on the same lock. The first session should be able # to get the lock immediately. The second session tries to steal the lock, then # unlocks the lock. OVSDB_CHECK_LOCK_SETUP([steal], [positive]) AT_CHECK([ovsdb-client --detach --no-chdir lock unix:socket lock0 >c1-output 2>&1], [0], [], []) AT_CHECK([ovsdb-client --detach --no-chdir --pidfile steal unix:socket lock0 >c2-output 2>&1], [0], [], []) AT_CHECK([ovs-appctl -t ovsdb-client unlock lock0], [0], [], []) OVSDB_SERVER_SHUTDOWN AT_CHECK([cat c1-output], 0, [{"locked":true} stolen [["lock0"]] locked [["lock0"]] ]) AT_CHECK([cat c2-output], 0, [{"locked":true} {} ], []) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-log.at000066400000000000000000000261071514270232600223520ustar00rootroot00000000000000AT_BANNER([OVSDB -- logging]) AT_SETUP([ovsdb-log - create empty, reread]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([log]) AT_CHECK( [test-ovsdb log-io file create], [0], [file: open successful ], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read], [0], [file: open successful file: read: end of log ], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, reread]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}']], [0], [[file: open successful file: write:{"x":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read], [0], [[file: open successful file: read: {"x":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - check that create fails if file exists]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":1}']], [0], [[file: open successful file: write:{"x":1} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read], [0], [[file: open successful file: read: {"x":1} ]], [ignore]) AT_CHECK( [test-ovsdb log-io file create-excl read], [1], [], [test-ovsdb: I/O error: file: create failed (File exists) ]) AT_CHECK( [test-ovsdb log-io file create read], [0], [file: open successful file: read: {"x":1} ]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, reread]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, replace, commit]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) for option in '' --no-rename-open-files; do rm -f file AT_CHECK( [[test-ovsdb $option log-io file create \ 'write:{"x":0}' \ 'replace_start' \ 'new-write:{"x":1}' \ 'new-write:{"x":2}' \ 'old-write:{"x":4}' \ 'replace_commit' \ 'read' \ 'write:{"x":3}']], [0], [[file: open successful file: write:{"x":0} successful file: replace_start successful (temp): write:{"x":1} successful (temp): write:{"x":2} successful file: write:{"x":4} successful file: replace_commit successful file: read: end of log file: write:{"x":3} successful ]]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":1} file: read: {"x":2} file: read: {"x":3} file: read: end of log ]], [ignore]) done AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, replace, abort]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) for option in '' --no-rename-open-files; do rm -f file AT_CHECK( [[test-ovsdb $option log-io file create \ 'write:{"x":0}' \ 'replace_start' \ 'new-write:{"x":1}' \ 'new-write:{"x":2}' \ 'old-write:{"x":4}' \ 'replace_abort' \ 'read' \ 'write:{"x":3}']], [0], [[file: open successful file: write:{"x":0} successful file: replace_start successful (temp): write:{"x":1} successful (temp): write:{"x":2} successful file: write:{"x":4} successful file: replace_abort successful file: read: end of log file: write:{"x":3} successful ]]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":4} file: read: {"x":3} file: read: end of log ]], [ignore]) done AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, reread - alternative magic]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) # Sometimes you just need more magic: # http://www.catb.org/jargon/html/magic-story.html AT_CHECK( [[test-ovsdb --magic="MORE_MAGIC" log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK( [test-ovsdb --magic="MORE_MAGIC" log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read: end of log ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only], [1], [], [test-ovsdb: ovsdb error: file: cannot identify file type ]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write one, reread, append]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK( [[test-ovsdb log-io file read/write read read read 'write:{"append":0}']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: write:{"append":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read: {"append":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write, reread one, overwrite]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK( [[test-ovsdb log-io file read/write read 'write:{"more data":0}']], [0], [[file: open successful file: read: {"x":0} file: write:{"more data":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"more data":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write, add corrupted data, read]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK([echo 'xxx' >> file]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read failed: syntax error: file: parse error at offset 186 in header line "xxx" ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write, add corrupted data, read, overwrite]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK([echo 'xxx' >> file]) AT_CHECK( [[test-ovsdb log-io file read/write read read read read 'write:{"x":3}']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read failed: syntax error: file: parse error at offset 186 in header line "xxx" file: write:{"x":3} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read: {"x":3} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write, corrupt some data, read, overwrite]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK([[sed 's/{"x":2}/{"x":3}/' < file > file.tmp]]) AT_CHECK([mv file.tmp file]) AT_CHECK([[grep -c '{"x":3}' file]], [0], [1 ]) AT_CHECK( [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read failed: syntax error: file: 8 bytes starting at offset 178 have SHA-1 hash 2683fd63b5b9fd49df4f2aa25bf7db5cbbebbe6f but should have hash 3d8ed30f471ad1b7b4b571cb0c7d5ed3e81350aa file: write:{"longer data":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"longer data":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write, truncate file, read, overwrite]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK([[sed 's/{"x":2}/2/' < file > file.tmp]]) AT_CHECK([mv file.tmp file]) AT_CHECK([[grep -c '^2$' file]], [0], [1 ]) AT_CHECK( [[test-ovsdb log-io file read/write read read read 'write:{"longer data":0}']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read failed: I/O error: file: error reading 8 bytes starting at offset 178 (End of file) file: write:{"longer data":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"longer data":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP AT_SETUP([ovsdb-log - write bad JSON, read, overwrite]) AT_KEYWORDS([ovsdb log]) AT_CAPTURE_FILE([file]) AT_CHECK( [[test-ovsdb log-io file create 'write:{"x":0}' 'write:{"x":1}' 'write:{"x":2}']], [0], [[file: open successful file: write:{"x":0} successful file: write:{"x":1} successful file: write:{"x":2} successful ]], [ignore]) AT_CHECK([[printf '%s\n%s\n' 'OVSDB JSON 5 d910b02871075d3156ec8675dfc95b7d5d640aa6' 'null' >> file]]) AT_CHECK( [[test-ovsdb log-io file read/write read read read read 'write:{"replacement data":0}']], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read failed: syntax error: file: 5 bytes starting at offset 240 are not valid JSON (line 0, column 4, byte 4: syntax error at beginning of input) file: write:{"replacement data":0} successful ]], [ignore]) AT_CHECK( [test-ovsdb log-io file read-only read read read read read], [0], [[file: open successful file: read: {"x":0} file: read: {"x":1} file: read: {"x":2} file: read: {"replacement data":0} file: read: end of log ]], [ignore]) AT_CHECK([test -f .file.~lock~]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-macros.at000066400000000000000000000107121514270232600230500ustar00rootroot00000000000000dnl OVSDB_INIT([$1]) dnl dnl Creates an empty database named $1. m4_define([OVSDB_INIT], [AT_CHECK( [ovsdb-tool create $1 $abs_top_srcdir/vswitchd/vswitch.ovsschema], [0], [stdout], [ignore]) AT_CHECK( [[ovsdb-tool transact $1 \ '["Open_vSwitch", {"op": "insert", "table": "Open_vSwitch", "row": {}}]']], [0], [ignore], [ignore])]) dnl OVSDB_SERVER_SHUTDOWN([ALLOWLIST]) dnl dnl Gracefully stops ovsdb-server, checking log files for messages with dnl severity WARN or higher and signaling an error if any is present. dnl The optional ALLOWLIST may contain shell-quoted "sed" commands to dnl delete any warnings that are actually expected, e.g.: dnl dnl OVSDB_SERVER_SHUTDOWN(["/expected error/d"]) m4_define([OVSDB_SERVER_SHUTDOWN], [AT_CHECK([check_logs $1]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([ovsdb-server], [ovsdb-server.pid])]) # OVSDB_CHECK_POSITIVE(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ]) # # Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with # status 0 and prints OUTPUT on stdout. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_POSITIVE], [AT_SETUP([ovsdb - $1]) AT_KEYWORDS([ovsdb positive $4]) AT_CHECK([test-ovsdb $2], [0], [$3 ], []) AT_CLEANUP]) # OVSDB_CHECK_POSITIVE_IDX(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ], [INDEX]) # # Runs "test-ovsdb TEST-OVSDB-ARGS" twice, with and without an index, and checks # that it exits with status 0 and prints OUTPUT on stdout. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_POSITIVE_IDX], [OVSDB_CHECK_POSITIVE($1, [$2], $3, $4, $5) OVSDB_CHECK_POSITIVE([indexed $1], [m4_bpatsubst([$2], ["columns":], ["indexes": [$6], "columns":])], $3, $4, $5)]) # OVSDB_CHECK_POSITIVE_PY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ], # [PY-CHECK]) # # Runs "test-ovsdb.py TEST-OVSDB-ARGS" and checks that it exits with # status 0 and prints OUTPUT on stdout. # # PY-CHECK is expanded before the check. It can check for features of the # Python implementation that are required for the test to pass. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_POSITIVE_PY], [AT_SETUP([ovsdb - $1]) $6 AT_KEYWORDS([ovsdb positive Python $4]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py $2], [0], [$3 ], []) AT_CLEANUP]) # OVSDB_CHECK_POSITIVE_CPY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], # [PREREQ], [PY3-CHECK]) # # Runs identical C and Python tests, as specified. m4_define([OVSDB_CHECK_POSITIVE_CPY], [OVSDB_CHECK_POSITIVE([$1 - C], [$2], [$3], [$4], [$5]) OVSDB_CHECK_POSITIVE_PY([$1 - Python3], [$2], [$3], [$4], [$5], [$7])]) # OVSDB_CHECK_NEGATIVE(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ]) # # Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with # status 1 and that its output on stdout contains substring OUTPUT. # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_NEGATIVE], [AT_SETUP([ovsdb - $1]) AT_KEYWORDS([ovsdb negative $4]) AT_CHECK([test-ovsdb $2], [1], [], [stderr]) m4_assert(m4_len([$3])) AT_CHECK( [if grep -F -e "AS_ESCAPE([$3])" stderr then : else exit 99 fi], [0], [ignore], [ignore]) AT_CLEANUP]) # OVSDB_CHECK_NEGATIVE_PY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], [PREREQ]) # # Runs "test-ovsdb TEST-OVSDB-ARGS" and checks that it exits with # status 1 and that its output on stdout contains substring OUTPUT. # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_NEGATIVE_PY], [AT_SETUP([ovsdb - $1]) AT_KEYWORDS([ovsdb negative $4]) AT_CHECK([$PYTHON3 $srcdir/test-ovsdb.py $2], [1], [], [stderr]) m4_assert(m4_len([$3])) AT_CHECK( [if grep -F -e "AS_ESCAPE([$3])" stderr then : else exit 99 fi], [0], [ignore], [ignore]) AT_CLEANUP]) # OVSDB_CHECK_NEGATIVE_CPY(TITLE, TEST-OVSDB-ARGS, OUTPUT, [KEYWORDS], # [PREREQ]) # # Runs identical C and Python tests, as specified. m4_define([OVSDB_CHECK_NEGATIVE_CPY], [OVSDB_CHECK_NEGATIVE([$1 - C], [$2], [$3], [$4], [$5]) OVSDB_CHECK_NEGATIVE_PY([$1 - Python3], [$2], [$3], [$4], [$5])]) OVS_START_SHELL_HELPERS ovsdb_client_wait() { ovsdb-client -vconsole:warn -vreconnect:err -vjsonrpc:err -vtimeval:off -vfile -vsyslog:off -vvlog:off wait "$@" } OVS_END_SHELL_HELPERS openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-monitor-sort.py000077500000000000000000000050001514270232600242610ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2020 VMware, Inc. # # 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. # Breaks lines read from stdin into groups using blank lines as # group separators, then sorts lines within the groups for # reproducibility. import re import sys # This is copied out of the Python Sorting HOWTO at # https://docs.python.org/3/howto/sorting.html#sortinghowto def cmp_to_key(mycmp): 'Convert a cmp= function into a key= function' class K(object): def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): return mycmp(self.obj, other.obj) < 0 def __gt__(self, other): return mycmp(self.obj, other.obj) > 0 def __eq__(self, other): return mycmp(self.obj, other.obj) == 0 def __le__(self, other): return mycmp(self.obj, other.obj) <= 0 def __ge__(self, other): return mycmp(self.obj, other.obj) >= 0 def __ne__(self, other): return mycmp(self.obj, other.obj) != 0 return K u = '[0-9a-fA-F]' uuid_re = re.compile(r'%s{8}-%s{4}-%s{4}-%s{4}-%s{12}' % ((u,) * 5)) def cmp(a, b): return (a > b) - (a < b) def compare_lines(a, b): if uuid_re.match(a): if uuid_re.match(b): return cmp(a[36:], b[36:]) else: return 1 elif uuid_re.match(b): return -1 else: return cmp(a, b) def output_group(group, dst): for x in sorted(group, key=cmp_to_key(compare_lines)): dst.write(x) def ovsdb_monitor_sort(src, dst): group = [] while True: line = src.readline() if not line: break if line.rstrip() == '': output_group(group, dst) group = [] dst.write(line) elif line.startswith(',') and group: group[len(group) - 1] += line else: group.append(line) if group: output_group(group, dst) if __name__ == '__main__': ovsdb_monitor_sort(sys.stdin, sys.stdout) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-monitor.at000066400000000000000000001162171514270232600232620ustar00rootroot00000000000000AT_BANNER([OVSDB -- ovsdb-server monitors]) OVS_START_SHELL_HELPERS # ovsdb_check_monitor SCHEMA_FUNC DB TABLE OUTPUT COLUMNS # PRE-MONITOR-TXN... -- TRANSACTION... ovsdb_check_monitor () { local schema_func=$1 db=$2 table=$3 output=$4 columns=$5 shift; shift; shift; shift; shift $schema_func > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) while test "$1" != "--"; do AT_CHECK([ovsdb-tool transact db "$1"], [0], [ignore], [ignore]) shift done shift AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db > ovsdb-server.stdout 2> ovsdb-server.stderr], [0], [], []) on_exit 'kill `cat ovsdb-server.pid`' if test "$IS_WIN32" = "yes"; then AT_CHECK([ovsdb-client -vjsonrpc --pidfile --log-file -d json monitor --format=csv unix:socket $db $table $columns > output 2> ovsdb-client.stderr &], [0], [ignore], [ignore]) sleep 1 else AT_CHECK([ovsdb-client -vjsonrpc --detach --no-chdir --pidfile --log-file -d json monitor --format=csv unix:socket $db $table $columns > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) fi on_exit 'kill `cat ovsdb-client.pid`' for txn in ${1+"$@"} '[["'$db'"]]'; do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-client.pid]) AT_CHECK_UNQUOTED([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [$output], [ignore]) } OVS_END_SHELL_HELPERS # OVSDB_CHECK_MONITOR(TITLE, SCHEMA, [PRE-MONITOR-TXN], DB, TABLE, # TRANSACTIONS, OUTPUT, [COLUMNS], [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. COLUMNS, if specified, is passed to ovsdb-client as the set # of columns and operations to select. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_MONITOR], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server monitor positive $9]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CAPTURE_FILE([ovsdb-server.stdout]) AT_CAPTURE_FILE([ovsdb-server.stderr]) AT_CAPTURE_FILE([ovsdb-client.log]) AT_CAPTURE_FILE([ovsdb-client.stderr]) ovsdb_check_monitor '$2' '$4' '$5' '$7' '$8' \ m4_foreach([txn], [$3], ['txn' ]) -- \ m4_foreach([txn], [$6], ['txn' ]) AT_CLEANUP]) # OVSDB_CHECK_MONITOR_COND(TITLE, SCHEMA, [PRE-MONITOR-TXN], DB, TABLE, # TRANSACTIONS, OUTPUT, CONDITIONS, [COLUMNS], [KEYWORDS], # [CONDITIONS_CHANGE]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. COLUMNS, if specified, is passed to ovsdb-client as the set # of columns and operations to select. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_MONITOR_COND], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server monitor monitor-cond positive $10]) $2 > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) for txn in m4_foreach([txn], [$3], ['txn' ]); do AT_CHECK([ovsdb-tool transact db "$txn"], [0], [ignore], [ignore]) done AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond --format=csv unix:socket $4 '[$8]' $5 $9 > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' for txn in m4_foreach([txn], [$6], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done for cond in m4_foreach([cond], [$10], ['cond' ]); do AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/cond_change $5 "$cond"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client transact unix:socket '[["$4"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [$7], [ignore]) AT_CLEANUP]) OVSDB_CHECK_MONITOR([monitor insert into empty table], [ordinal_schema], [], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]]], [[row,action,name,number,_version <0>,insert,"""zero""",0,"[""uuid"",""<1>""]" ]]) OVSDB_CHECK_MONITOR([monitor insert into populated table], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""zero""",0,"[""uuid"",""<3>""]" ]]) OVSDB_CHECK_MONITOR([monitor delete], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", 10]]}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <0>,delete,"""ten""",10,"[""uuid"",""<1>""]" ]]) OVSDB_CHECK_MONITOR([monitor row update], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "update", "table": "ordinals", "where": [["number", "==", 10]], "row": {"name": "five plus five"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <0>,old,"""ten""",,"[""uuid"",""<1>""]" ,new,"""five plus five""",10,"[""uuid"",""<2>""]" ]]) OVSDB_CHECK_MONITOR([monitor no-op row updates], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "update", "table": "ordinals", "where": [["number", "==", 10]], "row": {"number": 10, "name": "ten"}}]]], [[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 9, "name": "nine"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""nine""",9,"[""uuid"",""<3>""]" ]]) OVSDB_CHECK_MONITOR([monitor insert-and-update transaction], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 9, "name": "nine"}, "uuid-name": "nine"}, {"op": "update", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "nine"]]], "row": {"name": "three squared"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""three squared""",9,"[""uuid"",""<3>""]" ]]) OVSDB_CHECK_MONITOR([monitor insert-update-and-delete transaction], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 9, "name": "nine"}, "uuid-name": "nine"}, {"op": "update", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "nine"]]], "row": {"name": "three squared"}}, {"op": "delete", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "nine"]]]}, {"op": "insert", "table": "ordinals", "row": {"number": 7, "name": "seven"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""seven""",7,"[""uuid"",""<3>""]" ]]) OVSDB_CHECK_MONITOR([monitor weak reference change], [weak_schema], [[[["weak", {"op": "insert", "table": "a", "row": {"a": 0, "a2a1": ["named-uuid", "a0"], "a2b": ["named-uuid", "b2"]}, "uuid-name": "a0"}, {"op": "insert", "table": "a", "row": {"a": 1, "a2a": ["named-uuid", "a0"], "a2a1": ["named-uuid", "a1"], "a2b": ["named-uuid", "b2"]}, "uuid-name": "a1"}, {"op": "insert", "table": "b", "row": {"b": 2}, "uuid-name": "b2"}]]]], [weak], [a], [[[["weak", {"op": "delete", "table": "a", "where": [["a", "==", 0]]}]]]], [[row,action,a,a2a,a2a1,a2b,_version <0>,initial,0,"[""set"",[]]","[""uuid"",""<0>""]","[""uuid"",""<1>""]","[""uuid"",""<2>""]" <3>,initial,1,"[""uuid"",""<0>""]","[""uuid"",""<3>""]","[""uuid"",""<1>""]","[""uuid"",""<4>""]" row,action,a,a2a,a2a1,a2b,_version <0>,delete,0,"[""set"",[]]","[""uuid"",""<0>""]","[""uuid"",""<1>""]","[""uuid"",""<2>""]" <3>,old,,"[""uuid"",""<0>""]",,,"[""uuid"",""<4>""]" ,new,1,"[""set"",[]]","[""uuid"",""<3>""]","[""uuid"",""<1>""]","[""uuid"",""<5>""]" ]]) OVSDB_CHECK_MONITOR([monitor insert-update-and-delete transaction], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 9, "name": "nine"}, "uuid-name": "nine"}, {"op": "update", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "nine"]]], "row": {"name": "three squared"}}, {"op": "delete", "table": "ordinals", "where": [["_uuid", "==", ["named-uuid", "nine"]]]}, {"op": "insert", "table": "ordinals", "row": {"number": 7, "name": "seven"}}]]]], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""seven""",7,"[""uuid"",""<3>""]" ]]) AT_BANNER([ovsdb -- ovsdb-monitor monitor only some operations]) m4_define([OVSDB_MONITOR_INITIAL], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]]) m4_define([OVSDB_MONITOR_TXNS], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 5, "name": "five"}}]]], [[["ordinals", {"op": "update", "table": "ordinals", "where": [["name", "==", "five"]], "row": {"name": "FIVE"}}]]], [[["ordinals", {"op": "delete", "table": "ordinals", "where": []}]]]]) OVSDB_CHECK_MONITOR([monitor all operations], [ordinal_schema], [OVSDB_MONITOR_INITIAL], [ordinals], [ordinals], [OVSDB_MONITOR_TXNS], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""five""",5,"[""uuid"",""<3>""]" row,action,name,number,_version <2>,old,"""five""",,"[""uuid"",""<3>""]" ,new,"""FIVE""",5,"[""uuid"",""<4>""]" row,action,name,number,_version <2>,delete,"""FIVE""",5,"[""uuid"",""<4>""]" <0>,delete,"""ten""",10,"[""uuid"",""<1>""]" ]]) dnl A monitor with "initial" only doesn't really make sense, dnl but it's still allowed and should work. OVSDB_CHECK_MONITOR([monitor initial only], [ordinal_schema], [OVSDB_MONITOR_INITIAL], [ordinals], [ordinals], [OVSDB_MONITOR_TXNS], [[row,action,name,number,_version <0>,initial,"""ten""",10,"[""uuid"",""<1>""]" ]], [!insert,!delete,!modify]) OVSDB_CHECK_MONITOR([monitor insert only], [ordinal_schema], [OVSDB_MONITOR_INITIAL], [ordinals], [ordinals], [OVSDB_MONITOR_TXNS], [[row,action,name,number,_version <0>,insert,"""five""",5,"[""uuid"",""<1>""]" ]], [!initial,!delete,!modify]) OVSDB_CHECK_MONITOR([monitor delete only], [ordinal_schema], [OVSDB_MONITOR_INITIAL], [ordinals], [ordinals], [OVSDB_MONITOR_TXNS], [[row,action,name,number,_version <0>,delete,"""FIVE""",5,"[""uuid"",""<1>""]" <2>,delete,"""ten""",10,"[""uuid"",""<3>""]" ]], [!initial,!insert,!modify]) OVSDB_CHECK_MONITOR([monitor modify only], [ordinal_schema], [OVSDB_MONITOR_INITIAL], [ordinals], [ordinals], [OVSDB_MONITOR_TXNS], [[row,action,name,number,_version <0>,old,"""five""",,"[""uuid"",""<1>""]" ,new,"""FIVE""",5,"[""uuid"",""<2>""]" ]], [!initial,!insert,!delete]) AT_BANNER([ovsdb -- ovsdb-monitor-cond conditional monitor only some operations]) OVSDB_CHECK_MONITOR_COND([monitor-cond empty condition], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], [[row,action,name,number,_version <0>,initial,"""one""",1,"[""uuid"",""<1>""]" <2>,initial,"""two""",2,"[""uuid"",""<3>""]" <4>,initial,"""zero""",,"[""uuid"",""<5>""]" row,action,name,number,_version <6>,insert,"""eleven""",11,"[""uuid"",""<7>""]" <8>,insert,"""ten""",10,"[""uuid"",""<9>""]" ]], [[]]) OVSDB_CHECK_MONITOR_COND([monitor-cond multiple conditions], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], [[row,action,name,number,_version <0>,initial,"""one""",1,"[""uuid"",""<1>""]" row,action,name,number,_version <2>,insert,"""ten""",10,"[""uuid"",""<3>""]" ]], [[["name","==","one"],["name","==","ten"]]]) OVSDB_CHECK_MONITOR_COND([monitor-cond delete from populated table], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "delete", "table": "ordinals", "where": []}]]]], [[row,action,name,number,_version <0>,initial,"""one""",1,"[""uuid"",""<1>""]" row,action,name,number,_version <0>,delete,,, ]], [[["name","==","one"],["name","==","ten"]]]) OVSDB_CHECK_MONITOR_COND([monitor-cond insert due to modify], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "update", "table": "ordinals", "where": [["name", "==", "one"]], "row": {"name": "ONE"}}]]]], [[row,action,name,number,_version <0>,insert,"""ONE""",1,"[""uuid"",""<1>""]" ]], [[["name","==","ONE"]]], [!initial,!delete,!modify]) OVSDB_CHECK_MONITOR_COND([monitor-cond delete due to modify], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "update", "table": "ordinals", "where": [["name", "==", "one"]], "row": {"name": "ONE"}}]]]], [[row,action,name,number,_version <0>,delete,,, ]], [[["name","==","one"]]], [!initial,!insert,!modify]) OVSDB_CHECK_MONITOR_COND([monitor-cond condition non-monitored columns], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], [[row,action,number <0>,initial,1 row,action,number <1>,insert,10 ]], [[["name","==","one"],["name","==","ten"]]], ["number"]) OVSDB_CHECK_MONITOR_COND([monitor-cond-change], [ordinal_schema], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], [ordinals], [ordinals], [], [[row,action,name,number,_version <0>,initial,"""one""",1,"[""uuid"",""<1>""]" <2>,initial,"""two""",2,"[""uuid"",""<3>""]" <4>,initial,"""zero""",,"[""uuid"",""<5>""]" row,action,name,number,_version <4>,delete,,, row,action,name,number,_version <2>,delete,,, row,action,name,number,_version <0>,delete,,, row,action,name,number,_version <0>,insert,"""one""",1,"[""uuid"",""<1>""]" <2>,insert,"""two""",2,"[""uuid"",""<3>""]" <4>,insert,"""zero""",,"[""uuid"",""<5>""]" ]], [[]], [], [[[[["name","==","one"],["name","==","two"]]]], [[[["name","==","two"],["name","==","one"]]]], [[[["name","==","one"]]]], [[[false]]], [[[true]]]]) AT_SETUP(monitor-cond-change with many sessions pending) AT_KEYWORDS([ovsdb server monitor monitor-cond negative]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done # 1001 clients monitoring column "name" and with condition for "name" only. # The clients are created in a way that the 991th client will request condition # change, so that the chance is high that the condition change will be handled # before some pending changes are freed. cond='[[["name","==","ten"]]]' for i in `seq 1 990`; do AT_CHECK([ovsdb-client -vjsonrpc --pidfile=ovsdb-client$i.pid --detach --no-chdir -d json monitor-cond --format=csv unix:socket ordinals $cond ordinals ["name"] >ovsdb-client$i.out 2>&1], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond --format=csv unix:socket ordinals $cond ordinals ["name"] > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) for i in `seq 991 1000`; do AT_CHECK([ovsdb-client -vjsonrpc --pidfile=ovsdb-client$i.pid --detach --no-chdir -d json monitor-cond --format=csv unix:socket ordinals $cond ordinals ["name"] >ovsdb-client$i.out 2>&1 ], [0], [ignore], [ignore]) done for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done # Change the condition so that a new column "number" is added to monitor table. cond='[[["number","==",1]]]' AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/cond_change ordinals $cond], [0], [ignore], [ignore]) # Give some time for the server to flush and free pending changes # (to crash, when n_columns is not handled properly) sleep 1 AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN("/Too many open files/d") OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[row,action,name <0>,insert,"""ten""" row,action,name <0>,delete, <1>,insert,"""one""" ]], [ignore]) AT_CLEANUP # Test monitor-cond-since with zero uuid, which shouldn't # be found in server and server should send all rows # as initial. AT_SETUP([monitor-cond-since not found]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:db.raft], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done # Omitting the last_id parameter in ovsdb-client monitor-cond-since command # will by default using all zero uuid, which doesn't exist in any history txn. AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: false, last_id: <0> row,action,name,number,_version <1>,initial,"""one""",1,"[""uuid"",""<2>""]" last_id: <3> row,action,name,number,_version <4>,insert,"""ten""",10,"[""uuid"",""<5>""]" ]], [ignore]) AT_CLEANUP # Test monitor-cond-since in ovsdb server restart scenario. # ovsdb-client should receive only new changes after the # specific transaction id. AT_SETUP([monitor-cond-since db restart]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:db.raft], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' OVS_WAIT_UNTIL([grep last_id output]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) # Remember the last_id, which will be used for monitor-cond-since later. last_id=`grep last_id output | awk '{print $4}'` AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) # Some new changes made to db after restarting the server. for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done # Use last_id to monitor and get only the new changes. AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals $last_id '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: true, last_id: <0> row,action,name,number,_version <1>,insert,"""ten""",10,"[""uuid"",""<2>""]" ]], [ignore]) AT_CLEANUP # Test monitor-cond-since with last_id found in server # but there is no new change after that transaction. AT_SETUP([monitor-cond-since found but no new rows]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:db.raft], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' OVS_WAIT_UNTIL([grep last_id output]) kill `cat ovsdb-client.pid` OVS_WAIT_UNTIL([test ! -e ovsdb-client.pid]) last_id=`grep last_id output | awk '{print $4}'` AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals $last_id '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: true, last_id: <0> ]], [ignore]) AT_CLEANUP # Test monitor-cond-since against empty DB AT_SETUP([monitor-cond-since empty db]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:db.raft], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' OVS_WAIT_UNTIL([grep last_id output]) AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: false, last_id: <0> ]], [ignore]) AT_CLEANUP # Test monitor-cond-since with cond-change followed. AT_SETUP([monitor-cond-since condition change]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:db.raft], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file -vjsonrpc:file:dbg db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done AT_CAPTURE_FILE([ovsdb-client.log]) AT_CHECK([ovsdb-client -vjsonrpc --log-file --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[]]' ordinals > output 2> ovsdb-client.stderr]) on_exit 'kill `cat ovsdb-client.pid`' for cond in m4_foreach([cond], [[[[["name","==","one"],["name","==","two"]]]], [[[["name","==","one"]]]], [[[false]]], [[[true]]]], ['cond' ]); do AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/cond_change ordinals "$cond"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: false, last_id: <0> row,action,name,number,_version <1>,initial,"""one""",1,"[""uuid"",""<2>""]" <3>,initial,"""two""",2,"[""uuid"",""<4>""]" <5>,initial,"""zero""",,"[""uuid"",""<6>""]" last_id: <0> row,action,name,number,_version <5>,delete,,, last_id: <0> row,action,name,number,_version <3>,delete,,, last_id: <0> row,action,name,number,_version <1>,delete,,, last_id: <0> row,action,name,number,_version <1>,insert,"""one""",1,"[""uuid"",""<2>""]" <3>,insert,"""two""",2,"[""uuid"",""<4>""]" <5>,insert,"""zero""",,"[""uuid"",""<6>""]" ]], [ignore]) AT_CLEANUP # Test monitor-cond-since with non-cluster mode server AT_SETUP([monitor-cond-since non-cluster]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) # Transaction shouldn't be found, and last_id returned should always # be the same (all zero uuid) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: false, last_id: <0> row,action,name,number,_version <1>,initial,"""one""",1,"[""uuid"",""<2>""]" last_id: <0> row,action,name,number,_version <3>,insert,"""ten""",10,"[""uuid"",""<4>""]" ]], [ignore]) AT_CLEANUP # Test monitor-cond-since with non-cluster mode server with non-zero last_id AT_SETUP([monitor-cond-since non-cluster non-zero last_id]) AT_KEYWORDS([ovsdb server monitor monitor-cond-since negative]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done # A non-zero uuid last_id=11111111-1111-1111-1111-111111111111 AT_CHECK([ovsdb-client -vjsonrpc --pidfile --detach --no-chdir -d json monitor-cond-since --format=csv unix:socket ordinals $last_id '[[["name","==","one"],["name","==","ten"]]]' ordinals > output 2> ovsdb-client.stderr], [0], [ignore], [ignore]) on_exit 'kill `cat ovsdb-client.pid`' for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 10, "name": "ten"}}, {"op": "insert", "table": "ordinals", "row": {"number": 11, "name": "eleven"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore], [kill `cat server-pid client-pid`]) done AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && test ! -e ovsdb-client.pid]) # Transaction shouldn't be found, and last_id returned should always # be the same (all zero uuid) AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < output | uuidfilt], [0], [[found: false, last_id: <0> row,action,name,number,_version <1>,initial,"""one""",1,"[""uuid"",""<2>""]" last_id: <0> row,action,name,number,_version <3>,insert,"""ten""",10,"[""uuid"",""<4>""]" ]], [ignore]) AT_CLEANUP AT_SETUP([monitor-cond initial reply with condition on non-monitored column]) AT_KEYWORDS([ovsdb server monitor monitor-cond positive initial non-monitored]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' AT_CAPTURE_FILE([ovsdb-server.log]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \ --remote=punix:socket --log-file db], [0], [ignore], [ignore]) dnl Initialize the database content. for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done dnl Start a first client that monitors only the column 'name'. on_exit 'kill `cat client-1.pid`' AT_CAPTURE_FILE([client-1.out]) AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-1.pid --detach --no-chdir \ -d json monitor-cond --format=csv unix:socket \ ordinals '[[true]]' ordinals ["name"] \ > client-1.out 2> client-1.err], [0], [ignore], [ignore]) dnl Wait for the initial monitor reply. OVS_WAIT_UNTIL([grep -q 'initial' client-1.out]) dnl Start a second client that monitors the column 'name', but has a condition dnl on column 'number'. on_exit 'kill `cat client-2.pid`' AT_CAPTURE_FILE([client-2.out]) AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-2.pid --detach --no-chdir \ -d json monitor-cond --format=csv unix:socket \ ordinals '[[["number", "!=", 1]]]' ordinals ["name"] \ > client-2.out 2> client-2.err], [0], [ignore], [ignore]) dnl Wait for the initial monitor reply. OVS_WAIT_UNTIL([grep -q 'initial' client-2.out]) OVSDB_SERVER_SHUTDOWN OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && \ test ! -e client-1.pid && test ! -e client-2.pid]) dnl The first client should have all the names. AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-1.out | uuidfilt], [0], [dnl row,action,name <0>,initial,"""one""" <1>,initial,"""two""" <2>,initial,"""zero""" ]) dnl The second client should not have the name 'one'. AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-2.out | uuidfilt], [0], [dnl row,action,name <0>,initial,"""two""" <1>,initial,"""zero""" ]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-mutation.at000066400000000000000000000626611514270232600234360ustar00rootroot00000000000000AT_BANNER([OVSDB -- mutations]) OVSDB_CHECK_POSITIVE([null mutation], [[parse-mutations \ '{"columns": {"name": {"type": "string"}}}' \ '[]']], [[[]]]) OVSDB_CHECK_POSITIVE([mutations on scalars], [[parse-mutations \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[["i", "+=", 0]]' \ '[["i", "-=", 1]]' \ '[["i", "*=", 2]]' \ '[["i", "/=", 3]]' \ '[["i", "%=", 4]]' \ '[["r", "+=", 0.5]]' \ '[["r", "-=", 1.5]]' \ '[["r", "*=", 2.5]]' \ '[["r", "/=", 3.5]]']], [[[["i","+=",0]] [["i","-=",1]] [["i","*=",2]] [["i","/=",3]] [["i","%=",4]] [["r","+=",0.5]] [["r","-=",1.5]] [["r","*=",2.5]] [["r","/=",3.5]]]], [mutation]) AT_SETUP([disallowed mutations on scalars]) AT_KEYWORDS([ovsdb negative mutation]) AT_CHECK([[test-ovsdb parse-mutations \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[["i", "xxx", 1]]' \ '[["i", "insert", 1]]' \ '[["i", "delete", 2]]' \ '[["r", "%=", 0.5]]' \ '[["r", "insert", 1.5]]' \ '[["r", "delete", 2.5]]' \ '[["b", "+=", true]]' \ '[["b", "-=", false]]' \ '[["b", "*=", true]]' \ '[["b", "/=", false]]' \ '[["b", "%=", true]]' \ '[["b", "insert", false]]' \ '[["b", "delete", true]]' \ '[["s", "+=", "a"]]' \ '[["s", "-=", "b"]]' \ '[["s", "*=", "c"]]' \ '[["s", "/=", "d"]]' \ '[["s", "%=", "e"]]' \ '[["s", "insert", "f"]]' \ '[["s", "delete", "g"]]' \ '[["u", "+=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "-=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "*=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "/=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "insert", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \ '[["u", "delete", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]']], [1], [], [[test-ovsdb: unknown mutator: No mutator named xxx. test-ovsdb: syntax "["i","insert",1]": syntax error: Type mismatch: "insert" operator may not be applied to column i of type integer. test-ovsdb: syntax "["i","delete",2]": syntax error: Type mismatch: "delete" operator may not be applied to column i of type integer. test-ovsdb: syntax "["r","%=",0.5]": syntax error: Type mismatch: "%=" operator may not be applied to column r of type real. test-ovsdb: syntax "["r","insert",1.5]": syntax error: Type mismatch: "insert" operator may not be applied to column r of type real. test-ovsdb: syntax "["r","delete",2.5]": syntax error: Type mismatch: "delete" operator may not be applied to column r of type real. test-ovsdb: syntax "["b","+=",true]": syntax error: Type mismatch: "+=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","-=",false]": syntax error: Type mismatch: "-=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","*=",true]": syntax error: Type mismatch: "*=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","/=",false]": syntax error: Type mismatch: "/=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","%=",true]": syntax error: Type mismatch: "%=" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","insert",false]": syntax error: Type mismatch: "insert" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["b","delete",true]": syntax error: Type mismatch: "delete" operator may not be applied to column b of type boolean. test-ovsdb: syntax "["s","+=","a"]": syntax error: Type mismatch: "+=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","-=","b"]": syntax error: Type mismatch: "-=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","*=","c"]": syntax error: Type mismatch: "*=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","/=","d"]": syntax error: Type mismatch: "/=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","%=","e"]": syntax error: Type mismatch: "%=" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","insert","f"]": syntax error: Type mismatch: "insert" operator may not be applied to column s of type string. test-ovsdb: syntax "["s","delete","g"]": syntax error: Type mismatch: "delete" operator may not be applied to column s of type string. test-ovsdb: syntax "["u","+=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "+=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","-=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "-=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","*=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "*=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","/=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "/=" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","insert",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "insert" operator may not be applied to column u of type uuid. test-ovsdb: syntax "["u","delete",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "delete" operator may not be applied to column u of type uuid. ]]) AT_CLEANUP AT_SETUP([disallowed mutations on immutable columns]) AT_KEYWORDS([ovsdb negative mutation]) AT_CHECK([[test-ovsdb parse-mutations \ '{"columns": {"i": {"type": "integer", "mutable": false}}}' \ '[["i", "+=", 1]]' ]], [1], [], [[test-ovsdb: syntax "["i","+=",1]": constraint violation: Cannot mutate immutable column i in table mytable. ]]) AT_CLEANUP OVSDB_CHECK_POSITIVE([mutations on sets], [[parse-mutations \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}, "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}}, "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}, "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \ '[["i", "+=", 1]]' \ '[["i", "-=", 2]]' \ '[["i", "*=", 3]]' \ '[["i", "/=", 4]]' \ '[["i", "%=", 5]]' \ '[["i", "insert", ["set", [1, 2]]]]' \ '[["i", "delete", ["set", [1, 2, 3]]]]' \ '[["r", "+=", 1]]' \ '[["r", "-=", 2]]' \ '[["r", "*=", 3]]' \ '[["r", "/=", 4]]' \ '[["r", "insert", ["set", [1, 2]]]]' \ '[["r", "delete", ["set", [1, 2, 3]]]]' \ '[["b", "insert", ["set", [true]]]]' \ '[["b", "delete", ["set", [false]]]]' \ '[["s", "insert", ["set", ["a"]]]]' \ '[["s", "delete", ["set", ["a", "b"]]]]' \ '[["u", "insert", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]' \ '[["u", "delete", ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"], ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]]]' \ ]], [[[["i","+=",1]] [["i","-=",2]] [["i","*=",3]] [["i","/=",4]] [["i","%=",5]] [["i","insert",["set",[1,2]]]] [["i","delete",["set",[1,2,3]]]] [["r","+=",1]] [["r","-=",2]] [["r","*=",3]] [["r","/=",4]] [["r","insert",["set",[1,2]]]] [["r","delete",["set",[1,2,3]]]] [["b","insert",true]] [["b","delete",false]] [["s","insert","a"]] [["s","delete",["set",["a","b"]]]] [["u","insert",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]] [["u","delete",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]]], [mutation]) OVSDB_CHECK_POSITIVE([executing null mutation], [[execute-mutations \ '{"columns": {"i": {"type": "integer"}}}' \ '[[]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [mutation 0: row 0: no change row 1: no change row 2: no change ]) OVSDB_CHECK_POSITIVE([executing mutations on integers], [[execute-mutations \ '{"columns": {"i": {"type": "integer"}}}' \ '[[["i", "+=", 1]], [["i", "-=", 2]], [["i", "*=", 3]], [["i", "/=", 4]], [["i", "%=", 2]]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [mutation 0: row 0: {"i":1} row 1: {"i":2} row 2: {"i":3} mutation 1: row 0: {"i":-2} row 1: {"i":-1} row 2: {"i":0} mutation 2: row 0: no change row 1: {"i":3} row 2: {"i":6} mutation 3: row 0: no change row 1: {"i":0} row 2: {"i":0} mutation 4: row 0: no change row 1: no change row 2: {"i":0} ], [mutation]) OVSDB_CHECK_POSITIVE([integer overflow detection], [[execute-mutations \ '{"columns": {"i": {"type": "integer"}}}' \ '[[["i", "+=", 9223372036854775807]], [["i", "+=", -9223372036854775808]], [["i", "-=", -9223372036854775808]], [["i", "-=", 9223372036854775807]], [["i", "*=", 3037000500]], [["i", "/=", -1]], [["i", "/=", 0]]]' \ '[{"i": 0}, {"i": 1}, {"i": -1}, {"i": 9223372036854775807}, {"i": -9223372036854775808}, {"i": 3037000500}, {"i": -3037000500}']]], [mutation 0: row 0: {"i":9223372036854775807} row 1: range error: Result of "+=" operation is out of range. row 2: {"i":9223372036854775806} row 3: range error: Result of "+=" operation is out of range. row 4: {"i":-1} row 5: range error: Result of "+=" operation is out of range. row 6: {"i":9223372033817775307} mutation 1: row 0: {"i":-9223372036854775808} row 1: {"i":-9223372036854775807} row 2: range error: Result of "+=" operation is out of range. row 3: {"i":-1} row 4: range error: Result of "+=" operation is out of range. row 5: {"i":-9223372033817775308} row 6: range error: Result of "+=" operation is out of range. mutation 2: row 0: range error: Result of "-=" operation is out of range. row 1: range error: Result of "-=" operation is out of range. row 2: {"i":9223372036854775807} row 3: range error: Result of "-=" operation is out of range. row 4: {"i":0} row 5: range error: Result of "-=" operation is out of range. row 6: {"i":9223372033817775308} mutation 3: row 0: {"i":-9223372036854775807} row 1: {"i":-9223372036854775806} row 2: {"i":-9223372036854775808} row 3: {"i":0} row 4: range error: Result of "-=" operation is out of range. row 5: {"i":-9223372033817775307} row 6: range error: Result of "-=" operation is out of range. mutation 4: row 0: no change row 1: {"i":3037000500} row 2: {"i":-3037000500} row 3: range error: Result of "*=" operation is out of range. row 4: range error: Result of "*=" operation is out of range. row 5: range error: Result of "*=" operation is out of range. row 6: range error: Result of "*=" operation is out of range. mutation 5: row 0: no change row 1: {"i":-1} row 2: {"i":1} row 3: {"i":-9223372036854775807} row 4: range error: Result of "/=" operation is out of range. row 5: {"i":-3037000500} row 6: {"i":3037000500} mutation 6: row 0: domain error: Division by zero. row 1: domain error: Division by zero. row 2: domain error: Division by zero. row 3: domain error: Division by zero. row 4: domain error: Division by zero. row 5: domain error: Division by zero. row 6: domain error: Division by zero. ], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on integers with constraints], [[execute-mutations \ '{"columns": {"i": {"type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 2}}}}}' \ '[[["i", "+=", 1]], [["i", "-=", 2]], [["i", "*=", 3]], [["i", "/=", 4]], [["i", "%=", 2]]]' \ '[{"i": 0}, {"i": 1}, {"i": 2}']]], [mutation 0: row 0: {"i":1} row 1: {"i":2} row 2: constraint violation: 3 is not in the valid range 0 to 2 (inclusive) mutation 1: row 0: constraint violation: -2 is not in the valid range 0 to 2 (inclusive) row 1: constraint violation: -1 is not in the valid range 0 to 2 (inclusive) row 2: {"i":0} mutation 2: row 0: no change row 1: constraint violation: 3 is not in the valid range 0 to 2 (inclusive) row 2: constraint violation: 6 is not in the valid range 0 to 2 (inclusive) mutation 3: row 0: no change row 1: {"i":0} row 2: {"i":0} mutation 4: row 0: no change row 1: no change row 2: {"i":0} ], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on reals], [[execute-mutations \ '{"columns": {"r": {"type": "real"}}}' \ '[[["r", "+=", 0.5]], [["r", "-=", 1.5]], [["r", "*=", 2.5]], [["r", "/=", 4]]]' \ '[{"r": 0}, {"r": -2.5}, {"r": 1.25}']]], [mutation 0: row 0: {"r":0.5} row 1: {"r":-2} row 2: {"r":1.75} mutation 1: row 0: {"r":-1.5} row 1: {"r":-4} row 2: {"r":-0.25} mutation 2: row 0: no change row 1: {"r":-6.25} row 2: {"r":3.125} mutation 3: row 0: no change row 1: {"r":-0.625} row 2: {"r":0.3125} ], [mutation]) OVSDB_CHECK_POSITIVE([real overflow detection], [[execute-mutations \ '{"columns": {"r": {"type": "real"}}}' \ '[[["r", "+=", 1.7976931348623157e+308]], [["r", "-=", 1.7976931348623157e+308]], [["r", "*=", 2]], [["r", "/=", 4]], [["r", "/=", 0.5]], [["r", "/=", 0]]]' \ '[{"r": 0}, {"r": 1.7976931348623157e+308}, {"r": -1.7976931348623157e+308}']]], [mutation 0: row 0: {"r":1.79769313486232e+308} row 1: range error: Result of "+=" operation is out of range. row 2: {"r":0} mutation 1: row 0: {"r":-1.79769313486232e+308} row 1: {"r":0} row 2: range error: Result of "-=" operation is out of range. mutation 2: row 0: no change row 1: range error: Result of "*=" operation is out of range. row 2: range error: Result of "*=" operation is out of range. mutation 3: row 0: no change row 1: {"r":4.49423283715579e+307} row 2: {"r":-4.49423283715579e+307} mutation 4: row 0: no change row 1: range error: Result of "/=" operation is out of range. row 2: range error: Result of "/=" operation is out of range. mutation 5: row 0: domain error: Division by zero. row 1: domain error: Division by zero. row 2: domain error: Division by zero. ], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on reals with constraints], [[execute-mutations \ '{"columns": {"r": {"type": {"key": {"type": "real", "minReal": -2.5, "maxReal": 1.75}}}}}' \ '[[["r", "+=", 0.5]], [["r", "-=", 1.5]], [["r", "*=", 2.5]], [["r", "/=", 4]]]' \ '[{"r": 0}, {"r": -2.5}, {"r": 1.25}']]], [mutation 0: row 0: {"r":0.5} row 1: {"r":-2} row 2: {"r":1.75} mutation 1: row 0: {"r":-1.5} row 1: constraint violation: -4 is not in the valid range -2.5 to 1.75 (inclusive) row 2: {"r":-0.25} mutation 2: row 0: no change row 1: constraint violation: -6.25 is not in the valid range -2.5 to 1.75 (inclusive) row 2: constraint violation: 3.125 is not in the valid range -2.5 to 1.75 (inclusive) mutation 3: row 0: no change row 1: {"r":-0.625} row 2: {"r":0.3125} ], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on integer sets], [[execute-mutations \ '{"columns": {"i": {"type": {"key": {"type": "integer", "maxInteger": 5}, "min": 0, "max": "unlimited"}}}}' \ '[[["i", "+=", 1]], [["i", "-=", 2]], [["i", "*=", 3]], [["i", "/=", 4]], [["i", "%=", 2]], [["i", "insert", ["set", [1]]]], [["i", "insert", ["set", [2, 3]]]], [["i", "delete", ["set", [1]]]], [["i", "delete", ["set", [2, 3]]]]]' \ '[{"i": ["set", []]}, {"i": ["set", [0]]}, {"i": ["set", [0, 1]]}, {"i": ["set", [0, 1, 2]]}']]], [[mutation 0: row 0: no change row 1: {"i":1} row 2: {"i":["set",[1,2]]} row 3: {"i":["set",[1,2,3]]} mutation 1: row 0: no change row 1: {"i":-2} row 2: {"i":["set",[-2,-1]]} row 3: {"i":["set",[-2,-1,0]]} mutation 2: row 0: no change row 1: no change row 2: {"i":["set",[0,3]]} row 3: constraint violation: 6 is greater than maximum allowed value 5 mutation 3: row 0: no change row 1: no change row 2: constraint violation: Result of "/=" operation contains duplicates. row 3: constraint violation: Result of "/=" operation contains duplicates. mutation 4: row 0: no change row 1: no change row 2: no change row 3: constraint violation: Result of "%=" operation contains duplicates. mutation 5: row 0: {"i":1} row 1: {"i":["set",[0,1]]} row 2: no change row 3: no change mutation 6: row 0: {"i":["set",[2,3]]} row 1: {"i":["set",[0,2,3]]} row 2: {"i":["set",[0,1,2,3]]} row 3: {"i":["set",[0,1,2,3]]} mutation 7: row 0: no change row 1: no change row 2: {"i":0} row 3: {"i":["set",[0,2]]} mutation 8: row 0: no change row 1: no change row 2: no change row 3: {"i":["set",[0,1]]} ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on integer sets with constraints], [[execute-mutations \ '{"columns": {"i": {"type": {"key": "integer", "min": 1, "max": 2}}}}' \ '[[["i", "insert", ["set", [1]]]], [["i", "insert", ["set", [2]]]], [["i", "delete", ["set", [1]]]], [["i", "delete", ["set", [2]]]], [["i", "delete", ["set", [0, 1]]]]]' \ '[{"i": ["set", [0]]}, {"i": ["set", [2]]}, {"i": ["set", [0, 1]]}']]], [[mutation 0: row 0: {"i":["set",[0,1]]} row 1: {"i":["set",[1,2]]} row 2: no change mutation 1: row 0: {"i":["set",[0,2]]} row 1: no change row 2: constraint violation: Attempted to store 3 elements in set of 1 to 2 integers. mutation 2: row 0: no change row 1: no change row 2: {"i":0} mutation 3: row 0: no change row 1: constraint violation: Attempted to store 0 elements in set of 1 to 2 integers. row 2: no change mutation 4: row 0: constraint violation: Attempted to store 0 elements in set of 1 to 2 integers. row 1: no change row 2: constraint violation: Attempted to store 0 elements in set of 1 to 2 integers. ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on real sets], [[execute-mutations \ '{"columns": {"r": {"type": {"key": {"type": "real", "maxReal": 6}, "min": 0, "max": "unlimited"}}}}' \ '[[["r", "+=", 0.5]], [["r", "-=", 1.5]], [["r", "*=", 2.5]], [["r", "/=", 4]], [["r", "*=", 0]], [["r", "insert", 1.5]], [["r", "insert", 3]], [["r", "delete", ["set", [1.5, 3.5]]]], [["r", "delete", ["set", [0.5, 1.5, 2.5]]]]]' \ '[{"r": ["set", []]}, {"r": 0.5}, {"r": ["set", [0.5, 1.5]]}, {"r": ["set", [0.5, 1.5, 2.5]]}']]], [[mutation 0: row 0: no change row 1: {"r":1} row 2: {"r":["set",[1,2]]} row 3: {"r":["set",[1,2,3]]} mutation 1: row 0: no change row 1: {"r":-1} row 2: {"r":["set",[-1,0]]} row 3: {"r":["set",[-1,0,1]]} mutation 2: row 0: no change row 1: {"r":1.25} row 2: {"r":["set",[1.25,3.75]]} row 3: constraint violation: 6.25 is greater than maximum allowed value 6 mutation 3: row 0: no change row 1: {"r":0.125} row 2: {"r":["set",[0.125,0.375]]} row 3: {"r":["set",[0.125,0.375,0.625]]} mutation 4: row 0: no change row 1: {"r":0} row 2: constraint violation: Result of "*=" operation contains duplicates. row 3: constraint violation: Result of "*=" operation contains duplicates. mutation 5: row 0: {"r":1.5} row 1: {"r":["set",[0.5,1.5]]} row 2: no change row 3: no change mutation 6: row 0: {"r":3} row 1: {"r":["set",[0.5,3]]} row 2: {"r":["set",[0.5,1.5,3]]} row 3: {"r":["set",[0.5,1.5,2.5,3]]} mutation 7: row 0: no change row 1: no change row 2: {"r":0.5} row 3: {"r":["set",[0.5,2.5]]} mutation 8: row 0: no change row 1: {"r":["set",[]]} row 2: {"r":["set",[]]} row 3: {"r":["set",[]]} ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on boolean sets], [[execute-mutations \ '{"columns": {"b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}}}' \ '[[["b", "insert", ["set", [false]]]], [["b", "insert", ["set", [true]]]], [["b", "insert", ["set", [false, true]]]], [["b", "delete", ["set", [false]]]], [["b", "delete", ["set", [true]]]], [["b", "delete", ["set", [true, false]]]]]' \ '[{"b": ["set", []]}, {"b": ["set", [false]]}, {"b": ["set", [true]]}, {"b": ["set", [false, true]]}']]], [[mutation 0: row 0: {"b":false} row 1: no change row 2: {"b":["set",[false,true]]} row 3: no change mutation 1: row 0: {"b":true} row 1: {"b":["set",[false,true]]} row 2: no change row 3: no change mutation 2: row 0: {"b":["set",[false,true]]} row 1: {"b":["set",[false,true]]} row 2: {"b":["set",[false,true]]} row 3: no change mutation 3: row 0: no change row 1: {"b":["set",[]]} row 2: no change row 3: {"b":true} mutation 4: row 0: no change row 1: no change row 2: {"b":["set",[]]} row 3: {"b":false} mutation 5: row 0: no change row 1: {"b":["set",[]]} row 2: {"b":["set",[]]} row 3: {"b":["set",[]]} ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on string sets], [[execute-mutations \ '{"columns": {"s": {"type": {"key": "string", "min": 0, "max": "unlimited"}}}}' \ '[[["s", "insert", ["set", ["a"]]]], [["s", "insert", ["set", ["b"]]]], [["s", "insert", ["set", ["c", "d"]]]], [["s", "delete", ["set", ["a"]]]], [["s", "delete", ["set", ["b"]]]], [["s", "delete", ["set", ["c", "d"]]]]]' \ '[{"s": ["set", []]}, {"s": ["set", ["a"]]}, {"s": ["set", ["a", "b"]]}, {"s": ["set", ["a", "b", "c", "d"]]}']]], [[mutation 0: row 0: {"s":"a"} row 1: no change row 2: no change row 3: no change mutation 1: row 0: {"s":"b"} row 1: {"s":["set",["a","b"]]} row 2: no change row 3: no change mutation 2: row 0: {"s":["set",["c","d"]]} row 1: {"s":["set",["a","c","d"]]} row 2: {"s":["set",["a","b","c","d"]]} row 3: no change mutation 3: row 0: no change row 1: {"s":["set",[]]} row 2: {"s":"b"} row 3: {"s":["set",["b","c","d"]]} mutation 4: row 0: no change row 1: no change row 2: {"s":"a"} row 3: {"s":["set",["a","c","d"]]} mutation 5: row 0: no change row 1: no change row 2: no change row 3: {"s":["set",["a","b"]]} ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on uuid sets], [[execute-mutations \ '{"columns": {"u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \ '[[["u", "insert", ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]]], [["u", "insert", ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]]], [["u", "insert", ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]]], [["u", "delete", ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]]], [["u", "delete", ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]]], [["u", "delete", ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]]]]' \ '[{"u": ["set", []]}, {"u": ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}, {"u": ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]}, {"u": ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]}']]], [[mutation 0: row 0: {"u":["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]} row 1: no change row 2: {"u":["set",[["uuid","a60fe7ff-317b-4568-9106-892b37445313"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]} row 3: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]} mutation 1: row 0: {"u":["uuid","a60fe7ff-317b-4568-9106-892b37445313"]} row 1: {"u":["set",[["uuid","a60fe7ff-317b-4568-9106-892b37445313"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]} row 2: no change row 3: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","a60fe7ff-317b-4568-9106-892b37445313"]]]} mutation 2: row 0: {"u":["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"]} row 1: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]} row 2: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","a60fe7ff-317b-4568-9106-892b37445313"]]]} row 3: no change mutation 3: row 0: no change row 1: {"u":["set",[]]} row 2: no change row 3: no change mutation 4: row 0: no change row 1: no change row 2: {"u":["set",[]]} row 3: no change mutation 5: row 0: no change row 1: no change row 2: no change row 3: {"u":["set",[]]} ]], [mutation]) OVSDB_CHECK_POSITIVE([executing mutations on integer maps], [[execute-mutations \ '{"columns": {"i": {"type": {"key": "integer", "value": "integer", "min": 0, "max": "unlimited"}}}}' \ '[[["i", "insert", ["map", [[1, 2]]]]], [["i", "insert", ["map", [[2, 4], [3, 5]]]]], [["i", "delete", ["map", [[1, 2]]]]], [["i", "delete", ["map", [[2, 3]]]]], [["i", "delete", ["set", [1]]]], [["i", "delete", ["set", [2, 3]]]]]' \ '[{"i": ["map", []]}, {"i": ["map", [[1, 2]]]}, {"i": ["map", [[1, 3], [2, 3]]]}, {"i": ["map", [[3, 5]]]}']]], [[mutation 0: row 0: {"i":["map",[[1,2]]]} row 1: no change row 2: no change row 3: {"i":["map",[[1,2],[3,5]]]} mutation 1: row 0: {"i":["map",[[2,4],[3,5]]]} row 1: {"i":["map",[[1,2],[2,4],[3,5]]]} row 2: {"i":["map",[[1,3],[2,3],[3,5]]]} row 3: {"i":["map",[[2,4],[3,5]]]} mutation 2: row 0: no change row 1: {"i":["map",[]]} row 2: no change row 3: no change mutation 3: row 0: no change row 1: no change row 2: {"i":["map",[[1,3]]]} row 3: no change mutation 4: row 0: no change row 1: {"i":["map",[]]} row 2: {"i":["map",[[2,3]]]} row 3: no change mutation 5: row 0: no change row 1: no change row 2: {"i":["map",[[1,3]]]} row 3: {"i":["map",[]]} ]], [mutation]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-query.at000066400000000000000000000375601514270232600227430ustar00rootroot00000000000000AT_BANNER([OVSDB -- queries]) OVSDB_CHECK_POSITIVE_IDX([queries on scalars], [[query \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[{"i": 0, "r": 0.5, "b": true, "s": "a", "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]}, {"i": 1, "r": 1.5, "b": false, "s": "b", "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]}, {"i": 2, "r": 2.5, "b": true, "s": "c", "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]}, {"i": 3, "r": 3.5, "b": false, "s": "d", "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]}, {"i": 4, "r": 4.5, "b": true, "s": "e", "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \ '[[], [["i", "==", 0]], [["i", "!=", 1]], [["i", "<", 2]], [["i", "<=", 3]], [["i", ">", 2]], [["i", ">=", 4]], [["i", "includes", 3]], [["i", "excludes", 2]], [["r", "==", 0.5]], [["r", "!=", 1.5]], [["r", "<", 2.5]], [["r", "<=", 3.5]], [["r", ">", 4.5]], [["r", ">=", 5.5]], [["r", "includes", 1]], [["r", "excludes", 3]], [["b", "==", true]], [["b", "!=", true]], [["b", "includes", false]], [["b", "excludes", true]], [["s", "==", "a"]], [["s", "!=", "b"]], [["s", "includes", "c"]], [["s", "excludes", "d"]], [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]], [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]], [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]']], [dnl query 0: 11111 query 1: 1---- query 2: 1-111 query 3: 11--- query 4: 1111- query 5: ---11 query 6: ----1 query 7: ---1- query 8: 11-11 query 9: 1---- query 10: 1-111 query 11: 11--- query 12: 1111- query 13: ----- query 14: ----- query 15: ----- query 16: 11111 query 17: 1-1-1 query 18: -1-1- query 19: -1-1- query 20: -1-1- query 21: 1---- query 22: 1-111 query 23: --1-- query 24: 111-1 query 25: 1---- query 26: 1-111 query 27: --1--], [query], [], [["i"], ["r"], ["s"], ["u"]]) OVSDB_CHECK_POSITIVE_IDX([queries on sets], [[query \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \ '[{"i": ["set", []]}, {"i": ["set", [0]]}, {"i": ["set", [1]]}, {"i": ["set", [0, 1]]}, {"i": ["set", [2]]}, {"i": ["set", [2, 0]]}, {"i": ["set", [2, 1]]}, {"i": ["set", [2, 1, 0]]}]' \ '[[], [["i", "==", ["set", []]]], [["i", "==", ["set", [0]]]], [["i", "==", ["set", [1]]]], [["i", "==", ["set", [0, 1]]]], [["i", "==", ["set", [2]]]], [["i", "==", ["set", [2, 0]]]], [["i", "==", ["set", [2, 1]]]], [["i", "==", ["set", [2, 1, 0]]]], [["i", "!=", ["set", []]]], [["i", "!=", ["set", [0]]]], [["i", "!=", ["set", [1]]]], [["i", "!=", ["set", [0, 1]]]], [["i", "!=", ["set", [2]]]], [["i", "!=", ["set", [2, 0]]]], [["i", "!=", ["set", [2, 1]]]], [["i", "!=", ["set", [2, 1, 0]]]], [["i", "includes", ["set", []]]], [["i", "includes", ["set", [0]]]], [["i", "includes", ["set", [1]]]], [["i", "includes", ["set", [0, 1]]]], [["i", "includes", ["set", [2]]]], [["i", "includes", ["set", [2, 0]]]], [["i", "includes", ["set", [2, 1]]]], [["i", "includes", ["set", [2, 1, 0]]]], [["i", "excludes", ["set", []]]], [["i", "excludes", ["set", [0]]]], [["i", "excludes", ["set", [1]]]], [["i", "excludes", ["set", [0, 1]]]], [["i", "excludes", ["set", [2]]]], [["i", "excludes", ["set", [2, 0]]]], [["i", "excludes", ["set", [2, 1]]]], [["i", "excludes", ["set", [2, 1, 0]]]]]']], [dnl query 0: 11111 111 query 1: 1---- --- query 2: -1--- --- query 3: --1-- --- query 4: ---1- --- query 5: ----1 --- query 6: ----- 1-- query 7: ----- -1- query 8: ----- --1 query 9: -1111 111 query 10: 1-111 111 query 11: 11-11 111 query 12: 111-1 111 query 13: 1111- 111 query 14: 11111 -11 query 15: 11111 1-1 query 16: 11111 11- query 17: 11111 111 query 18: -1-1- 1-1 query 19: --11- -11 query 20: ---1- --1 query 21: ----1 111 query 22: ----- 1-1 query 23: ----- -11 query 24: ----- --1 query 25: 11111 111 query 26: 1-1-1 -1- query 27: 11--1 1-- query 28: 1---1 --- query 29: 1111- --- query 30: 1-1-- --- query 31: 11--- --- query 32: 1---- ---], [query], [], [["i"]]) # This is the same as the "set" test except that it adds values, # all of which always match. OVSDB_CHECK_POSITIVE_IDX([queries on maps (1)], [[query \ '{"columns": {"i": {"type": {"key": "integer", "value": "boolean", "min": 0, "max": "unlimited"}}}}' \ '[{"i": ["map", []]}, {"i": ["map", [[0, true]]]}, {"i": ["map", [[1, false]]]}, {"i": ["map", [[0, true], [1, false]]]}, {"i": ["map", [[2, true]]]}, {"i": ["map", [[2, true], [0, true]]]}, {"i": ["map", [[2, true], [1, false]]]}, {"i": ["map", [[2, true], [1, false], [0, true]]]}]' \ '[[], [["i", "==", ["map", []]]], [["i", "==", ["map", [[0, true]]]]], [["i", "==", ["map", [[1, false]]]]], [["i", "==", ["map", [[0, true], [1, false]]]]], [["i", "==", ["map", [[2, true]]]]], [["i", "==", ["map", [[2, true], [0, true]]]]], [["i", "==", ["map", [[2, true], [1, false]]]]], [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "!=", ["map", []]]], [["i", "!=", ["map", [[0, true]]]]], [["i", "!=", ["map", [[1, false]]]]], [["i", "!=", ["map", [[0, true], [1, false]]]]], [["i", "!=", ["map", [[2, true]]]]], [["i", "!=", ["map", [[2, true], [0, true]]]]], [["i", "!=", ["map", [[2, true], [1, false]]]]], [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", []]]], [["i", "includes", ["map", [[0, true]]]]], [["i", "includes", ["map", [[1, false]]]]], [["i", "includes", ["map", [[0, true], [1, false]]]]], [["i", "includes", ["map", [[2, true]]]]], [["i", "includes", ["map", [[2, true], [0, true]]]]], [["i", "includes", ["map", [[2, true], [1, false]]]]], [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "excludes", ["map", []]]], [["i", "excludes", ["map", [[0, true]]]]], [["i", "excludes", ["map", [[1, false]]]]], [["i", "excludes", ["map", [[0, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true]]]]], [["i", "excludes", ["map", [[2, true], [0, true]]]]], [["i", "excludes", ["map", [[2, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']], [dnl query 0: 11111 111 query 1: 1---- --- query 2: -1--- --- query 3: --1-- --- query 4: ---1- --- query 5: ----1 --- query 6: ----- 1-- query 7: ----- -1- query 8: ----- --1 query 9: -1111 111 query 10: 1-111 111 query 11: 11-11 111 query 12: 111-1 111 query 13: 1111- 111 query 14: 11111 -11 query 15: 11111 1-1 query 16: 11111 11- query 17: 11111 111 query 18: -1-1- 1-1 query 19: --11- -11 query 20: ---1- --1 query 21: ----1 111 query 22: ----- 1-1 query 23: ----- -11 query 24: ----- --1 query 25: 11111 111 query 26: 1-1-1 -1- query 27: 11--1 1-- query 28: 1---1 --- query 29: 1111- --- query 30: 1-1-- --- query 31: 11--- --- query 32: 1---- ---], [query], [], [["i"]]) # This is the same as the "set" test except that it adds values, # and those values don't always match. OVSDB_CHECK_POSITIVE_IDX([queries on maps (2)], [[query \ '{"columns": {"i": {"type": {"key": "integer", "value": "boolean", "min": 0, "max": "unlimited"}}}}' \ '[{"i": ["map", []]}, {"i": ["map", [[0, true]]]}, {"i": ["map", [[0, false]]]}, {"i": ["map", [[1, false]]]}, {"i": ["map", [[1, true]]]}, {"i": ["map", [[0, true], [1, false]]]}, {"i": ["map", [[0, true], [1, true]]]}, {"i": ["map", [[2, true]]]}, {"i": ["map", [[2, false]]]}, {"i": ["map", [[2, true], [0, true]]]}, {"i": ["map", [[2, false], [0, true]]]}, {"i": ["map", [[2, true], [1, false]]]}, {"i": ["map", [[2, true], [1, true]]]}, {"i": ["map", [[2, true], [1, false], [0, true]]]}, {"i": ["map", [[2, true], [1, false], [0, false]]]}]' \ '[[], [["i", "==", ["map", []]]], [["i", "==", ["map", [[0, true]]]]], [["i", "==", ["map", [[1, false]]]]], [["i", "==", ["map", [[0, true], [1, false]]]]], [["i", "==", ["map", [[2, true]]]]], [["i", "==", ["map", [[2, true], [0, true]]]]], [["i", "==", ["map", [[2, true], [1, false]]]]], [["i", "==", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "!=", ["map", []]]], [["i", "!=", ["map", [[0, true]]]]], [["i", "!=", ["map", [[1, false]]]]], [["i", "!=", ["map", [[0, true], [1, false]]]]], [["i", "!=", ["map", [[2, true]]]]], [["i", "!=", ["map", [[2, true], [0, true]]]]], [["i", "!=", ["map", [[2, true], [1, false]]]]], [["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "includes", ["map", []]]], [["i", "includes", ["map", [[0, true]]]]], [["i", "includes", ["map", [[1, false]]]]], [["i", "includes", ["map", [[0, true], [1, false]]]]], [["i", "includes", ["map", [[2, true]]]]], [["i", "includes", ["map", [[2, true], [0, true]]]]], [["i", "includes", ["map", [[2, true], [1, false]]]]], [["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]], [["i", "excludes", ["map", []]]], [["i", "excludes", ["map", [[0, true]]]]], [["i", "excludes", ["map", [[1, false]]]]], [["i", "excludes", ["map", [[0, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true]]]]], [["i", "excludes", ["map", [[2, true], [0, true]]]]], [["i", "excludes", ["map", [[2, true], [1, false]]]]], [["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']], [dnl query 0: 11111 11111 11111 query 1: 1---- ----- ----- query 2: -1--- ----- ----- query 3: ---1- ----- ----- query 4: ----- 1---- ----- query 5: ----- --1-- ----- query 6: ----- ----1 ----- query 7: ----- ----- -1--- query 8: ----- ----- ---1- query 9: -1111 11111 11111 query 10: 1-111 11111 11111 query 11: 111-1 11111 11111 query 12: 11111 -1111 11111 query 13: 11111 11-11 11111 query 14: 11111 1111- 11111 query 15: 11111 11111 1-111 query 16: 11111 11111 111-1 query 17: 11111 11111 11111 query 18: -1--- 11--1 1--1- query 19: ---1- 1---- -1-11 query 20: ----- 1---- ---1- query 21: ----- --1-1 -1111 query 22: ----- ----1 ---1- query 23: ----- ----- -1-11 query 24: ----- ----- ---1- query 25: 11111 11111 11111 query 26: 1-111 --11- -11-1 query 27: 111-1 -1111 1-1-- query 28: 1-1-1 --11- --1-- query 29: 11111 11-1- 1---- query 30: 1-111 ---1- ----- query 31: 111-1 -1-1- 1---- query 32: 1-1-1 ---1- -----], [query], [], [["i"]]) OVSDB_CHECK_POSITIVE([UUID-distinct queries on scalars], [[query-distinct \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[{"i": 0, "r": 0.5, "b": true, "s": "a", "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]}, {"i": 1, "r": 1.5, "b": false, "s": "b", "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]}, {"i": 2, "r": 2.5, "b": true, "s": "c", "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]}, {"i": 3, "r": 3.5, "b": false, "s": "d", "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]}, {"i": 4, "r": 4.5, "b": true, "s": "e", "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \ '[[], [["i", "==", 0]], [["i", "!=", 1]], [["i", "<", 2]], [["i", "<=", 3]], [["i", ">", 2]], [["i", ">=", 4]], [["i", "includes", 3]], [["i", "excludes", 2]], [["r", "==", 0.5]], [["r", "!=", 1.5]], [["r", "<", 2.5]], [["r", "<=", 3.5]], [["r", ">", 4.5]], [["r", ">=", 5.5]], [["r", "includes", 1]], [["r", "excludes", 3]], [["b", "==", true]], [["b", "!=", true]], [["b", "includes", false]], [["b", "excludes", true]], [["s", "==", "a"]], [["s", "!=", "b"]], [["s", "includes", "c"]], [["s", "excludes", "d"]], [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]], [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]], [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \ '["_uuid"]']], [dnl query 0: abcde query 1: a---- query 2: a-cde query 3: ab--- query 4: abcd- query 5: ---de query 6: ----e query 7: ---d- query 8: ab-de query 9: a---- query 10: a-cde query 11: ab--- query 12: abcd- query 13: ----- query 14: ----- query 15: ----- query 16: abcde query 17: a-c-e query 18: -b-d- query 19: -b-d- query 20: -b-d- query 21: a---- query 22: a-cde query 23: --c-- query 24: abc-e query 25: a---- query 26: a-cde query 27: --c--], [query], [], [["i"], ["r"], ["s"], ["u"]]) OVSDB_CHECK_POSITIVE([Boolean-distinct queries on scalars], [[query-distinct \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '[{"i": 0, "r": 0.5, "b": true, "s": "a", "u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]}, {"i": 1, "r": 1.5, "b": false, "s": "b", "u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]}, {"i": 2, "r": 2.5, "b": true, "s": "c", "u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]}, {"i": 3, "r": 3.5, "b": false, "s": "d", "u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]}, {"i": 4, "r": 4.5, "b": true, "s": "e", "u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \ '[[], [["i", "==", 0]], [["i", "!=", 1]], [["i", "<", 2]], [["i", "<=", 3]], [["i", ">", 2]], [["i", ">=", 4]], [["i", "includes", 3]], [["i", "excludes", 2]], [["r", "==", 0.5]], [["r", "!=", 1.5]], [["r", "<", 2.5]], [["r", "<=", 3.5]], [["r", ">", 4.5]], [["r", ">=", 5.5]], [["r", "includes", 1]], [["r", "excludes", 3]], [["b", "==", true]], [["b", "!=", true]], [["b", "includes", false]], [["b", "excludes", true]], [["s", "==", "a"]], [["s", "!=", "b"]], [["s", "includes", "c"]], [["s", "excludes", "d"]], [["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]], [["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]], [["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \ '["b"]']], [dnl query 0: ababa query 1: a-a-a query 2: ababa query 3: ababa query 4: ababa query 5: ababa query 6: a-a-a query 7: -b-b- query 8: ababa query 9: a-a-a query 10: ababa query 11: ababa query 12: ababa query 13: ----- query 14: ----- query 15: ----- query 16: ababa query 17: a-a-a query 18: -b-b- query 19: -b-b- query 20: -b-b- query 21: a-a-a query 22: ababa query 23: a-a-a query 24: ababa query 25: a-a-a query 26: ababa query 27: a-a-a], [query]) OVSDB_CHECK_NEGATIVE([parse colunn set containing bad name], [[query-distinct \ '{"columns": {"i": {"type": "integer"}}}' \ '[{"i": 0}]' \ '[[]]' \ '["i", "bad"]']], [bad is not a valid column name]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-rbac.at000066400000000000000000000377461514270232600225130ustar00rootroot00000000000000AT_BANNER([OVSDB -- ovsdb-server rbac]) AT_SETUP([ovsdb-server/rbac 2]) AT_KEYWORDS([ovsdb server rbac]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) RBAC_PKIDIR="$(pwd)" RBAC_PKI="sh $abs_top_srcdir/utilities/ovs-pki.in --dir=$RBAC_PKIDIR/pki --log=$RBAC_PKIDIR/rbac-pki.log" $RBAC_PKI init $RBAC_PKI req+sign ovsdb-server switch $RBAC_PKI -u req+sign client-1 switch $RBAC_PKI -u req+sign client-2 switch AT_DATA([schema], [[{"name": "mydb", "tables": { "Root": { "columns": { "connections": { "type": { "key": {"type": "uuid", "refTable": "Connection"}, "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Connection": { "columns": { "target": { "type": "string"}, "role": { "type": "string"}}}, "RBAC_Role": { "columns": { "name": {"type": "string"}, "permissions": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "RBAC_Permission", "refType": "weak"}, "min": 0, "max": "unlimited"}}}, "isRoot": true}, "RBAC_Permission": { "columns": { "table": {"type": "string"}, "authorization": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "insert_delete": {"type": "boolean"}, "update" : {"type": {"key": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "fixed_colors": { "columns": { "name": {"type": "string"}, "value": {"type": "integer"}}, "indexes": [["name"]], "isRoot": true}, "user_colors": { "columns": { "creator": {"type": "string"}, "name": {"type": "string"}, "value": {"type": "integer"}}, "indexes": [["name"]], "isRoot": true}, "other_colors": { "columns": { "creator": { "type": {"key": {"type": "string"}, "value": {"type": "string"}, "min": 0, "max": "unlimited"}}, "name": {"type": "string"}, "value": {"type": "integer"}}, "indexes": [["name"]], "isRoot": true} }, "version": "5.1.3", "cksum": "12345678 9" } ]]) AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) AT_CHECK( [[ovsdb-tool transact db \ '["mydb", {"op": "insert", "table": "Root", "row": { "connections": ["set", [["named-uuid", "x"]]]}}, {"op": "insert", "table": "Connection", "uuid-name": "x", "row": {"target": "pssl:0:127.0.0.1", "role": "testrole"}}, {"op": "insert", "table": "fixed_colors", "row": {"name": "red", "value": '16711680'}}, {"op": "insert", "table": "RBAC_Role", "row": {"name": "testrole", "permissions": ["map", [["user_colors", ["named-uuid", "y"]], ["other_colors", ["named-uuid", "z"]]]]}}, {"op": "insert", "table": "RBAC_Permission", "uuid-name": "y", "row": {"authorization": "creator", "insert_delete": true, "table": "user_colors", "update": ["set", ["name", "value"]]}}, {"op": "insert", "table": "RBAC_Permission", "uuid-name": "z", "row": {"authorization": "creator:chassis", "insert_delete": true, "table": "user_colors", "update": ["set", ["name", "value"]]}} ]']], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --remote=db:mydb,Root,connections \ --private-key=$RBAC_PKIDIR/ovsdb-server-privkey.pem \ --certificate=$RBAC_PKIDIR/ovsdb-server-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # Test 1: # Attempt to insert a row into the "fixed_colors" table. This should # fail as there are no permissions for role "testrole" for this table. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "insert", "table": "fixed_colors", "row": {"name": "chartreuse", "value": '8388352'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-1\" role \"testrole\" prohibit row insertion into table \"fixed_colors\".","error":"permission error"}]] ], [ignore]) # Test 2: # Attempt to insert a row into the "user_colors" table with a client ID that # does not match the value in the column used for authorization. This should # fail the authorization check for insertion. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "insert", "table": "user_colors", "row": {"creator": "client-2", "name": "chartreuse", "value": '8388352'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-1\" role \"testrole\" prohibit row insertion into table \"user_colors\".","error":"permission error"}]] ], [ignore]) # Test 3: # Attempt to insert a row into the "user_colors" table. This should # succeed since role "testrole" has permissions for this table that # allow row insertion. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "insert", "table": "user_colors", "row": {"creator": "client-1", "name": "chartreuse", "value": '8388352'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"uuid":["uuid","<0>"]}]] ], [ignore]) # Test 4: # Attempt to update a column in the "user_colors" table. This should # succeed since role "testrole" has permissions for this table that # allow update of the "value" column when ID is equal to the value in # the "creator" column. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "update", "table": "user_colors", "where": [["name", "==", "chartreuse"]], "row": {"value": '8388353'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1}]] ], [ignore]) # Test 5: # Attempt to update a column in the "user_colors" table. Same as # previous test, but with a different client ID. This should fail # the RBAC authorization test because "client-2" does not match the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "update", "table": "user_colors", "where": [["name", "==", "chartreuse"]], "row": {"value": '8388354'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-2\" role \"testrole\" prohibit modification of table \"user_colors\".","error":"permission error"}]] ], [ignore]) # Test 6: # Attempt to mutate a column in the "user_colors" table. This should # succeed since role "testrole" has permissions for this table that # allow update of the "value" column when ID is equal to the value in # the "creator" column. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "mutate", "table": "user_colors", "where": [["name", "==", "chartreuse"]], "mutations": [["value", "+=", '10']]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1}]] ], [ignore]) # Test 7: # Attempt to mutate a column in the "user_colors" table. Same as # previous test, but with a different client ID. This should fail # the RBAC authorization test because "client-2" does not match the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "mutate", "table": "user_colors", "where": [["name", "==", "chartreuse"]], "mutations": [["value", "+=", '10']]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-2\" role \"testrole\" prohibit mutate operation on table \"user_colors\".","error":"permission error"}]] ], [ignore]) # Test 8: # Attempt to delete a row from the "user_colors" table. This should fail # the RBAC authorization test because "client-2" does not match the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "delete", "table": "user_colors", "where": [["name", "==", "chartreuse"]]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-2\" role \"testrole\" prohibit row deletion from table \"user_colors\".","error":"permission error"}]] ], [ignore]) # Test 9: # Attempt to delete a row from the "user_colors" table. This should pass # the RBAC authorization test because "client-1" does matches the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "delete", "table": "user_colors", "where": [["name", "==", "chartreuse"]]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1}]] ], [ignore]) # Test 10: # Attempt to insert a row into the "other_colors" table. This should # succeed since role "testrole" has permissions for this table that # allow row insertion. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "insert", "table": "other_colors", "row": {"creator": ["map",[["chassis", "client-1"]]], "name": "seafoam", "value": '7466680'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"uuid":["uuid","<0>"]}]] ], [ignore]) # Test 11: # Attempt to update a column in the "user_colors" table. This should # succeed since role "testrole" has permissions for this table that # allow update of the "value" column when ID is equal to the value in # the "creator" column. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "update", "table": "other_colors", "where": [["name", "==", "seafoam"]], "row": {"value": '8388353'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1}]] ], [ignore]) # Test 12: # Attempt to update a column in the "other_colors" table. Same as # previous test, but with a different client ID. This should fail # the RBAC authorization test because "client-2" does not match the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "update", "table": "other_colors", "where": [["name", "==", "seafoam"]], "row": {"value": '8388354'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-2\" role \"testrole\" prohibit modification of table \"other_colors\".","error":"permission error"}]] ], [ignore]) # Test 13: # Attempt to delete a row from the "other_colors" table. This should fail # the RBAC authorization test because "client-2" does not match the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "delete", "table": "other_colors", "where": [["name", "==", "seafoam"]]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"details":"RBAC rules for client \"client-2\" role \"testrole\" prohibit row deletion from table \"other_colors\".","error":"permission error"}]] ], [ignore]) # Test 14: # Count the rows in other_colors. This should pass even though the RBAC # authorization would fail because "client-2" does not match the # "creator" column for this row. Because the RBAC check is bypassed when # mutation is empty. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-2-privkey.pem \ --certificate=$RBAC_PKIDIR/client-2-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "mutate", "table": "other_colors", "where": [], "mutations": []}, {"op": "mutate", "table": "other_colors", "where": [["name", "==", "seafoam"]], "mutations": []} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1},{"count":1}]] ], [ignore]) # Test 15: # Attempt to delete a row from the "other_colors" table. This should pass # the RBAC authorization test because "client-1" does matches the # "creator" column for this row. AT_CHECK([ovsdb-client transact ssl:127.0.0.1:$SSL_PORT \ --private-key=$RBAC_PKIDIR/client-1-privkey.pem \ --certificate=$RBAC_PKIDIR/client-1-cert.pem \ --ca-cert=$RBAC_PKIDIR/pki/switchca/cacert.pem \ ['["mydb", {"op": "delete", "table": "other_colors", "where": [["name", "==", "seafoam"]]} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt stdout], [0], [[[{"count":1}]] ], [ignore]) OVSDB_SERVER_SHUTDOWN([" /No status column present in the Connection table/d "]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-replication.at000066400000000000000000000124161514270232600241000ustar00rootroot00000000000000AT_BANNER([OVSDB -- replication]) m4_divert_push([PREPARE_TESTS]) [ replication_schema () { cat <<'EOF' {"name": "mydb", "tables": { "a": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"]]}, "b": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"]]}} } EOF } replication_schema_v2 () { cat <<'EOF' {"name": "mydb", "tables": { "a": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"]]}, "b": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}, "foo" : {"type": "string"}}, "indexes": [["number"]]}, "c": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"]]}} } EOF } ] m4_divert_pop([PREPARE_TESTS]) m4_define([REPLICATION_EXAMPLES], [ OVSDB_CHECK_REPLICATION([insert monitored table, insert excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "insert", "table": "a", "row": {"number": 1, "name": "one"}}]]]], [[8,10c8,9 < _uuid name number < ------------------------------------ ---- ------ < <0> one 1 --- > _uuid name number > ----- ---- ------]] ) OVSDB_CHECK_REPLICATION([insert monitored table, update excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "update", "table": "b", "where":[["name","==","one"]], "row": {"number": 2, "name": "two"}}]]], [[["mydb", {"op": "insert", "table": "a", "row": {"number": 1, "name": "one"}}]]]], [[8,10c8,9 < _uuid name number < ------------------------------------ ---- ------ < <0> two 2 --- > _uuid name number > ----- ---- ------]] ) OVSDB_CHECK_REPLICATION([update monitored table, insert excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "update", "table": "a", "where":[["name","==","zero"]], "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 2, "name": "two"}}]]]], [[7,9c7,8 < _uuid name number < ------------------------------------ ---- ------ < <0> two 2 --- > _uuid name number > ----- ---- ------]] ) OVSDB_CHECK_REPLICATION([update monitored table, update excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "update", "table": "a", "where":[["name","==","zero"]], "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 2, "name": "two"}}]]], [[["mydb", {"op": "update", "table": "b", "where":[["name","==","two"]], "row": {"number": 3, "name": "three"}}]]]], [[7,9c7,8 < _uuid name number < ------------------------------------ ----- ------ < <0> three 3 --- > _uuid name number > ----- ---- ------]] ) OVSDB_CHECK_REPLICATION([delete monitored table, insert excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "delete", "table": "a", "where":[["name","==","zero"]]}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "insert", "table": "a", "row": {"number": 1, "name": "one"}}]]]], [[7,9c7,8 < _uuid name number < ------------------------------------ ---- ------ < <0> one 1 --- > _uuid name number > ----- ---- ------]] ) OVSDB_CHECK_REPLICATION([delete monitored table, update excluded table], [replication_schema], [[[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]], [[["mydb", {"op": "delete", "table": "a", "where":[["name","==","zero"]]}]]], [[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]], [[["mydb", {"op": "update", "table": "b", "where":[["name","==","one"]], "row": {"number": 2, "name": "two"}}]]], [[["mydb", {"op": "insert", "table": "a", "row": {"number": 1, "name": "one"}}]]]], [[7,9c7,8 < _uuid name number < ------------------------------------ ---- ------ < <0> two 2 --- > _uuid name number > ----- ---- ------]] ) ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-row.at000066400000000000000000000270541514270232600224020ustar00rootroot00000000000000AT_BANNER([OVSDB -- rows]) OVSDB_CHECK_POSITIVE([row with one string column], [[parse-rows \ '{"columns": {"name": {"type": "string"}}}' \ '{"name": "value"}' \ '{"name": ""}' \ '{"name": "longer string with spaces"}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"name":"value"} name {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"name":""} name {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"name":"longer string with spaces"} name {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"name":""} ]], []) OVSDB_CHECK_POSITIVE([row with one integer column], [[parse-rows \ '{"columns": {"count": {"type": "integer"}}}' \ '{"count": 1}' \ '{"count": -1}' \ '{"count": 2e10}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"count":1} count {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"count":-1} count {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"count":20000000000} count {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"count":0} ]], []) OVSDB_CHECK_POSITIVE([row with one real column], [[parse-rows \ '{"columns": {"cost": {"type": "real"}}}' \ '{"cost": 1.0}' \ '{"cost": -2.0}' \ '{"cost": 123000}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"cost":1} cost {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"cost":-2} cost {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"cost":123000} cost {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"cost":0} ]], []) OVSDB_CHECK_POSITIVE([row with one boolean column], [[parse-rows \ '{"columns": {"feasible": {"type": "boolean"}}}' \ '{"feasible": true}' \ '{"feasible": false}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"feasible":true} feasible {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"feasible":false} feasible {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"feasible":false} ]], []) OVSDB_CHECK_POSITIVE([row with one uuid column], [[parse-rows \ '{"columns": {"ref": {"type": "uuid"}}}' \ '{"ref": ["uuid", "f707423d-bf5b-48b5-b6c0-797c900ba4b6"]}' \ '{"ref": ["uuid", "33583cc5-d2f4-43de-b1ca-8aac14071b51"]}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"ref":["uuid","f707423d-bf5b-48b5-b6c0-797c900ba4b6"]} ref {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"ref":["uuid","33583cc5-d2f4-43de-b1ca-8aac14071b51"]} ref {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"ref":["uuid","00000000-0000-0000-0000-000000000000"]} ]], []) OVSDB_CHECK_POSITIVE([row with set of 1 to 2 elements], [[parse-rows \ '{"columns": {"myset": {"type": {"key": "integer", "min": 1, "max": 2}}}}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"myset":0} ]]) OVSDB_CHECK_POSITIVE([row with map of 1 to 2 elements], [[parse-rows \ '{"columns": {"mymap": {"type": {"key": "integer", "value": "uuid", "min": 1, "max": 2}}}}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"mymap":["map",[[0,["uuid","00000000-0000-0000-0000-000000000000"]]]]} ]], []) OVSDB_CHECK_POSITIVE([row with several columns], [[parse-rows \ '{"columns": {"vswitch": {"type": "uuid"}, "name": {"type": "string"}, "datapath_id": {"type": {"key": "string", "min": 0}}, "hwaddr": {"type": "string"}, "mirrors": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}, "netflows": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}, "controller": {"type": {"key": "uuid", "min": 0}}, "listeners": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}, "snoops": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \ '{"vswitch": ["uuid", "1a5c7280-0d4c-4e34-9ec7-c772339f7774"], "name": "br0", "datapath_id": "000ae4256bb0", "hwaddr": "00:0a:e4:25:6b:b0"}' \ '{}']], [[{"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"controller":["set",[]],"datapath_id":"000ae4256bb0","hwaddr":"00:0a:e4:25:6b:b0","listeners":["set",[]],"mirrors":["set",[]],"name":"br0","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","1a5c7280-0d4c-4e34-9ec7-c772339f7774"]} datapath_id, hwaddr, name, vswitch {"_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"],"controller":["set",[]],"datapath_id":["set",[]],"hwaddr":"","listeners":["set",[]],"mirrors":["set",[]],"name":"","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","00000000-0000-0000-0000-000000000000"]} ]], []) OVSDB_CHECK_POSITIVE([row hashing (scalars)], [[compare-rows \ '{"columns": {"i": {"type": "integer"}, "r": {"type": "real"}, "b": {"type": "boolean"}, "s": {"type": "string"}, "u": {"type": "uuid"}}}' \ '["null", {}]' \ '["i1", {"i": 1}]' \ '["i2", {"i": 2}]' \ '["i4", {"i": 4}]' \ '["i8", {"i": 8}]' \ '["i16", {"i": 16}]' \ '["i32", {"i": 32}]' \ '["i64", {"i": 64}]' \ '["i128", {"i": 128}]' \ '["i256", {"i": 256}]' \ '["null2", {"r": -0}]' \ '["r123", {"r": 123}]' \ '["r0.0625", {"r": 0.0625}]' \ '["r0.125", {"r": 0.125}]' \ '["r0.25", {"r": 0.25}]' \ '["r0.5", {"r": 0.5}]' \ '["r1", {"r": 1}]' \ '["r2", {"r": 2}]' \ '["r4", {"r": 4}]' \ '["r8", {"r": 8}]' \ '["r16", {"r": 16}]' \ '["r32", {"r": 32}]' \ '["null3", {"b": false}]' \ '["b1", {"b": true}]' \ '["null4", {"s": ""}]' \ '["s0", {"s": "a"}]' \ '["s1", {"s": "b"}]' \ '["s2", {"s": "c"}]' \ '["s3", {"s": "d"}]' \ '["s4", {"s": "e"}]' \ '["s5", {"s": "f"}]' \ '["s6", {"s": "g"}]' \ '["s7", {"s": "h"}]' \ '["s8", {"s": "i"}]' \ '["s9", {"s": "j"}]' \ '["null5", {"u": ["uuid","00000000-0000-0000-0000-000000000000"]}]' \ '["u1", {"u": ["uuid","10000000-0000-0000-0000-000000000000"]}]' \ '["u2", {"u": ["uuid","01000000-0000-0000-0000-000000000000"]}]' \ '["u3", {"u": ["uuid","00100000-0000-0000-0000-000000000000"]}]' \ '["u4", {"u": ["uuid","00010000-0000-0000-0000-000000000000"]}]' \ '["u5", {"u": ["uuid","00001000-0000-0000-0000-000000000000"]}]' \ '["u6", {"u": ["uuid","00000100-0000-0000-0000-000000000000"]}]' \ '["u7", {"u": ["uuid","00000010-0000-0000-0000-000000000000"]}]' \ '["u8", {"u": ["uuid","00000001-0000-0000-0000-000000000000"]}]' \ '["null6", {"u": ["uuid","00000000-c6db-4d22-970f-b41fabd20c4b"]}]']], [[null == null2 null == null3 null == null4 null == null5 hash(null) == hash(null6) null2 == null3 null2 == null4 null2 == null5 hash(null2) == hash(null6) null3 == null4 null3 == null5 hash(null3) == hash(null6) null4 == null5 hash(null4) == hash(null6) hash(null5) == hash(null6)]]) OVSDB_CHECK_POSITIVE([row hashing (sets)], [[compare-rows \ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}, "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}}, "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}, "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \ '["null0", {"i": ["set", []]}]' \ '["i0", {"i": ["set", [0]]}]' \ '["i01", {"i": ["set", [0, 1]]}]' \ '["i012", {"i": ["set", [0, 1, 2]]}]' \ '["i021", {"i": ["set", [0, 2, 1]]}]' \ '["i201", {"i": ["set", [2, 0, 1]]}]' \ '["i102", {"i": ["set", [1, 0, 2]]}]' \ '["i120", {"i": ["set", [1, 2, 0]]}]' \ '["i210", {"i": ["set", [2, 1, 0]]}]' \ '["r0", {"r": ["set", [0]]}]' \ '["r01", {"r": ["set", [0, 1]]}]' \ '["r012", {"r": ["set", [0, 1, 2]]}]' \ '["r201", {"r": ["set", [2, 0, 1]]}]' \ '["null1", {"b": ["set", []]}]' \ '["b0", {"b": ["set", [false]]}]' \ '["b1", {"b": ["set", [true]]}]' \ '["b01", {"b": ["set", [false, true]]}]' \ '["b10", {"b": ["set", [true, false]]}]' \ '["null2", {"s": ["set", []]}]' \ '["sa", {"s": ["set", ["a"]]}]' \ '["sb", {"s": ["set", ["b"]]}]' \ '["sab", {"s": ["set", ["a", "b"]]}]' \ '["sba", {"s": ["set", ["b", "a"]]}]']], [[null0 == null1 null0 == null2 i012 == i021 i012 == i201 i012 == i102 i012 == i120 i012 == i210 i021 == i201 i021 == i102 i021 == i120 i021 == i210 i201 == i102 i201 == i120 i201 == i210 i102 == i120 i102 == i210 i120 == i210 r012 == r201 null1 == null2 b01 == b10 sab == sba]]) OVSDB_CHECK_POSITIVE([row hashing (maps)], [[compare-rows \ '{"columns": {"ii": {"type": {"key": "integer", "value": "integer", "min": 0, "max": "unlimited"}}, "rr": {"type": {"key": "real", "value": "real", "min": 0, "max": "unlimited"}}, "bb": {"type": {"key": "boolean", "value": "boolean", "min": 0, "max": "unlimited"}}, "ss": {"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}' \ '["null", {}]' \ '["ii0", {"ii": ["map", [[0, 0]]]}]' \ '["ii1", {"ii": ["map", [[0, 1]]]}]' \ '["ii00", {"ii": ["map", [[0, 0], [1, 0]]]}]' \ '["ii01", {"ii": ["map", [[0, 0], [1, 1]]]}]' \ '["ii10", {"ii": ["map", [[0, 1], [1, 0]]]}]' \ '["ii11", {"ii": ["map", [[0, 1], [1, 1]]]}]' \ '["rr0", {"rr": ["map", [[0, 0]]]}]' \ '["rr0", {"rr": ["map", [[0, 1]]]}]' \ '["rr00", {"rr": ["map", [[0, 0], [1, 0]]]}]' \ '["rr01", {"rr": ["map", [[0, 0], [1, 1]]]}]' \ '["rr10", {"rr": ["map", [[0, 1], [1, 0]]]}]' \ '["rr11", {"rr": ["map", [[0, 1], [1, 1]]]}]' \ '["bb0", {"bb": ["map", [[false, false]]]}]' \ '["bb1", {"bb": ["map", [[false, true]]]}]' \ '["bb00", {"bb": ["map", [[false, false], [true, false]]]}]' \ '["bb01", {"bb": ["map", [[false, false], [true, true]]]}]' \ '["bb10", {"bb": ["map", [[false, true], [true, false]]]}]' \ '["bb11", {"bb": ["map", [[false, true], [true, true]]]}]' \ '["ss0", {"ss": ["map", [["a", "a"]]]}]' \ '["ss1", {"ss": ["map", [["a", "b"]]]}]' \ '["ss00", {"ss": ["map", [["a", "a"], ["b", "a"]]]}]' \ '["ss01", {"ss": ["map", [["a", "a"], ["b", "b"]]]}]' \ '["ss10", {"ss": ["map", [["a", "b"], ["b", "a"]]]}]' \ '["ss11", {"ss": ["map", [["a", "b"], ["b", "b"]]]}]'; echo ]], [[]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-schema.at000066400000000000000000000101371514270232600230250ustar00rootroot00000000000000AT_BANNER([OVSDB -- schemas]) OVSDB_CHECK_POSITIVE_CPY([schema with valid refTables], [[parse-schema \ '{"name": "mydb", "version": "4.2.1", "tables": { "a": { "columns": { "map": { "type": { "key": { "type": "uuid", "refTable": "b"}, "value": { "type": "uuid", "refTable": "a"}}}}}, "b": { "columns": { "aRef": { "type": { "key": { "type": "uuid", "refTable": "a"}}}}}}}']], [[{"name":"mydb","tables":{"a":{"columns":{"map":{"type":{"key":{"refTable":"b","type":"uuid"},"value":{"refTable":"a","type":"uuid"}}}}},"b":{"columns":{"aRef":{"type":{"key":{"refTable":"a","type":"uuid"}}}}}},"version":"4.2.1"}]]) dnl Ephemeral strong references to root set tables are OK. dnl Ephemeral strong references to non-root set tables are forced to be dnl persistent. OVSDB_CHECK_POSITIVE_CPY([schema with ephemeral strong references], [[parse-schema \ '{"name": "mydb", "version": "4.2.1", "tables": { "a": { "columns": { "x": { "type": { "key": { "type": "uuid", "refTable": "b"}}, "ephemeral": true}, "y": { "type": { "key": { "type": "uuid", "refTable": "a"}}, "ephemeral": true}}}, "b": { "columns": { "aRef": { "type": { "key": { "type": "uuid", "refTable": "a"}}}}, "isRoot": true}}}']], [[{"name":"mydb","tables":{"a":{"columns":{"x":{"ephemeral":true,"type":{"key":{"refTable":"b","type":"uuid"}}},"y":{"type":{"key":{"refTable":"a","type":"uuid"}}}}},"b":{"columns":{"aRef":{"type":{"key":{"refTable":"a","type":"uuid"}}}},"isRoot":true}},"version":"4.2.1"}]]) dnl Immutable weak references are forced to be mutable. OVSDB_CHECK_POSITIVE_CPY([schema with immutable weak references], [[parse-schema \ '{"name": "mydb", "version": "4.2.1", "tables": { "a": { "columns": { "x": { "type": { "key": { "type": "uuid", "refTable": "a", "refType": "weak"}}, "mutable": false}}}}}']], [[{"name":"mydb","tables":{"a":{"columns":{"x":{"type":{"key":{"refTable":"a","refType":"weak","type":"uuid"}}}}}},"version":"4.2.1"}]]) dnl Schemas without version numbers are accepted for backward dnl compatibility, but this is a deprecated feature. OVSDB_CHECK_POSITIVE_CPY([schema without version number], [[parse-schema \ '{"name": "mydb", "tables": { "x": { "columns": { "y": { "type": "integer"}}}}}']], [{"name":"mydb","tables":{"x":{"columns":{"y":{"type":"integer"}}}}}]) OVSDB_CHECK_NEGATIVE_CPY([schema with invalid refTables], [[parse-schema \ '{"name": "mydb", "tables": { "a": { "columns": { "map": { "type": { "key": { "type": "uuid", "refTable": "c"}, "value": { "type": "uuid", "refTable": "a"}}}}}, "b": { "columns": { "aRef": { "type": { "key": { "type": "uuid", "refTable": "a"}}}}}}}']], [[syntax error: column map key refers to undefined table c]]) OVSDB_CHECK_NEGATIVE_CPY([schema with invalid version number], [[parse-schema \ '{"name": "mydb", "tables": { "x": { "columns": { "y": { "type": "integer"}}}}, "version": "xxx"}']], [[schema version "xxx" not in format x.y.z]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-server.at000066400000000000000000003442601514270232600231020ustar00rootroot00000000000000AT_BANNER([OVSDB -- ovsdb-server transactions (Unix sockets)]) dnl OVSDB_SERVER_SHUTDOWN_N(N, [ALLOWLIST]) dnl dnl Similar to OVSDB_SERVER_SHUTDOWN, but stops the server started with N.pid dnl pidfile and unixctlN socket. m4_define([OVSDB_SERVER_SHUTDOWN_N], [AT_CHECK([check_logs $2]) cp $1.pid savepid$1 AT_CHECK([ovs-appctl -t "`pwd`"/unixctl$1 -e exit], [0], [ignore], [ignore]) OVS_WAIT_WHILE([kill -0 `cat savepid$1`], [kill `cat savepid$1`])]) m4_define([OVSDB_SERVER_SHUTDOWN2], [OVSDB_SERVER_SHUTDOWN_N([2], $1)]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # Additionally, checks that records written to a database file can be # read back producing the same in-memory database content. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server positive unix $5]) $2 > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \ --remote=punix:socket db], [0], [ignore], [ignore]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) AT_CHECK([ovsdb-client dump unix:socket], [0], [stdout], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \ --remote=punix:socket db], [0], [ignore], [ignore]) OVS_WAIT_UNTIL([ovsdb-client dump unix:socket > dump2; diff stdout dump2]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([ovsdb-server miscellaneous features]) AT_SETUP([truncating corrupted database log]) AT_KEYWORDS([ovsdb server positive unix]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) dnl Do one transaction and save the output. AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], []) cat stdout >> output dnl Add some crap to the database log and run another transaction, which should dnl ignore the crap and truncate it out of the log. echo 'xxx' >> db AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], [stderr]) AT_CHECK([grep 'syntax error: db: parse error.* in header line "xxx"' stderr], [0], [ignore]) cat stdout >> output dnl Run a final transaction to verify that both transactions succeeeded. dnl The crap that we added should have been truncated by the previous run, dnl so ovsdb-server shouldn't log a warning this time. AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "select", "table": "ordinals", "where": [], "sort": ["number"]}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], []) cat stdout >> output AT_CHECK([uuidfilt output], [0], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]], []) AT_CLEANUP AT_SETUP([truncating database log with bad transaction]) AT_KEYWORDS([ovsdb server positive unix]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) dnl Do one transaction and save the output. AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], []) cat stdout >> output dnl Add some crap to the database log and run another transaction, which should dnl ignore the crap and truncate it out of the log. echo 'OVSDB JSON 15 ffbcdae4b0386265f9ea3280dd7c8f0b72a20e56 {"invalid":{}}' >> db AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], [stderr]) AT_CHECK([grep 'syntax "{"invalid":{}}": unknown table: No table named invalid.' stderr], [0], [ignore]) cat stdout >> output dnl Run a final transaction to verify that both transactions succeeeded. dnl The crap that we added should have been truncated by the previous run, dnl so ovsdb-server shouldn't log a warning this time. AT_DATA([txnfile], [[ovsdb-client transact unix:socket \ '["ordinals", {"op": "select", "table": "ordinals", "where": [], "sort": ["number"]}]' ]]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], []) cat stdout >> output AT_CHECK([uuidfilt output], [0], [[[{"uuid":["uuid","<0>"]}] [{"uuid":["uuid","<1>"]}] [{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] ]], []) AT_CLEANUP dnl CHECK_DBS([databases]) dnl dnl Checks that ovsdb-server hosts the given 'databases', each of which dnl needs to be followed by a newline. m4_define([CHECK_DBS], [AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs], [0], [_Server $1]) AT_CHECK([ovsdb-client --no-headings dump _Server Database name | sort], [0], [dnl Database table _Server $1])]) AT_SETUP([database multiplexing implementation]) AT_KEYWORDS([ovsdb server positive]) ordinal_schema > schema1 constraint_schema > schema2 AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:db.sock db1 db2], [0], [ignore], [ignore]) CHECK_DBS([constraints ordinals ]) AT_CHECK( [[ovstest test-jsonrpc request unix:db.sock get_schema [\"nonexistent\"]]], [0], [[{"error":{"details":"get_schema request specifies unknown database nonexistent","error":"unknown database","syntax":"[\"nonexistent\"]"},"id":0,"result":null} ]], []) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([database multiplexing implementation with config file]) AT_KEYWORDS([ovsdb server positive config-file]) ordinal_schema > schema1 constraint_schema > schema2 AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) on_exit 'kill $(cat *.pid)' AT_DATA([config.json], [ {"remotes" : { "punix:db.sock": {} }, "databases": { "db1": {}, "db2": { "service-model": "standalone" } } } ]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \ --config-file=config.json], [0], [ignore], [ignore]) CHECK_DBS([constraints ordinals ]) AT_CHECK( [[ovstest test-jsonrpc request unix:db.sock get_schema [\"nonexistent\"]]], [0], [[{"error":{"details":"get_schema request specifies unknown database nonexistent","error":"unknown database","syntax":"[\"nonexistent\"]"},"id":0,"result":null} ]]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-server/add-db and remove-db]) AT_KEYWORDS([ovsdb server positive]) on_exit 'kill `cat *.pid`' ordinal_schema > schema1 constraint_schema > schema2 AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) # Start ovsdb-server with just a single database - db1. AT_CHECK([ovsdb-server -vfile -vvlog:off --log-file --detach --no-chdir --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) CHECK_DBS([ordinals ]) # Remove the database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [0]) CHECK_DBS([]) # Start monitoring processes. AT_CHECK([ovsdb-client --detach --no-chdir --pidfile=ovsdb-client-1.pid --no-db-change-aware --no-headings monitor _Server Database name > db-change-unaware.stdout 2> db-change-unaware.stderr]) AT_CHECK([ovsdb-client --detach --no-chdir --pidfile=ovsdb-client-2.pid --db-change-aware --no-headings monitor _Server Database name > db-change-aware.stdout 2> db-change-aware.stderr]) AT_CAPTURE_FILE([db-change-unaware.stdout]) AT_CAPTURE_FILE([db-change-unaware.stderr]) AT_CAPTURE_FILE([db-change-aware.stdout]) AT_CAPTURE_FILE([db-change-aware.stderr]) # Add the first database back. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db1], [0]) CHECK_DBS([ordinals ]) # Add the second database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0]) CHECK_DBS([constraints ordinals ]) # The databases are responsive. AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client list-tables unix:db.sock ordinals], [0], [ignore], [ignore]) # Add an already added database. if test $IS_WIN32 = "yes"; then AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], 2, [], [I/O error: db2: failed to lock lockfile (Resource deadlock avoided) ovs-appctl: ovsdb-server: server returned an error ]) else AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], 2, [], [ovsdb error: db2: already open ovs-appctl: ovsdb-server: server returned an error ]) fi # Add a non-existing database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db3], 2, [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [I/O error: db3: open failed (...) ovs-appctl: ovsdb-server: server returned an error ]) # Add a remote through a db path in db1. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote db:ordinals,ordinals,name], [0]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [db:ordinals,ordinals,name punix:db.sock ]) # Removing db1 has no effect on its remote. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [0]) CHECK_DBS([constraints ]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [db:ordinals,ordinals,name punix:db.sock ]) AT_CHECK([ovsdb-client list-tables unix:db.sock ordinals], [1], [ignore], [ignore]) # Remove db2. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db constraints], [0]) CHECK_DBS() AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [1], [ignore], [ignore]) # Remove a non-existent database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [2], [], [Failed to find the database. ovs-appctl: ovsdb-server: server returned an error ]) # Add a removed database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0]) CHECK_DBS([constraints ]) AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore]) # Check the monitoring results. AT_CHECK([uuidfilt db-change-aware.stdout], [0], [dnl <0> initial _Server <1> insert ordinals <2> insert constraints <1> delete ordinals <2> delete constraints <3> insert constraints ]) AT_CHECK([uuidfilt db-change-unaware.stdout], [0], [dnl <0> initial _Server ]) OVSDB_SERVER_SHUTDOWN(["/no database named ordinals/d"]) AT_CLEANUP AT_SETUP([ovsdb-server/add-db and remove-db with a config file]) AT_KEYWORDS([ovsdb server positive config-file]) on_exit 'kill $(cat *.pid)' ordinal_schema > schema1 constraint_schema > schema2 AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) dnl Start ovsdb-server with just a single database - db1. AT_DATA([config.json], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db1": {} } } ]) AT_CAPTURE_FILE([config.json]) AT_CHECK([ovsdb-server -vfile -vvlog:off --log-file --detach --no-chdir \ --pidfile --config-file=config.json], [0], [ignore], [ignore]) CHECK_DBS([ordinals ]) dnl Remove the database. AT_CHECK([sed -i'back' '/db1/d' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) CHECK_DBS([]) dnl Start monitoring processes. AT_CHECK([ovsdb-client --detach --no-chdir --pidfile=ovsdb-client-1.pid \ --no-db-change-aware --no-headings monitor _Server Database name \ > db-change-unaware.stdout 2> db-change-unaware.stderr]) AT_CHECK([ovsdb-client --detach --no-chdir --pidfile=ovsdb-client-2.pid \ --db-change-aware --no-headings monitor _Server Database name \ > db-change-aware.stdout 2> db-change-aware.stderr]) AT_CAPTURE_FILE([db-change-unaware.stdout]) AT_CAPTURE_FILE([db-change-unaware.stderr]) AT_CAPTURE_FILE([db-change-aware.stdout]) AT_CAPTURE_FILE([db-change-aware.stderr]) dnl Add the first database back. AT_CHECK([sed -i'back' '/"databases"/a\ "db1": {} ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) CHECK_DBS([ordinals ]) dnl Add the second database. AT_CHECK([sed -i'back' '/"databases"/a\ "db2": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) CHECK_DBS([constraints ordinals ]) dnl The databases are responsive. AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client list-tables unix:db.sock ordinals], [0], [ignore], [ignore]) dnl Add an already added database. AT_CHECK([sed -i'back' '/"databases"/a\ "db2": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) dnl Fix the config back. AT_CHECK([sed -i'back' '/db2/d' config.json]) AT_CHECK([sed -i'back' '/"databases"/a\ "db2": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) dnl Add a non-existing database. AT_CHECK([sed -i'back' '/"databases"/a\ "db3": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload], [2], [ignore], [ignore]) OVS_WAIT_UNTIL([grep -q 'failed to configure databases' ovsdb-server.log]) AT_CHECK([sed -i'back' '/db3/d' config.json]) dnl Add a remote through a db path in db1. AT_CHECK([sed -i'back' '/"remotes"/a\ "db:ordinals,ordinals,name": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [db:ordinals,ordinals,name punix:db.sock ]) dnl Removing db1 has no effect on its remote. AT_CHECK([sed -i'back' '/db1/d' config.json]) AT_CHECK([sed -i'back' 's/"db2": {},/"db2": {}/' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload], [2], [ignore], [ignore]) CHECK_DBS([constraints ]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [db:ordinals,ordinals,name punix:db.sock ]) AT_CHECK([ovsdb-client list-tables unix:db.sock ordinals], [1], [ignore], [ignore]) dnl Remove now missing remote. AT_CHECK([sed -i'back' '/db:ordinals,ordinals,name/d' config.json]) dnl Remove db2. AT_CHECK([sed -i'back' '/db2/d' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) CHECK_DBS() AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [1], [ignore], [ignore]) dnl Add a removed database. AT_CHECK([sed -i'back' '/"databases"/a\ "db2": {} ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) CHECK_DBS([constraints ]) AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore]) # Check the monitoring results. AT_CHECK([uuidfilt db-change-aware.stdout], [0], [dnl <0> initial _Server <1> insert ordinals <2> insert constraints <1> delete ordinals <2> delete constraints <3> insert constraints ]) AT_CHECK([uuidfilt db-change-unaware.stdout], [0], [dnl <0> initial _Server ]) OVSDB_SERVER_SHUTDOWN([" /no database named ordinals/d /failed to open database 'db3'/d /failed to configure databases/d "]) AT_CLEANUP AT_SETUP([ovsdb-server/add-db with --monitor]) AT_KEYWORDS([ovsdb server positive]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS # Start ovsdb-server, initially with one db. ordinal_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vfile -vvlog:off --monitor --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db1], [0], [ignore], [ignore]) # Add the second database. constraint_schema > schema2 AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0]) CHECK_DBS([constraints ordinals ]) # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned. cp ovsdb-server.pid old.pid AT_CHECK([kill -SEGV `cat ovsdb-server.pid`]) OVS_WAIT_WHILE([kill -0 `cat old.pid`]) OVS_WAIT_UNTIL( [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`]) OVS_WAIT_UNTIL([ovs-appctl -t ovsdb-server version]) CHECK_DBS([constraints ordinals ]) OVSDB_SERVER_SHUTDOWN([" /backtrace/d /killed/d "]) AT_CLEANUP AT_SETUP([ovsdb-server/add-db and remove-db with --monitor]) AT_KEYWORDS([ovsdb server positive]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS # Start ovsdb-server, initially with one db. ordinal_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [ignore], [ignore]) constraint_schema > schema2 AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vfile -vvlog:off --monitor --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db1 db2], [0], [ignore], [ignore]) # Remove the second database. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db constraints]) CHECK_DBS([ordinals ]) # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned. cp ovsdb-server.pid old.pid AT_CHECK([kill -SEGV `cat ovsdb-server.pid`]) OVS_WAIT_WHILE([kill -0 `cat old.pid`]) OVS_WAIT_UNTIL( [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`]) OVS_WAIT_UNTIL([ovs-appctl -t ovsdb-server version]) CHECK_DBS([ordinals ]) OVSDB_SERVER_SHUTDOWN([" /backtrace/d /killed/d "]) AT_CLEANUP AT_SETUP([--remote=db: implementation]) AT_KEYWORDS([ovsdb server positive]) AT_DATA([schema], [[{"name": "mydb", "tables": { "Root": { "columns": { "managers": { "type": { "key": "string", "min": 0, "max": "unlimited"}}, "manager_options": { "type": { "key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}}}, "Manager": { "columns": { "target": { "type": "string"}, "is_connected": { "type": { "key": "boolean", "min": 0, "max": 1}}}}}} ]]) AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) AT_CHECK( [[ovsdb-tool transact db \ '["mydb", {"op": "insert", "table": "Root", "row": { "managers": "punix:socket1", "manager_options": ["set", [["named-uuid", "x"]]]}}, {"op": "insert", "table": "Manager", "uuid-name": "x", "row": {"target": "punix:socket2"}}]']], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=db:mydb,Root,managers --remote=db:mydb,Root,manager_options db], [0], [ignore], [ignore]) ovs-appctl -t ovsdb-server time/warp 6000 1000 AT_CHECK( [[ovsdb-client transact unix:socket1 \ '["mydb", {"op": "select", "table": "Root", "where": [], "columns": ["managers"]}, {"op": "select", "table": "Manager", "where": [], "columns": ["target", "is_connected"]}]']], [0], [stdout], [ignore]) AT_CHECK( [uuidfilt stdout], [0], [[[{"rows":[{"managers":"punix:socket1"}]},{"rows":[{"is_connected":false,"target":"punix:socket2"}]}] ]], [ignore]) OVSDB_SERVER_SHUTDOWN([" /No status column present in the Manager table/d "]) AT_CLEANUP AT_SETUP([ovsdb-server/add-remote and remove-remote]) AT_KEYWORDS([ovsdb server positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile db], [0], [ignore], [ignore]) AT_CHECK([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1]) if test "$IS_WIN32" = "yes"; then OVS_WAIT_UNTIL([test -e socket1]) else OVS_WAIT_UNTIL([test -S socket1]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 ]) AT_CHECK([test ! -e socket2]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket2]) if test "$IS_WIN32" = "yes"; then OVS_WAIT_UNTIL([test -e socket2]) else OVS_WAIT_UNTIL([test -S socket2]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 punix:socket2 ]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote db:x,y,z], [2], [], ["db:x,y,z": no database named x ovs-appctl: ovsdb-server: server returned an error ]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket1]) OVS_WAIT_UNTIL([test ! -e socket1]) if test "$IS_WIN32" = "yes"; then AT_CHECK([test -e socket2]) else AT_CHECK([test -S socket2]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket2 ]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket2]) OVS_WAIT_UNTIL([test ! -e socket2]) AT_CHECK([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-server/add-remote and remove-remote with config file]) AT_KEYWORDS([ovsdb server positive config-file]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill $(cat *.pid)' AT_DATA([config.json], [ { "remotes": { }, "databases": { "db": {} } } ]) AT_CAPTURE_FILE([config.json]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file --pidfile \ --config-file=config.json], [0], [ignore], [ignore]) AT_CHECK([test ! -e socket1]) AT_CHECK([sed -i'back' '/"remotes"/a\ "punix:socket1": {} ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) if test "$IS_WIN32" = "yes"; then OVS_WAIT_UNTIL([test -e socket1]) else OVS_WAIT_UNTIL([test -S socket1]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 ]) AT_CHECK([test ! -e socket2]) AT_CHECK([sed -i'back' '/"remotes"/a\ "punix:socket2": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) if test "$IS_WIN32" = "yes"; then OVS_WAIT_UNTIL([test -e socket2]) else OVS_WAIT_UNTIL([test -S socket2]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 punix:socket2 ]) AT_CHECK([sed -i'back' '/"remotes"/a\ "db:x,y,z": {}, ' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload], [2], [ignore], [ignore]) OVS_WAIT_UNTIL([grep -q '"db:x,y,z": no database named x' ovsdb-server.log]) AT_CHECK([sed -i'back' '/db:x,y,z/d' config.json]) AT_CHECK([sed -i'back' '/punix:socket1/d' config.json]) AT_CHECK([sed -i'back' 's/"punix:socket2": {},/"punix:socket2": {}/' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) OVS_WAIT_UNTIL([test ! -e socket1]) if test "$IS_WIN32" = "yes"; then AT_CHECK([test -e socket2]) else AT_CHECK([test -S socket2]) fi AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket2 ]) AT_CHECK([sed -i'back' '/punix:socket2/d' config.json]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/reload]) OVS_WAIT_UNTIL([test ! -e socket2]) AT_CHECK([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes]) OVSDB_SERVER_SHUTDOWN(['/"db:x,y,z": no database named x/d']) AT_CLEANUP AT_SETUP([ovsdb-server/add-remote with --monitor]) AT_KEYWORDS([ovsdb server positive]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS # Start ovsdb-server, initially with no remotes. ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vfile -vvlog:off --monitor --detach --no-chdir --pidfile --log-file db], [0], [ignore], [ignore]) # Add a remote. AT_CHECK([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1]) OVS_WAIT_UNTIL([test -S socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 ]) # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned and for it to # start listening on 'socket1'. cp ovsdb-server.pid old.pid rm socket1 AT_CHECK([kill -SEGV `cat ovsdb-server.pid`]) OVS_WAIT_WHILE([kill -0 `cat old.pid`]) OVS_WAIT_UNTIL( [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`]) OVS_WAIT_UNTIL([ovs-appctl -t ovsdb-server version]) OVS_WAIT_UNTIL([test -S socket1]) OVSDB_SERVER_SHUTDOWN([" /backtrace/d /killed/d "]) AT_CLEANUP AT_SETUP([ovsdb-server/add-remote and remove-remote with --monitor]) AT_KEYWORDS([ovsdb server positive]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) # This test intentionally causes SIGSEGV, so make sanitizers ignore it. ASAN_OPTIONS=$ASAN_OPTIONS:handle_segv=0; export ASAN_OPTIONS UBSAN_OPTIONS=$UBSAN_OPTIONS:handle_segv=0; export UBSAN_OPTIONS # Start ovsdb-server, initially with no remotes. ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vfile -vvlog:off --monitor --detach --no-chdir --pidfile --log-file db], [0], [ignore], [ignore]) # Add a remote. AT_CHECK([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1]) OVS_WAIT_UNTIL([test -S socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [punix:socket1 ]) # Remove the remote. AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket1]) OVS_WAIT_UNTIL([test ! -e socket1]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes]) # Kill the daemon process, making it look like a segfault, # and wait for a new daemon process to get spawned and make sure that it # does not listen on 'socket1'. cp ovsdb-server.pid old.pid AT_CHECK([kill -SEGV `cat ovsdb-server.pid`]) OVS_WAIT_WHILE([kill -0 `cat old.pid`]) OVS_WAIT_UNTIL( [test -s ovsdb-server.pid && test `cat ovsdb-server.pid` != `cat old.pid`]) OVS_WAIT_UNTIL([ovs-appctl -t ovsdb-server version]) AT_CHECK([test ! -e socket1]) OVSDB_SERVER_SHUTDOWN([" /backtrace/d /killed/d "]) AT_CLEANUP AT_SETUP([SSL/TLS db: implementation]) AT_KEYWORDS([ovsdb server positive ssl tls $5]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) # For this test, we pass PKIDIR through a ovsdb-tool transact and # msys on Windows does not convert the path style automatically. # So, do that forcefully with a 'pwd -W' (called through pwd() function). PKIDIR="$(cd $abs_top_builddir/tests && pwd)" AT_SKIP_IF([expr "$PKIDIR" : ".*[[ '\" \\]]"]) AT_DATA([schema], [[{"name": "mydb", "tables": { "SSL": { "columns": { "private_key": {"type": "string"}, "certificate": {"type": "string"}, "ca_cert": {"type": "string"}, "ssl_protocols" : {"type": "string"}, "ssl_ciphers" : {"type" : "string"}, "ssl_ciphersuites" : {"type": "string"} }}}} ]]) AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) # The !ECDHE-ECDSA-AES256-GCM-SHA384 in the ssl_ciphers is so that # a cipher negotiation failure can be tested for later. Same for # the TLS_CHACHA20_POLY1305_SHA256 ciphersuite. AT_CHECK( [[ovsdb-tool transact db \ '["mydb", {"op": "insert", "table": "SSL", "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'", "certificate": "'"$PKIDIR/testpki-cert2.pem"'", "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'", "ssl_protocols": "'"TLSv1.3,TLSv1.2"'", "ssl_ciphers": "'"DEFAULT:@SECLEVEL=2:!ECDHE-ECDSA-AES256-GCM-SHA384"'", "ssl_ciphersuites": "TLS_CHACHA20_POLY1305_SHA256" }}]']], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK( [ovsdb-server --log-file --detach --no-chdir --pidfile \ --private-key=db:mydb,SSL,private_key \ --certificate=db:mydb,SSL,certificate \ --ca-cert=db:mydb,SSL,ca_cert \ --ssl-protocols=db:mydb,SSL,ssl_protocols \ --ssl-ciphers=db:mydb,SSL,ssl_ciphers \ --ssl-ciphersuites=db:mydb,SSL,ssl_ciphersuites \ --remote=pssl:0:127.0.0.1 db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # SSL_OVSDB_CLIENT(PROTOCOL, [CIPHERS], [CIPHERSUITES]) m4_define([SSL_OVSDB_CLIENT], [dnl ovsdb-client -vconsole:stream_ssl:dbg \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ --ssl-protocols=[$1] \ m4_if([$2], [], [], [--ssl-ciphers=$2]) \ m4_if([$3], [], [], [--ssl-ciphersuites=$3]) \ transact ssl:127.0.0.1:$SSL_PORT \ '[[["mydb", {"op": "select", "table": "SSL", "where": [], "columns": ["private_key"]}]]]']) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.3,TLSv1.2]), [0], [stdout], [ignore]) AT_CHECK_UNQUOTED( [cat stdout], [0], [[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-privkey2.pem"}@:>@}@:>@ ]], [ignore]) # Check that connection over TLSv1.2 works. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2]), [0], [stdout], [ignore]) AT_CHECK_UNQUOTED( [cat stdout], [0], [[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-privkey2.pem"}@:>@}@:>@ ]], [ignore]) # Check that connection over TLSv1.3 works. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.3]), [0], [stdout], [ignore]) AT_CHECK_UNQUOTED( [cat stdout], [0], [[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-privkey2.pem"}@:>@}@:>@ ]], [ignore]) # Check that when ciphers are not compatible, a negotiation # failure occurs. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2], [ECDHE-ECDSA-AES256-GCM-SHA384]), [1], [stdout], [stderr]) cat stderr > output AT_CHECK_UNQUOTED( [sed -n "/failed to connect/s/ (.*)//p" output], [0], [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" ], [ignore]) # The error message for being unable to negotiate a shared ciphersuite # is 'sslv3 alert handshake failure'. This is not the clearest message. # In openssl 3.2.0 all the error messages were updated to replace 'sslv3' # with 'ssl/tls'. AT_CHECK_UNQUOTED( [grep -E "(sslv3|ssl/tls) alert handshake failure" output], [0], [stdout], [ignore]) # Check that when ciphersuites are not compatible, a negotiation # failure occurs. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.3], [DEFAULT], [TLS_AES_256_GCM_SHA384]), [1], [stdout], [stderr]) cat stderr > output AT_CHECK_UNQUOTED( [sed -n "/failed to connect/s/ (.*)//p" output], [0], [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" ], [ignore]) AT_CHECK([grep -q -E "(sslv3|ssl/tls) alert handshake failure" output]) # Checking parsing of different protocol ranges. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2,TLSv1.3]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'Enabled protocol range: TLSv1.2 - TLSv1.3' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2-TLSv1.3]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'Enabled protocol range: TLSv1.2 - TLSv1.3' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.3-TLSv1.2]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'Enabled protocol range: TLSv1.2 - TLSv1.3' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.3+]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'Enabled protocol range: TLSv1.3 or later' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2-TLSv1.3,TLSv1.3+]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'SSL/TLS protocol not recognized' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2+TLSv1.3]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'SSL/TLS protocol not recognized' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1+]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'SSL/TLS protocol not recognized' stderr]) AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.1]), [0], [stdout], [stderr]) AT_CHECK([grep -q 'SSL/TLS protocol not recognized' stderr]) OVSDB_SERVER_SHUTDOWN([" /stream_ssl|WARN/d /Protocol error/d "]) AT_CLEANUP AT_SETUP([SSL/TLS db: implementation (TLSv1.3 only)]) AT_KEYWORDS([ovsdb server positive ssl tls $5]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) # For this test, we pass PKIDIR through a ovsdb-tool transact and # msys on Windows does not convert the path style automatically. # So, do that forcefully with a 'pwd -W' (called through pwd() function). PKIDIR="$(cd $abs_top_builddir/tests && pwd)" AT_SKIP_IF([expr "$PKIDIR" : ".*[[ '\" \\]]"]) AT_DATA([schema], [[{"name": "mydb", "tables": { "SSL": { "columns": { "private_key": {"type": "string"}, "certificate": {"type": "string"}, "ca_cert": {"type": "string"}, "ssl_protocols" : {"type": "string"}, "ssl_ciphers" : {"type" : "string"}, "ssl_ciphersuites" : {"type": "string"} }}}} ]]) AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CHECK( [[ovsdb-tool transact db \ '["mydb", {"op": "insert", "table": "SSL", "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'", "certificate": "'"$PKIDIR/testpki-cert2.pem"'", "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'", "ssl_protocols": "'"TLSv1.3"'" }}]']], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK( [ovsdb-server --log-file --detach --no-chdir --pidfile \ --private-key=db:mydb,SSL,private_key \ --certificate=db:mydb,SSL,certificate \ --ca-cert=db:mydb,SSL,ca_cert \ --ssl-protocols=db:mydb,SSL,ssl_protocols \ --ssl-ciphers=db:mydb,SSL,ssl_ciphers \ --ssl-ciphersuites=db:mydb,SSL,ssl_ciphersuites \ --remote=pssl:0:127.0.0.1 db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) # SSL_OVSDB_CLIENT(PROTOCOL) m4_define([SSL_OVSDB_CLIENT], [dnl ovsdb-client -vconsole:stream_ssl:dbg \ --private-key=$PKIDIR/testpki-privkey.pem \ --certificate=$PKIDIR/testpki-cert.pem \ --ca-cert=$PKIDIR/testpki-cacert.pem \ --ssl-protocols=[$1] \ transact ssl:127.0.0.1:$SSL_PORT \ '[[["mydb", {"op": "select", "table": "SSL", "where": [], "columns": ["private_key"]}]]]']) # Check that, when the server has TLSv1.3 and the client has # TLSv1.2, connection fails. AT_CHECK(SSL_OVSDB_CLIENT([TLSv1.2]), [1], [stdout], [stderr]) cat stderr > output AT_CHECK_UNQUOTED( [sed -n "/failed to connect/s/ (.*)//p" output], [0], [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" ], [ignore]) AT_CHECK([grep -q 'Enabled protocol range: TLSv1.2 - TLSv1.2' stderr]) OVSDB_SERVER_SHUTDOWN([" /stream_ssl|WARN/d /Protocol error/d "]) AT_CLEANUP OVS_START_SHELL_HELPERS # ovsdb_check_online_compaction MODEL # # where MODEL is "standalone" or "cluster" ovsdb_check_online_compaction() { local model=$1 ordinal_schema > schema dnl Make sure that "ovsdb-tool create" works with a dangling symlink for dnl the database and the lockfile, creating the target of each symlink rather dnl than replacing the symlinks with regular files. mkdir dir if test "$IS_WIN32" = "no"; then ln -s dir/db db ln -s dir/.db.~lock~ .db.~lock~ AT_SKIP_IF([test ! -h db || test ! -h .db.~lock~]) fi AT_CHECK([if test $model = standalone; then ovsdb-tool create db schema else ovsdb-tool create-cluster db schema unix:s1.raft fi]) dnl Start ovsdb-server. on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vvlog:off -vconsole:off --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) AT_CHECK([ovsdb_client_wait unix:socket ordinals connected]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Do a bunch of random transactions that put crap in the database log. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-client transact unix:socket ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' ovsdb-client transact unix:socket ' ["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", '$2']]}, {"op": "comment", "comment": "delete row for '"$2"'"}]' ovsdb-client transact unix:socket ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add back row for '"$pair"'"}]' done]], [0], [stdout]) if test $model = standalone; then dnl Check that all the crap is in fact in the database log. AT_CHECK([[uuidfilt db | grep -v ^OVSDB | \ sed 's/"_date":[0-9]*/"_date":0/' | sed 's/"_is_diff":true,//' | \ ovstest test-json --multiple -]], [0], [[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"],["number","name"]]}},"version":"5.1.3"} {"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}} {"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}} {"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}} {"_comment":"add row for one 1","_date":0,"ordinals":{"<2>":{"name":"one","number":1}}} {"_comment":"delete row for 1","_date":0,"ordinals":{"<2>":null}} {"_comment":"add back row for one 1","_date":0,"ordinals":{"<3>":{"name":"one","number":1}}} {"_comment":"add row for two 2","_date":0,"ordinals":{"<4>":{"name":"two","number":2}}} {"_comment":"delete row for 2","_date":0,"ordinals":{"<4>":null}} {"_comment":"add back row for two 2","_date":0,"ordinals":{"<5>":{"name":"two","number":2}}} {"_comment":"add row for three 3","_date":0,"ordinals":{"<6>":{"name":"three","number":3}}} {"_comment":"delete row for 3","_date":0,"ordinals":{"<6>":null}} {"_comment":"add back row for three 3","_date":0,"ordinals":{"<7>":{"name":"three","number":3}}} {"_comment":"add row for four 4","_date":0,"ordinals":{"<8>":{"name":"four","number":4}}} {"_comment":"delete row for 4","_date":0,"ordinals":{"<8>":null}} {"_comment":"add back row for four 4","_date":0,"ordinals":{"<9>":{"name":"four","number":4}}} {"_comment":"add row for five 5","_date":0,"ordinals":{"<10>":{"name":"five","number":5}}} {"_comment":"delete row for 5","_date":0,"ordinals":{"<10>":null}} {"_comment":"add back row for five 5","_date":0,"ordinals":{"<11>":{"name":"five","number":5}}} ]]) else dnl Check that at least there's a lot of transactions. AT_CHECK([test `wc -l < db` -gt 50]) fi dnl Dump out and check the actual database contents. AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) cp db db.pre-compaction dnl Now compact the database in-place. AT_CHECK([[ovs-appctl -t ovsdb-server ovsdb-server/compact]], [0], [], [ignore]) dnl Negative test. AT_CHECK([[ovs-appctl -t ovsdb-server ovsdb-server/compact _Server]], [2], [], [cannot compact built-in databases ovs-appctl: ovsdb-server: server returned an error ]) dnl Make sure that "db" is still a symlink to dir/db instead of getting dnl replaced by a regular file, ditto for .db.~lock~. if test "$IS_WIN32" = "no"; then AT_CHECK([test -h db]) AT_CHECK([test -h .db.~lock~]) AT_CHECK([test -f dir/db]) AT_CHECK([test -f dir/.db.~lock~]) fi # We can't fully re-check the contents of the database log, because the # order of the records is not predictable, but there should only be 4 lines # in it now in the standalone case AT_CAPTURE_FILE([db]) compacted_lines=`wc -l < db` echo compacted_lines=$compacted_lines if test $model = standalone; then AT_CHECK([test $compacted_lines -eq 4]) fi dnl And check that the dumped data is the same too: AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) dnl Now do some more transactions. AT_CHECK( [[ovsdb-client transact unix:socket ' ["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "<", 3]]}]']], [0], [[[{"count":3}] ]], [ignore]) dnl There should be 6 lines in the log now, for the standalone case, dnl and for the clustered case the file should at least have grown. updated_lines=`wc -l < db` echo compacted_lines=$compacted_lines updated_lines=$updated_lines if test $model = standalone; then AT_CHECK([test $updated_lines -eq 6]) else AT_CHECK([test $updated_lines -gt $compacted_lines]) fi dnl Then check that the dumped data is correct. This time first kill dnl and restart the database server to ensure that the data is correct on dnl disk as well as in memory. OVSDB_SERVER_SHUTDOWN AT_CHECK([ovsdb-server -vvlog:off -vconsole:off --detach --no-chdir --pidfile --remote=punix:socket --log-file db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> three 3 ], []) OVSDB_SERVER_SHUTDOWN } OVS_END_SHELL_HELPERS AT_SETUP([compacting online - standalone]) AT_KEYWORDS([ovsdb server compact]) ovsdb_check_online_compaction standalone AT_CLEANUP AT_SETUP([compacting online - cluster]) AT_KEYWORDS([ovsdb server compact]) ovsdb_check_online_compaction cluster AT_CLEANUP OVS_START_SHELL_HELPERS # ovsdb_check_online_conversion MODEL # # where MODEL is "standalone" or "cluster" ovsdb_check_online_conversion() { local model=$1 on_exit 'kill `cat *.pid`' ordinal_schema > schema AT_DATA([new-schema], [[{"name": "ordinals", "tables": { "ordinals": { "columns": { "number": {"type": "integer"}}}}} ]]) dnl Make sure that "ovsdb-tool create" works with a dangling symlink for dnl the database and the lockfile, creating the target of each symlink dnl rather than replacing the symlinks with regular files. mkdir dir if test "$IS_WIN32" = "no"; then ln -s dir/db db ln -s dir/.db.~lock~ .db.~lock~ AT_SKIP_IF([test ! -h db || test ! -h .db.~lock~]) fi AT_CHECK([if test $model = standalone; then ovsdb-tool create db schema else ovsdb-tool create-cluster db schema unix:s1.raft fi]) dnl Start the database server. AT_CHECK([ovsdb-server -vfile -vvlog:off -vconsole:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db], [0], [ignore], [ignore]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Put some data in the database. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-client transact ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] [{"uuid":["uuid","<1>"]},{}] [{"uuid":["uuid","<2>"]},{}] [{"uuid":["uuid","<3>"]},{}] [{"uuid":["uuid","<4>"]},{}] [{"uuid":["uuid","<5>"]},{}] ]], [ignore]) dnl Try "needs-conversion". AT_CHECK([ovsdb-client needs-conversion schema], [0], [no ]) AT_CHECK([ovsdb-client needs-conversion new-schema], [0], [yes ]) dnl Start two monitors on the 'ordinals' db, one that is database dnl change aware and one that is not. AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=monitor-ordinals-aware.pid --log-file=monitor-ordinals-aware.log --db-change-aware --no-headings monitor ordinals ordinals number name > monitor-ordinals-aware.stdout 2> monitor-ordinals-aware.stderr]) AT_CAPTURE_FILE([monitor-ordinals-aware.stdout]) AT_CAPTURE_FILE([monitor-ordinals-aware.log]) AT_CAPTURE_FILE([monitor-ordinals-aware.stderr]) AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=monitor-ordinals-unaware.pid --log-file=monitor-ordinals-unaware.log --no-db-change-aware --no-headings monitor ordinals ordinals number name > monitor-ordinals-unaware.stdout 2> monitor-ordinals-unaware.stderr]) AT_CAPTURE_FILE([monitor-ordinals-unaware.stdout]) AT_CAPTURE_FILE([monitor-ordinals-unaware.log]) AT_CAPTURE_FILE([monitor-ordinals-unaware.stderr]) dnl Start two monitors on the '_Server' db, one that is database dnl change aware and one that is not. AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=monitor-server-aware.pid --log-file=monitor-server-aware.log --db-change-aware --no-headings monitor _Server Database name > monitor-server-aware.stdout 2> monitor-server-aware.stderr]) AT_CAPTURE_FILE([monitor-server-aware.stdout]) AT_CAPTURE_FILE([monitor-server-aware.log]) AT_CAPTURE_FILE([monitor-server-aware.stderr]) AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=monitor-server-unaware.pid --log-file=monitor-server-unaware.log --no-db-change-aware --no-headings monitor _Server Database name > monitor-server-unaware.stdout 2> monitor-server-unaware.stderr]) AT_CAPTURE_FILE([monitor-server-unaware.stdout]) AT_CAPTURE_FILE([monitor-server-unaware.log]) AT_CAPTURE_FILE([monitor-server-unaware.stderr]) dnl Start two long-running transactions (triggers) on the 'ordinals' db, dnl one that is database change aware and one that is not. ordinals_txn='[["ordinals", {"op": "wait", "table": "ordinals", "where": [["name", "==", "seven"]], "columns": ["name", "number"], "rows": [], "until": "!="}]]' AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=trigger-ordinals-aware.pid --log-file=trigger-ordinals-aware.log --db-change-aware transact "$ordinals_txn" > trigger-ordinals-aware.stdout 2> trigger-ordinals-aware.stderr]) AT_CAPTURE_FILE([trigger-ordinals-aware.stdout]) AT_CAPTURE_FILE([trigger-ordinals-aware.log]) AT_CAPTURE_FILE([trigger-ordinals-aware.stderr]) AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=trigger-ordinals-unaware.pid --log-file=trigger-ordinals-unaware.log --no-db-change-aware transact "$ordinals_txn" > trigger-ordinals-unaware.stdout 2> trigger-ordinals-unaware.stderr]) AT_CAPTURE_FILE([trigger-ordinals-unaware.stdout]) AT_CAPTURE_FILE([trigger-ordinals-unaware.log]) AT_CAPTURE_FILE([trigger-ordinals-unaware.stderr]) dnl Start two long-running transactions (triggers) on the _Server db, dnl one that is database change aware and one that is not. server_txn='[["_Server", {"op": "wait", "table": "Database", "where": [["name", "==", "xyzzy"]], "columns": ["name"], "rows": [], "until": "!="}]]' AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=trigger-server-aware.pid --log-file=trigger-server-aware.log --db-change-aware transact "$server_txn" > trigger-server-aware.stdout 2> trigger-server-aware.stderr]) AT_CAPTURE_FILE([trigger-server-aware.stdout]) AT_CAPTURE_FILE([trigger-server-aware.log]) AT_CAPTURE_FILE([trigger-server-aware.stderr]) AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir --pidfile=trigger-server-unaware.pid --log-file=trigger-server-unaware.log --no-db-change-aware transact "$server_txn" > trigger-server-unaware.stdout 2> trigger-server-unaware.stderr]) AT_CAPTURE_FILE([trigger-server-unaware.stdout]) AT_CAPTURE_FILE([trigger-server-unaware.log]) AT_CAPTURE_FILE([trigger-server-unaware.stderr]) dnl Dump out and check the actual database contents. AT_CHECK([ovsdb-client dump unix:db.sock ordinals], [0], [stdout]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) dnl Convert the database. AT_CHECK([ovsdb-client convert new-schema]) dnl Try "needs-conversion". AT_CHECK([ovsdb-client needs-conversion schema], [0], [yes ]) AT_CHECK([ovsdb-client needs-conversion new-schema], [0], [no ]) dnl Verify that the "ordinals" monitors behaved as they should have. dnl Both should have exited, for different reasons. for x in aware unaware; do echo $x OVS_WAIT_WHILE([test -e monitor-ordinals-$x.pid]) AT_CHECK([sort -k 3 monitor-ordinals-$x.stdout | uuidfilt], [0], [<0> initial 0 zero <1> initial 1 one <2> initial 2 two <3> initial 3 three <4> initial 4 four <5> initial 5 five ]) done AT_CHECK([sed 's/.*: //' monitor-ordinals-unaware.stderr], [0], [receive failed (End of file) ]) AT_CHECK([sed 's/.*: //' monitor-ordinals-aware.stderr], [0], [ordinals database was removed ]) dnl Verify that the _Server monitors behaved as they should have. dnl The db-aware monitor should still be running, but not the unaware one. for x in aware unaware; do AT_CHECK([sort -k 3 monitor-server-$x.stdout | uuidfilt], [0], [<0> initial _Server <1> initial ordinals ]) done OVS_WAIT_WHILE([test -e monitor-server-unaware.pid]) AT_CHECK([sed 's/.*: //' monitor-ordinals-unaware.stderr], [0], [receive failed (End of file) ]) AT_CHECK([test -e monitor-server-aware.pid]) dnl Verify that the "ordinals" triggers behaved as they should have: dnl Both should have exited, for different reasons. for x in unaware aware; do OVS_WAIT_WHILE([test -e trigger-ordinals-$x.pid]) AT_CHECK([cat trigger-ordinals-$x.stdout]) done AT_CHECK([cat trigger-ordinals-unaware.stderr], [0], [ovsdb-client: transaction failed (End of file) ]) AT_CHECK([cat trigger-ordinals-aware.stderr], [0], [ovsdb-client: transaction returned error: "canceled" ]) dnl Verify that the _Server triggers behaved as they should have: dnl The db-aware trigger should still be waiting, but not the unaware one. for x in aware unaware; do AT_CHECK([cat trigger-server-$x.stdout]) done OVS_WAIT_WHILE([test -e trigger-server-unaware.pid]) AT_CHECK([sed 's/.*: //' trigger-ordinals-unaware.stderr], [0], [transaction failed (End of file) ]) AT_CHECK([test -e trigger-server-aware.pid]) AT_CAPTURE_FILE([db]) if test $model = standalone; then dnl We can't fully re-check the contents of the database log, because the dnl order of the records is not predictable, but there should only be 4 lines dnl in it now. AT_CHECK([test `wc -l < db` -eq 4]) fi dnl Check that the dumped data is the same except for the removed column: AT_CHECK([ovsdb-client dump unix:db.sock ordinals | uuidfilt], [0], [dnl ordinals table _uuid number ------------------------------------ ------ <0> 0 <1> 1 <2> 2 <3> 3 <4> 4 <5> 5 ]) dnl Now check that the converted database is still online and can be modified, dnl then check that the database log has one more record and that the data dnl is as expected. AT_CHECK( [[ovsdb-client transact ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 6}}, {"op": "comment", "comment": "add row for 6"}]' | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] ]]) if test $model = standalone; then AT_CHECK([test `wc -l < db` -eq 6]) fi AT_CHECK([ovsdb-client dump unix:db.sock ordinals | uuidfilt], [0], [dnl ordinals table _uuid number ------------------------------------ ------ <0> 0 <1> 1 <2> 2 <3> 3 <4> 4 <5> 5 <6> 6 ]) dnl Now kill and restart the database server to ensure that the data is dnl correct on disk as well as in memory. OVSDB_SERVER_SHUTDOWN AT_CHECK([[ovsdb-server -vfile -vvlog:off -vconsole:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db]], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:db.sock ordinals | uuidfilt], [0], [dnl ordinals table _uuid number ------------------------------------ ------ <0> 0 <1> 1 <2> 2 <3> 3 <4> 4 <5> 5 <6> 6 ]) dnl Make sure that "db" is still a symlink to dir/db instead of getting dnl replaced by a regular file, ditto for .db.~lock~. if test "$IS_WIN32" = "no"; then AT_CHECK([test -h db]) AT_CHECK([test -h .db.~lock~]) AT_CHECK([test -f dir/db]) AT_CHECK([test -f dir/.db.~lock~]) fi OVSDB_SERVER_SHUTDOWN } OVS_END_SHELL_HELPERS AT_SETUP([schema conversion online - standalone]) AT_KEYWORDS([ovsdb server convert needs-conversion standalone]) ovsdb_check_online_conversion standalone AT_CLEANUP AT_SETUP([schema conversion online - clustered]) AT_KEYWORDS([ovsdb server convert needs-conversion cluster]) ovsdb_check_online_conversion cluster AT_CLEANUP AT_SETUP([ovsdb-server combines updates on backlogged connections]) AT_KEYWORDS([ovsdb server]) on_exit 'kill `cat *.pid`' # The maximum socket receive buffer size is important for this test, which # tests behavior when the receive buffer overflows. if test -e /proc/sys/net/core/rmem_max; then # Linux rmem_max=`cat /proc/sys/net/core/rmem_max` elif rmem_max=`sysctl -n net.inet.tcp.recvbuf_max 2>/dev/null`; then : # FreeBSD, NetBSD else # Don't know how to get maximum socket receive buffer on this OS AT_SKIP_IF([:]) fi # Calculate the number of iterations we need to queue. Each of the # iterations we execute, by itself, yields a monitor update of about # 25 kB, so fill up that much space plus a few for luck. n_iterations=`expr $rmem_max / 25000 + 5` echo rmem_max=$rmem_max n_iterations=$n_iterations # If there's too much queuing skip the test to avoid timing out. AT_SKIP_IF([test $rmem_max -gt 1048576]) # Calculate the exact number of monitor updates expected for $n_iterations, # assuming no updates are combined. The "extra" update is for the initial # contents of the database. n_updates=`expr $n_iterations \* 3 + 1` # Start an ovsdb-server with the vswitchd schema. OVSDB_INIT([db]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db], [0], [ignore], [ignore]) # Executes a set of transactions that add a bridge with 100 ports, and # then deletes that bridge. This yields three monitor updates that # add up to about 25 kB in size. # # The update also increments a counter held in the database so that we can # verify that the overall effect of the transactions took effect (e.g. # monitor updates at the end weren't just dropped). We add an arbitrary # string to the counter to make grepping for it more reliable. counter=0 trigger_big_update () { counter=`expr $counter + 1` ovs-vsctl --no-wait -- set open_vswitch . system_version=xyzzy$counter ovs-vsctl --no-wait -- add-br br0 $add ovs-vsctl --no-wait -- del-br br0 } add_ports () { for j in `seq 1 100`; do printf " -- add-port br0 p%d" $j done } add=`add_ports` AT_CAPTURE_FILE([ovsdb-client.err]) AT_CAPTURE_FILE([ovsdb-client-nonblock.err]) # Start an ovsdb-client monitoring all changes to the database, # By default, it is non-blocking, and will get update message # for each ovsdb-server transaactions. AT_CHECK([ovsdb-client --detach --no-chdir --pidfile=nonblock.pid monitor ALL >ovsdb-client-nonblock.out 2>ovsdb-client-nonblock.err]) # Start an ovsdb-client monitoring all changes to the database, # make it block to force the buffers to fill up, and then execute # enough iterations that ovsdb-server starts combining updates. AT_CHECK([ovsdb-client --detach --no-chdir --pidfile monitor ALL >ovsdb-client.out 2>ovsdb-client.err]) AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/block]) for i in `seq 1 $n_iterations`; do echo "blocked update ($i of $n_iterations)" trigger_big_update $i done AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/unblock]) OVS_WAIT_UNTIL([grep "xyzzy$counter" ovsdb-client.out]) OVS_WAIT_UNTIL([grep "xyzzy$counter" ovsdb-client-nonblock.out]) OVS_APP_EXIT_AND_WAIT([ovsdb-client]) AT_CHECK([kill `cat nonblock.pid`]) # Count the number of updates in the ovsdb-client output, by counting # the number of changes to the Open_vSwitch table. (All of our # transactions modify the Open_vSwitch table.) It should be less than # $n_updates updates. # # Check that the counter is what we expect. logged_updates=`grep -c '^Open_vSwitch' ovsdb-client.out` logged_nonblock_updates=`grep -c '^Open_vSwitch' ovsdb-client-nonblock.out` echo "logged_nonblock_updates=$logged_nonblock_updates (expected less or equal to $n_updates)" echo "logged_updates=$logged_updates (expected less than $logged_nonblock_updates)" AT_CHECK([test $logged_nonblock_updates -le $n_updates]) AT_CHECK([test $logged_updates -lt $logged_nonblock_updates]) AT_CHECK_UNQUOTED([ovs-vsctl get open_vswitch . system_version], [0], [xyzzy$counter ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-server transaction history size]) AT_KEYWORDS([ovsdb server transaction]) on_exit 'kill `cat *.pid`' dnl Start an ovsdb-server with the clustered vswitchd schema. AT_CHECK([ovsdb-tool create-cluster db dnl $abs_top_srcdir/vswitchd/vswitch.ovsschema unix:s1.raft], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile dnl --log-file --remote=punix:db.sock db], [0], [ignore], [ignore]) AT_CHECK([ovs-vsctl --no-wait init]) dnl Create a bridge with N ports per transaction. Increase N every 4 dnl iterations. And then remove the bridges. By increasing the size of dnl transactions, ensuring that they take up a significant percentage of dnl the total database size, so the transaction history will not be able dnl to hold all of them. dnl dnl The test verifies that the number of atoms in the transaction history dnl is always less than the number of atoms in the database, except for dnl a case where there is only one transaction in a history. get_memory_value () { n=$(ovs-appctl -t ovsdb-server memory/show dnl | tr ' ' '\n' | grep "^$1:" | cut -d ':' -f 2) if test X"$n" = "X"; then n=0 fi echo $n } check_atoms () { if test $(get_memory_value txn-history) -eq 1; then return; fi n_db_atoms=$(get_memory_value atoms) n_txn_history_atoms=$(get_memory_value txn-history-atoms) echo "n_db_atoms: $n_db_atoms" echo "n_txn_history_atoms: $n_txn_history_atoms" AT_CHECK([test $n_txn_history_atoms -le $n_db_atoms]) } add_ports () { for j in $(seq 1 $2); do printf " -- add-port br$1 p$1-%d" $j done } initial_db_atoms=$(get_memory_value atoms) for i in $(seq 1 100); do cmd=$(add_ports $i $(($i / 4 + 1))) AT_CHECK([ovs-vsctl --no-wait add-br br$i $cmd]) check_atoms done for i in $(seq 1 100); do AT_CHECK([ovs-vsctl --no-wait del-br br$i]) check_atoms done dnl After removing all the bridges, the number of atoms in the database dnl should return to its initial value. AT_CHECK([test $(get_memory_value atoms) -eq $initial_db_atoms]) dnl Add a few more resources. for i in $(seq 1 10); do cmd=$(add_ports $i $(($i / 4 + 1))) AT_CHECK([ovs-vsctl --no-wait add-br br$i $cmd]) done check_atoms db_atoms_before_conversion=$(get_memory_value atoms) dnl Trigger online conversion. AT_CHECK([ovsdb-client convert $abs_top_srcdir/vswitchd/vswitch.ovsschema], [0], [ignore], [ignore]) dnl Check that conversion didn't change the number of atoms and the history dnl still has a reasonable size. check_atoms AT_CHECK([test $(get_memory_value atoms) -eq $db_atoms_before_conversion]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_BANNER([OVSDB -- ovsdb-server transactions (SSL/TLS IPv4 sockets)]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server positive ssl tls $5]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) $2 > schema PKIDIR=$abs_top_builddir/tests AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server transactions (SSL/TLS IPv6 sockets)]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server positive ssl6 ssl tls $5]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) AT_SKIP_IF([test $HAVE_IPV6 = no]) $2 > schema PKIDIR=$abs_top_builddir/tests on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:[[::1]] db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:[[::1]]:$SSL_PORT 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) ONE_EXECUTION_EXAMPLE AT_BANNER([OVSDB -- ovsdb-server transactions (TCP IPv4 sockets)]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server positive tcp $5]) $2 > schema PKIDIR=$abs_top_builddir/tests on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --remote=ptcp:0:127.0.0.1 db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server transactions (TCP IPv6 sockets)]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA, starts an ovsdb-server on # that database, and runs each of the TRANSACTIONS (which should be a # quoted list of quoted strings) against it with ovsdb-client one at a # time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server positive tcp6 $5]) AT_SKIP_IF([test $HAVE_IPV6 = no]) $2 > schema PKIDIR=$abs_top_builddir/tests on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile --remote=ptcp:0:[[::1]] db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client transact tcp:[[::1]]:$TCP_PORT 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) ONE_EXECUTION_EXAMPLE AT_BANNER([OVSDB -- transactions on transient ovsdb-server]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA and runs each of the # TRANSACTIONS (which should be a quoted list of quoted strings) # against it with ovsdb-client one at a time. Each ovsdb-client # is run against a separately started ovsdb-server that executes # only that single transaction. (The idea is that this should # help to ferret out any differences between what ovsdb-server has # in memory and what actually gets committed to disk.) # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_KEYWORDS([ovsdb server positive transient $5]) $2 > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) m4_foreach([txn], [$3], [AT_DATA([txnfile], [ovsdb-client transact unix:socket 'txn' ]) AT_CHECK([ovsdb-server --remote=punix:socket db --run="sh txnfile"], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4], [ignore]) AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server relay]) # OVSDB_CHECK_EXECUTION_RELAY(MODEL, TITLE, SCHEMA, TRANSACTIONS, # OUTPUT, [KEYWORDS]) # # Creates a clustered or standalone (MODEL) database with the given SCHEMA # and starts an ovsdb-server on it. Also starts a daisy chain of # ovsdb-servers in relay mode where the first relay server is connected to # the main non-relay ovsdb-server. # # Runs each of the TRANSACTIONS (which should be a quoted list of # quoted strings) against one of relay servers in the middle with # ovsdb-client one at a time. The server executes read-only transactions # and forwards rest of them to the previous ovsdb-server in a chain. # The main ovsdb-server executes 'write' transactions. Transaction # reply with data updates propagates back through the chain to all # the servers and the client. # # main relay relay relay relay relay # server1 <-- server2 <-- server3 <-- server4 <-- server5 <-- server6 # ^ # | # ovsdb-client # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # Checks that the dump of all databases and transaction ids are the same. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION_RELAY], [AT_SETUP([$2 - relay - $1]) AT_KEYWORDS([ovsdb server tcp relay $6 $1]) n_servers=6 target=4 $3 > schema schema_name=`ovsdb-tool schema-name schema` on_exit 'kill `cat *.pid`' AT_CHECK([if test $1 = standalone; then ovsdb-tool create db1 schema else ovsdb-tool create-cluster db1 schema unix:s1.raft fi], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log dnl --pidfile --remote=punix:db1.sock db1 ], [0], [ignore], [ignore]) for i in $(seq 2 ${n_servers}); do dnl Run every second relay with a config file. if test $(expr $i % 2) -eq 0; then echo "{ \"remotes\": { \"punix:db${i}.sock\": {} }, \"databases\": { \"${schema_name}\": { \"service-model\": \"relay\", \"source\": { \"unix:db$((i-1)).sock\": {} } } } }" > config${i}.json AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=${i}.pid \ --log-file=ovsdb-server$i.log \ --unixctl=unixctl${i} -vjsonrpc:file:dbg \ --config-file=config${i}.json ], [0], [ignore], [ignore]) else AT_CHECK([ovsdb-server --detach --no-chdir \ --log-file=ovsdb-server$i.log \ --pidfile=${i}.pid --remote=punix:db${i}.sock \ --unixctl=unixctl${i} -vjsonrpc:file:dbg \ relay:${schema_name}:unix:db$((i-1)).sock ], [0], [ignore], [ignore]) fi done m4_foreach([txn], [$4], [AT_CHECK([ovsdb-client transact unix:db${target}.sock 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$5], [ignore]) AT_CHECK([ovsdb-client dump unix:db1.sock], [0], [stdout], [ignore]) for i in $(seq 2 ${n_servers}); do OVS_WAIT_UNTIL([ovsdb-client dump unix:db${i}.sock > dump${i}; dnl diff stdout dump${i}]) done dnl Check that transaction ids in notifications are the same on all relays. last_id_pattern='s/\(.*"monid","[[a-z]]*".,"\)\(.*\)\(",{".*\)/\2/' AT_CHECK([grep 'received notification, method="update3"' ovsdb-server2.log dnl | sed $last_id_pattern > txn_ids2]) if test $1 = clustered; then dnl Check that transaction ids are not all zeroes in clustered mode. AT_CHECK([! grep -q "00000000-0000-0000-0000-000000000000" txn_ids2]) fi for i in $(seq 3 ${n_servers}); do AT_CHECK([grep 'received notification, method="update3"' ovsdb-server$i.log dnl | sed $last_id_pattern > txn_ids$i]) AT_CHECK([diff txn_ids2 txn_ids$i]) done OVSDB_SERVER_SHUTDOWN for i in $(seq 2 ${n_servers}); do OVSDB_SERVER_SHUTDOWN_N([$i]) done AT_CLEANUP]) m4_define([OVSDB_CHECK_EXECUTION], [OVSDB_CHECK_EXECUTION_RELAY(standalone, $@) OVSDB_CHECK_EXECUTION_RELAY(clustered, $@)]) EXECUTION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server replication]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates three databases with the given SCHEMA, and starts an ovsdb-server on # each database. # Runs each of the TRANSACTIONS (which should be a quoted list of # quoted strings) against one of the servers with ovsdb-client one at a # time. The server replicates its database to the other two ovsdb-servers, # one of which is configured via command line and the other via --config-file. # # Checks that the dump of all databases are the same. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server tcp replication $5]) $2 > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db3 schema], [0], [stdout], [ignore]) on_exit 'kill $(cat *.pid)' AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server1.log \ --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server2.log \ --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 \ --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) AT_DATA([config3.json], [ { "remotes": { "punix:db3.sock": {} }, "databases": { "db3": { "service-model": "active-backup", "backup": true, "source": { "unix:db.sock": {} } } } } ]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server3.log \ --pidfile=3.pid --unixctl=unixctl3 --config-file=config3.json], [0], [ignore], [ignore]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-client transact 'txn'], [0], [stdout], [ignore]) ]) AT_CHECK([ovsdb-client dump], [0], [stdout], [ignore]) OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock > dump2; diff -u stdout dump2]) OVS_WAIT_UNTIL([ ovsdb-client dump unix:db3.sock > dump3; diff -u stdout dump3]) OVSDB_SERVER_SHUTDOWN OVSDB_SERVER_SHUTDOWN2 OVSDB_SERVER_SHUTDOWN_N([3]) AT_CLEANUP]) EXECUTION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server replication table-exclusion]) # OVSDB_CHECK_REPLICATION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates three databases with the given SCHEMA, and starts an # ovsdb-server on each database. # Runs each of the TRANSACTIONS (which should be a quoted list of # quoted strings) against one of the servers with ovsdb-client one at a # time. The server replicates its database to the other two ovsdb-servers, # one of which is configured via command line and the other via --config-file. # # Checks that the difference between the dump of the first and the other two # databases is OUTPUT, but UUIDs in the output are replaced by markers of the # form where N is a number. The first unique UUID is replaced by <0>, # the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # Also checks that the dumps of the second and third databases are the same. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_REPLICATION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb server tcp replication table-exclusion]) AT_SKIP_IF([test $DIFF_SUPPORTS_NORMAL_FORMAT = no]) $2 > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db3 schema], [0], [stdout], [ignore]) on_exit 'kill $(cat *.pid)' AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server1.log \ --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server2.log \ --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 \ --sync-from=unix:db.sock --sync-exclude-tables=mydb:b db2], [0], [ignore], [ignore]) AT_DATA([config3.json], [ { "remotes": { "punix:db3.sock": {} }, "databases": { "db3": { "service-model": "active-backup", "backup": true, "source": { "unix:db.sock": {} }, "exclude-tables": [["b"]] } } } ]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server3.log \ --pidfile=3.pid --unixctl=unixctl3 --config-file=config3.json], [0], [ignore], [ignore]) m4_foreach([txn], [$3], [AT_CHECK([ ovsdb-client transact 'txn' ], [0], [stdout], [ignore]) ]) AT_CHECK([ovsdb-client dump], [0], [stdout], [ignore]) cat stdout > dump1 OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock | grep one ]) AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout], [ignore]) cat stdout > dump2 OVS_WAIT_UNTIL([ ovsdb-client dump unix:db3.sock | grep one ]) AT_CHECK([ovsdb-client dump unix:db3.sock], [0], [stdout], [ignore]) cat stdout > dump3 AT_CHECK([diff -u dump2 dump3]) AT_CHECK([diff dump1 dump2], [1], [stdout], [ignore]) cat stdout > output AT_CHECK([uuidfilt output], [0], [$4], [ignore]) OVSDB_SERVER_SHUTDOWN OVSDB_SERVER_SHUTDOWN2 OVSDB_SERVER_SHUTDOWN_N([3]) AT_CLEANUP]) REPLICATION_EXAMPLES AT_BANNER([OVSDB -- ovsdb-server replication runtime management commands]) #ovsdb-server/get-active-ovsdb-server command AT_SETUP([ovsdb-server/get-active-ovsdb-server]) AT_KEYWORDS([ovsdb server replication get-active]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --sync-from=tcp:127.0.0.1:9999 db], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/get-active-ovsdb-server], [0], [tcp:127.0.0.1:9999 ]) AT_CLEANUP #*ovsdb-server/set-active-ovsdb-server command AT_SETUP([ovsdb-server/set-active-ovsdb-server]) AT_KEYWORDS([ovsdb server replication set-active]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile db], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/set-active-ovsdb-server tcp:127.0.0.1:9999]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/get-active-ovsdb-server], [0], [tcp:127.0.0.1:9999 ]) AT_CLEANUP #ovsdb-server/get-sync-exclude-tables command AT_SETUP([ovsdb-server/get-sync-exclude-tables]) AT_KEYWORDS([ovsdb server replication get-exclude-tables]) ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --sync-exclude-tables=mydb:db1,mydb:db2 db], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/get-sync-exclude-tables], [0], [mydb:db1,mydb:db2 ]) AT_CLEANUP #ovsdb-server/set-sync-exclude-tables command AT_SETUP([ovsdb-server/set-sync-exclude-tables]) on_exit 'kill `cat *.pid`' AT_KEYWORDS([ovsdb server replication set-exclude-tables]) AT_SKIP_IF([test $DIFF_SUPPORTS_NORMAL_FORMAT = no]) replication_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/set-sync-exclude-tables mydb:b], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [stdout], [ignore]) AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout], [ignore]) cat stdout > dump1 OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock | grep zero ]) AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout], [ignore]) cat stdout > dump2 AT_CHECK([diff dump1 dump2], [1], [stdout], [ignore]) cat stdout > output AT_CHECK([uuidfilt output], [0], [7,9c7,8 < _uuid name number < ------------------------------------ ---- ------ < <0> one 1 --- > _uuid name number > ----- ---- ------ ]) OVSDB_SERVER_SHUTDOWN OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP #ovsdb-server/connect-active-ovsdb-server AT_SETUP([ovsdb-server/connect-active-server]) on_exit 'kill `cat *.pid`' AT_KEYWORDS([ovsdb server replication connect-active-server]) replication_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir \ --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir \ --log-file=ovsdb-server2.log --pidfile=2.pid \ --remote=punix:db2.sock --unixctl=unixctl2 db2], [0], [ignore], [ignore]) dnl Try to connect without specifying the active server. AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/connect-active-ovsdb-server], [0], [Unable to connect: active server is not specified. ], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/set-active-ovsdb-server unix:db.sock], [0], [stdout], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/connect-active-ovsdb-server], [0], [stdout], [ignore]) AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]'], [0], [stdout], [ignore]) AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout], [ignore]) cat stdout > dump1 OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock | grep zero ]) AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout], [ignore]) cat stdout > dump2 AT_CHECK([diff dump1 dump2], [0], [], [ignore]) OVSDB_SERVER_SHUTDOWN OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP #ovsdb-server/disconnect-active-server command AT_SETUP([ovsdb-server/disconnect-active-server]) on_exit 'kill `cat *.pid`' AT_KEYWORDS([ovsdb server replication disconnect-active-server]) AT_SKIP_IF([test $DIFF_SUPPORTS_NORMAL_FORMAT = no]) replication_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock db1], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl=unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]'], [0], [stdout], [ignore]) dnl Make sure the transaction shows up in db2. This also tests the back up server dnl can be read. OVS_WAIT_UNTIL([ovsdb-client dump unix:db2.sock | grep zero]) dnl The backup server does not accept any write transaction AT_CHECK([ovsdb-client transact unix:db2.sock \ '[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [[[{"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}]] ]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/disconnect-active-ovsdb-server], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [stdout], [ignore]) AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout], [ignore]) cat stdout > dump1 sleep 1 AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout], [ignore]) cat stdout > dump2 AT_CHECK([diff dump1 dump2], [1], [stdout], [ignore]) cat stdout > output AT_CHECK([uuidfilt output], [0], [7,9c7,8 < _uuid name number < ------------------------------------ ---- ------ < <0> one 1 --- > _uuid name number > ----- ---- ------ ], [ignore]) dnl The backup server now become active, and can accept write transactions. AT_CHECK([ovsdb-client transact unix:db2.sock \ '[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [stdout], [ignore]) AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout]) cat stdout > output AT_CHECK([uuidfilt output], [0], [a table _uuid name number ------------------------------------ ---- ------ <0> zero 0 b table _uuid name number ------------------------------------ ---- ------ <1> one 1 ]) OVSDB_SERVER_SHUTDOWN OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP #ovsdb-server/active-backup-role-switching AT_SETUP([ovsdb-server/active-backup-role-switching]) AT_KEYWORDS([ovsdb server replication active-backup-switching]) replication_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) dnl Add some data to both DBs AT_CHECK([ovsdb-tool transact db1 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool transact db2 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) dnl Start both 'db1' and 'db2' in backup mode. Let them backup from each dnl other. This is not an supported operation state, but to simulate a start dnl up condition where an HA manger can select which one to be an active dnl server soon after. on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server1.log --pidfile --remote=punix:db.sock --unixctl="`pwd`"/unixctl db1 --sync-from=unix:db2.sock --active ], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server2.log --pidfile=2.pid --remote=punix:db2.sock --unixctl="`pwd`"/unixctl2 --sync-from=unix:db.sock db2], [0], [ignore], [ignore]) dnl dnl make sure both servers reached the replication state OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep replicating]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status |grep replicating]) dnl Switch the 'db1' to active AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/disconnect-active-ovsdb-server]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status], [0], [dnl database: mydb state: active ]) dnl Issue a transaction to 'db1' AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]'], [0], [ignore]) dnl It should be replicated to 'db2' OVS_WAIT_UNTIL([ovsdb-client dump unix:db2.sock | grep zero]) dnl Flip the role of 'db1' and 'db2'. 'db1' becomes backup, and db2 becomes active AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/disconnect-active-ovsdb-server]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) dnl Verify the change happend OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep replicating]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status], [0], [dnl database: mydb state: active ]) dnl Issue an transaction to 'db2' which is now active. AT_CHECK([ovsdb-client transact unix:db2.sock \ '[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [ignore]) dnl The transaction should be replicated to 'db1' OVS_WAIT_UNTIL([ovsdb-client dump unix:db.sock | grep one]) dnl Both servers should have the same content. AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) cat stdout > dump1 AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout]) cat stdout > dump2 AT_CHECK([diff dump1 dump2]) dnl OVSDB_SERVER_SHUTDOWN dnl OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP AT_SETUP([ovsdb-server/active-backup-role-switching with config file]) AT_KEYWORDS([ovsdb server replication active-backup-switching config-file]) replication_schema > schema AT_CHECK([ovsdb-tool create db1 schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [stdout], [ignore]) dnl Add some data to both DBs. AT_CHECK([ovsdb-tool transact db1 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool transact db2 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) dnl Start both 'db1' and 'db2' in backup mode. Let them backup from each dnl other. This is not a supported operation state, but to simulate a start dnl up condition where an HA manger can select which one to be an active dnl server soon after. on_exit 'kill $(cat *.pid)' AT_DATA([config1.json], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db1": { "service-model": "active-backup", "backup": true, "source": { "unix:db2.sock": {} } } } } ]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server1.log \ --pidfile=1.pid --unixctl=unixctl1 --config-file=config1.json], [0], [ignore], [ignore]) AT_DATA([config2.json], [ { "remotes": { "punix:db2.sock": {} }, "databases": { "db2": { "service-model": "active-backup", "backup": true, "source": { "unix:db.sock": {} } } } } ]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir --log-file=ovsdb-server2.log \ --pidfile=2.pid --unixctl=unixctl2 --config-file=config2.json], [0], [ignore], [ignore]) dnl Make sure both servers reached the replication state. OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/unixctl1 ovsdb-server/sync-status | grep replicating]) OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/unixctl2 ovsdb-server/sync-status | grep replicating]) dnl Switch the 'db1' to active. AT_CHECK([sed -i'back' 's/"backup": true/"backup": false/' config1.json]) AT_CHECK([ovs-appctl -t $(pwd)/unixctl1 ovsdb-server/reload]) AT_CHECK([ovs-appctl -t $(pwd)/unixctl1 ovsdb-server/sync-status], [0], [dnl database: mydb state: active ]) dnl Issue a transaction to 'db1'. AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 0, "name": "zero"}}]]'], [0], [ignore]) dnl It should be replicated to 'db2'. OVS_WAIT_UNTIL([ovsdb-client dump unix:db2.sock | grep zero]) dnl Issue a transaction to 'db2', it should fail. AT_CHECK([ovsdb-client transact unix:db2.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 1, "name": "one"}}]]'], [0], [dnl [[{"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}]] ]) dnl Flip the role of 'db1' and 'db2'. 'db1' becomes backup, and 'db2' becomes active. AT_CHECK([sed -i'back' 's/"backup": true/"backup": false/' config2.json]) AT_CHECK([ovs-appctl -t $(pwd)/unixctl2 ovsdb-server/reload]) AT_CHECK([sed -i'back' 's/"backup": false/"backup": true/' config1.json]) AT_CHECK([ovs-appctl -t $(pwd)/unixctl1 ovsdb-server/reload]) dnl Verify the change happend. OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/unixctl1 ovsdb-server/sync-status | grep replicating]) AT_CHECK([ovs-appctl -t $(pwd)/unixctl2 ovsdb-server/sync-status], [0], [dnl database: mydb state: active ]) dnl Issue a transaction to 'db2' which is now active. AT_CHECK([ovsdb-client transact unix:db2.sock \ '[["mydb", {"op": "insert", "table": "b", "row": {"number": 1, "name": "one"}}]]'], [0], [ignore]) dnl The transaction should be replicated to 'db1'. OVS_WAIT_UNTIL([ovsdb-client dump unix:db.sock | grep one]) dnl Issue a transaction to 'db1', it should fail. AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 2, "name": "two"}}]]'], [0], [dnl [[{"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}]] ]) dnl Both servers should have the same content. AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) cat stdout > dump1 AT_CHECK([ovsdb-client dump unix:db2.sock], [0], [stdout]) cat stdout > dump2 AT_CHECK([diff -u dump1 dump2]) OVSDB_SERVER_SHUTDOWN_N([1]) OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP #ovsdb-server prevent self replicating AT_SETUP([ovsdb-server prevent self replicating]) AT_KEYWORDS([ovsdb server replication]) replication_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) dnl Add some data to both DBs AT_CHECK([ovsdb-tool transact db \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) dnl Start 'db', then try to be a back up server of itself. on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server.log --pidfile --remote=punix:db.sock --unixctl="`pwd`"/unixctl db --sync-from=unix:db.sock --active ], [0], [ignore], [ignore]) dnl Save the current content AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) cp stdout dump1 AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) dnl Check that self replicating is blocked. AT_CHECK([grep "Self replicating is not allowed" ovsdb-server.log], [0], [stdout]) dnl Check current DB content is preserved. AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) cat stdout > dump2 AT_CHECK([diff dump1 dump2]) AT_CLEANUP AT_SETUP([ovsdb-server/read-only db:ptcp connection]) on_exit 'kill `cat *.pid`' AT_KEYWORDS([ovsdb server read-only]) AT_DATA([schema], [[{"name": "mydb", "tables": { "Root": { "columns": { "managers": { "type": { "key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}}}, "Manager": { "columns": { "target": { "type": "string"}, "read_only": { "type": { "key": "boolean", "min": 0, "max": 1}}, "is_connected": { "type": { "key": "boolean", "min": 0, "max": 1}}}}, "ordinals": { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, "indexes": [["number"]]} }, "version": "5.1.3", "cksum": "12345678 9" } ]]) AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) AT_CHECK( [[ovsdb-tool transact db \ '["mydb", {"op": "insert", "table": "Root", "row": { "managers": ["set", [["named-uuid", "x"]]]}}, {"op": "insert", "table": "Manager", "uuid-name": "x", "row": {"target": "ptcp:0:127.0.0.1", "read_only": true}}]']], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=db:mydb,Root,managers db], [0], [ignore], [ignore]) PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) AT_CHECK([ovsdb-client get-schema-version tcp:127.0.0.1:$TCP_PORT mydb], [0], [5.1.3 ]) AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT \ ['["mydb", {"op": "insert", "table": "ordinals", "row": {"name": "two", "number": '2'}} ]']], [0], [stdout], [ignore]) cat stdout >> output AT_CHECK([uuidfilt output], [0], [[[{"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}]] ], [ignore]) OVSDB_SERVER_SHUTDOWN([" /No status column present in the Manager table/d "]) AT_CLEANUP AT_SETUP([ovsdb-server replication with schema mismatch]) AT_KEYWORDS([ovsdb server replication]) replication_schema > subset_schema replication_schema_v2 > superset_schema AT_CHECK([ovsdb-tool create db1 subset_schema], [0], [stdout], [ignore]) AT_CHECK([ovsdb-tool create db2 superset_schema], [0], [stdout], [ignore]) dnl Add some data to both DBs AT_CHECK([ovsdb-tool transact db1 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool transact db2 \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 10, "name": "ten"}}]]'], [0], [ignore], [ignore]) dnl Start both 'db1' and 'db2'. on_exit 'kill `cat *.pid`' AT_CHECK([ovsdb-server -vfile --detach --no-chdir \ --log-file=ovsdb-server1.log --pidfile \ --remote=punix:db.sock \ --unixctl="$(pwd)"/unixctl db1 --active ], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server -vfile --detach --no-chdir \ --log-file=ovsdb-server2.log --pidfile=2.pid \ --remote=punix:db2.sock --unixctl="$(pwd)"/unixctl2 db2], [0], [ignore], [ignore]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep active]) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status |grep active]) AT_CHECK([ovsdb-client dump unix:db.sock a number name], 0, [dnl a table name number ---- ------ nine 9 ]) AT_CHECK([ovsdb-client dump unix:db2.sock a number name], 0, [dnl a table name number ---- ------ ten 10 ]) # Replicate db1 from db2. It should fail since db2 schema # doesn't match with db1 and has additional tables/columns. AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/set-active-ovsdb-server unix:db2.sock]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) OVS_WAIT_UNTIL( [test 1 = `cat ovsdb-server1.log | grep "Schema version mismatch, checking if mydb can still be replicated or not" | wc -l]` ) OVS_WAIT_UNTIL( [test 1 = `cat ovsdb-server1.log | grep "mydb cannot be replicated" | wc -l]` ) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep active]) # Replicate db2 from db1. This should be successful. AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/disconnect-active-ovsdb-server]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/set-active-ovsdb-server unix:db.sock]) AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/connect-active-ovsdb-server]) OVS_WAIT_UNTIL( [test 1 = `cat ovsdb-server2.log | grep "Schema version mismatch, checking if mydb can still be replicated or not" | wc -l]` ) OVS_WAIT_UNTIL( [test 1 = `cat ovsdb-server2.log | grep "mydb can be replicated" | wc -l]` ) OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status |grep replicating]) AT_CHECK([ovsdb-client dump unix:db.sock a number name], 0, [dnl a table name number ---- ------ nine 9 ]) AT_CHECK([ovsdb-client dump unix:db2.sock a number name], 0, [dnl a table name number ---- ------ nine 9 ]) AT_CHECK([ovsdb-client transact unix:db.sock \ '[["mydb", {"op": "insert", "table": "a", "row": {"number": 6, "name": "six"}}]]'], [0], [ignore], [ignore]) OVS_WAIT_UNTIL([test 1 = `ovsdb-client dump unix:db2.sock a number name | grep six | wc -l`]) AT_CHECK([ ovsdb-client dump unix:db2.sock a number name], 0, [dnl a table name number ---- ------ nine 9 six 6 ]) AT_CLEANUP AT_BANNER([OVSDB -- ovsdb-server stream record/replay]) AT_SETUP([ovsdb-server record/replay]) AT_KEYWORDS([ovsdb server record replay]) on_exit 'kill `cat *.pid`' ordinal_schema > schema AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) dnl Create a directory for replay files. AT_CHECK([mkdir replay_dir]) dnl Make a copy of a database for later replay. AT_CHECK([cp db ./replay_dir/db.copy]) dnl Starting a dummy server only to reserve some tcp port. AT_CHECK([cp db db.tmp]) AT_CHECK([ovsdb-server -vfile -vvlog:off --log-file=listener.log dnl --detach --no-chdir dnl --pidfile=2.pid --unixctl=unixctl2 dnl --remote=ptcp:0:127.0.0.1 dnl db.tmp], [0], [stdout], [stderr]) PARSE_LISTENING_PORT([listener.log], [BAD_TCP_PORT]) dnl Start ovsdb-server with recording enabled. dnl Trying to start a tcp session on already used port to record the error. AT_CHECK([ovsdb-server --record=./replay_dir dnl -vfile -vvlog:off -vjsonrpc:file:dbg --log-file=1.log dnl --detach --no-chdir --pidfile dnl --remote=punix:db.sock dnl --remote=ptcp:$BAD_TCP_PORT:127.0.0.1 dnl --remote=ptcp:0:127.0.0.1 dnl db], [0], [stdout], [stderr]) CHECK_DBS([ordinals ]) PARSE_LISTENING_PORT([1.log], [TCP_PORT]) dnl Start a monitor on the 'ordinals' db to check recording of this kind dnl of messages. AT_CHECK([ovsdb-client -vfile -vvlog:off --detach --no-chdir dnl --pidfile=monitor.pid --log-file=monitor.log dnl --db-change-aware --no-headings dnl monitor tcp:127.0.0.1:$TCP_PORT dnl ordinals ordinals number name dnl > monitor.stdout 2> monitor.stderr]) OVS_WAIT_UNTIL([test -e monitor.pid]) dnl Do a bunch of random transactions. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair if test "$2" -eq "5"; then # killing the monitor to check if this correctly recorded. kill -9 $(cat monitor.pid) fi ovsdb-client --db-change-aware transact unix:db.sock ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' ovsdb-client transact unix:db.sock ' ["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", '$2']]}, {"op": "comment", "comment": "delete row for '"$2"'"}]' ovsdb-client transact unix:db.sock ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add back row for '"$pair"'"}]' done]], [0], [stdout]) AT_CHECK([ovsdb-client dump unix:db.sock ordinals | uuidfilt], 0, [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) AT_CHECK([uuidfilt monitor.stdout | sed '/^$/d'], [0], [dnl <0> insert 0 zero <0> delete 0 zero <1> insert 0 zero <2> insert 1 one <2> delete 1 one <3> insert 1 one <4> insert 2 two <4> delete 2 two <5> insert 2 two <6> insert 3 three <6> delete 3 three <7> insert 3 three <8> insert 4 four <8> delete 4 four <9> insert 4 four ]) dnl glibc returns 'Address already in use' error message dnl musl returns 'Address in use' error message OVSDB_SERVER_SHUTDOWN(["/Address \(already \)*in use/d"]) OVSDB_SERVER_SHUTDOWN2(["/Address \(already \)*in use/d"]) dnl Starting a replay. AT_CHECK([ovsdb-server --replay=./replay_dir dnl -vfile -vvlog:off -vjsonrpc:file:dbg --log-file=2.log dnl --detach --no-chdir --pidfile dnl --remote=punix:db.sock dnl --remote=ptcp:$BAD_TCP_PORT:127.0.0.1 dnl --remote=ptcp:0:127.0.0.1 dnl ./replay_dir/db.copy], [0], [stdout], [stderr]) dnl Waiting for process termination. Process should exit after correct dnl processing of the 'exit' unixctl command from the recorded session. OVS_WAIT_WHILE([test -e ovsdb-server.pid]) dnl Stripping out timestamps from database files. Also clearing record dnl hashes in database files, since dates inside are different. m4_define([CLEAN_DB_FILE], [sed 's/\(OVSDB JSON [[0-9]]*\).*$/\1/g' $1 | dnl sed 's/"_date":[[0-9]]*/"_date":/g' > $2]) CLEAN_DB_FILE([db], [db.clear]) CLEAN_DB_FILE([./replay_dir/db.copy], [./replay_dir/db.copy.clear]) dnl Stripping out timestamps, PIDs and poll_loop warnings from the log. dnl Also stripping socket_util errors as sockets are not used in replay. m4_define([CLEAN_LOG_FILE], [sed 's/[[0-9\-]]*T[[0-9:\.]]*Z|[[0-9]]*\(|.*$\)/\1/g' $1 | dnl sed '/|poll_loop|/d' | dnl sed '/|socket_util|/d' | dnl sed '/|cooperative_multitasking|DBG|/d' | dnl sed 's/[[0-9]]*\.ctl/\.ctl/g'> $2]) CLEAN_LOG_FILE([1.log], [1.log.clear]) CLEAN_LOG_FILE([2.log], [2.log.clear]) dnl Checking that databases and logs are equal. AT_CHECK([diff db.clear ./replay_dir/db.copy.clear]) AT_CHECK([diff -u 1.log.clear 2.log.clear]) AT_CLEANUP AT_BANNER([OVSDB -- ovsdb-server configuration file]) dnl TEST_CONFIG_FILE([NAME], [config], [EXIT_CODE], [FAILURE_STRINGS]) dnl dnl Tries the config as a data for --config-file, checks the EXIT_CODE dnl of the ovsdb-server and checks the stderr for FAILURE_STRINGS. dnl NAME is added to the test name and keywords. m4_define([TEST_CONFIG_FILE], [ AT_SETUP([ovsdb-server config-file - $1]) AT_KEYWORDS([ovsdb server config-file $1]) on_exit 'kill $(cat *.pid)' echo '$2' > config.json AT_CAPTURE_FILE([config.json]) ordinal_schema > schema constraint_schema > schema2 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create db2 schema], [0], [ignore], [ignore]) AT_CHECK([ovsdb-tool create-cluster db_cluster schema2 unix:s1.raft], [0], [ignore], [ignore]) AT_CHECK([ovsdb-server -vfile -vPATTERN:console:'%p|%m' -vvlog:off \ --log-file --detach --no-chdir --pidfile \ --config-file=config.json], [$3], [ignore], [stderr]) m4_if([$4], [], [], [ AT_CHECK([cat stderr | grep -v -E 'INFO|DBG' \ | grep -v 'failed to load configuration from' \ | sed -e "/duplicate database name/ s/'db'/'db2'/" \ > warnings]) AT_CHECK([cat warnings], [0], [m4_if([$3], [0], [$4], [$4 ovsdb-server: server configuration failed ])])]) m4_if([$3$4], [0], [OVSDB_SERVER_SHUTDOWN]) AT_CLEANUP ]) TEST_CONFIG_FILE([simple], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": null, "db_cluster": {} } } ], [0]) TEST_CONFIG_FILE([standalone], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "standalone" } } } ], [0]) TEST_CONFIG_FILE([clustered], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db_cluster": { "service-model": "clustered" } } } ], [0]) TEST_CONFIG_FILE([unknown service model], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "not-a-service-model" } } } ], [1], [dnl WARN|Unrecognized database service model: 'not-a-service-model' WARN|syntax "{"service-model":"not-a-service-model"}": syntax error:dnl Parsing database db failed: 'not-a-service-model' is not a valid service model WARN|config: failed to parse 'databases']) TEST_CONFIG_FILE([same schema], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": null, "db2": {} } } ], [1], [dnl WARN|failed to open database 'db2': ovsdb error: ordinals: duplicate database name WARN|failed to configure databases]) TEST_CONFIG_FILE([model mismatch], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "clustered" } } } ], [1], [dnl WARN|failed to open database 'db': ovsdb error: db: database is standalone and not clustered WARN|failed to configure databases]) TEST_CONFIG_FILE([model mismatch clustered], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db_cluster": { "service-model": "standalone" } } } ], [1], [dnl WARN|failed to open database 'db_cluster': ovsdb error: db_cluster: database is clustered and not standalone WARN|failed to configure databases]) TEST_CONFIG_FILE([relay], [ { "remotes": { "punix:db.sock": {} }, "databases": { "RelaySchema": { "service-model": "relay", "source": { "unix:db2.sock": {} } } } } ], [0]) TEST_CONFIG_FILE([relay without source], [ { "remotes": { "punix:db.sock": {} }, "databases": { "RelaySchema": { "service-model": "relay" } } } ], [1], [dnl WARN|syntax "{"service-model":"relay"}": syntax error: Parsing database RelaySchema failed:dnl Required 'source' member is missing. WARN|config: failed to parse 'databases']) TEST_CONFIG_FILE([relay with options], [ { "remotes": { "punix:db.sock": {} }, "databases": { "RelaySchema": { "service-model": "relay", "source": { "punix:db2.sock": { "inactivity-probe": 10000, "max-backoff": 8000, "dscp": 42 } } } } } ], [0]) TEST_CONFIG_FILE([relay with unrelated options], [ { "remotes": { "punix:db.sock": {} }, "databases": { "RelaySchema": { "service-model": "relay", "source": { "punix:db2.sock": { "inactivity-probe": 10000, "max-backoff": 8000, "dscp": 42, "role": "My-RBAC-role" } } } } } ], [0], [dnl WARN|syntax "{"dscp":42,"inactivity-probe":10000,"max-backoff":8000,"role":"My-RBAC-role"}":dnl syntax error: Parsing JSON-RPC options failed: Member 'role' is present but not allowed here. ]) TEST_CONFIG_FILE([unknown config], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "unknnown": "unknown" } } } ], [1], [dnl WARN|syntax "{"unknnown":"unknown"}": syntax error: Parsing database db failed:dnl Member 'unknnown' is present but not allowed here. WARN|config: failed to parse 'databases']) TEST_CONFIG_FILE([active-backup active], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "active-backup", "backup": false } } } ], [0]) TEST_CONFIG_FILE([active-backup backup], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "active-backup", "backup": true, "source": { "punix:db2.sock": { "inactivity-probe": 100000, "max-backoff": 16000, "dscp": 42 } } } } } ], [0]) TEST_CONFIG_FILE([active-backup backup without source], [ { "remotes": { "punix:db.sock": {} }, "databases": { "db": { "service-model": "active-backup", "backup": true } } } ], [1], [dnl WARN|syntax "{"backup":true,"service-model":"active-backup"}": syntax error:dnl Parsing database db failed: Required 'source' member is missing. WARN|config: failed to parse 'databases']) TEST_CONFIG_FILE([syntax error], [ { "remotes": { "punix:db.sock": {}, }, "databases": { "db": {}, "db_cluster": {} } } ], [1], [dnl WARN|config: reading JSON failed (line 2, column 38, byte 41: syntax error parsing object expecting string)]) TEST_CONFIG_FILE([complex config], [ { "remotes": { "punix:db.sock": { "inactivity-probe": 0, "read-only": false }, "pssl:0:127.0.0.1": { "inactivity-probe": 5000, "max-backoff": 8000, "read-only": true, "role": "ovn-controller", "dscp": 48 }, "db:ordinals,ordinals,name": null }, "databases": { "db_cluster": { "service-model": "clustered" }, "OVN_Northbound": { "service-model": "relay", "source": { "unix:nb.sock": { "max-backoff": 3000, "inactivity-probe": 16000 } } }, "db": { "service-model": "active-backup", "backup": true, "source": { "unix:active.sock": { "max-backoff": 16000, "inactivity-probe": 180000 } }, "exclude-tables": [["IC_SB_Global", "Availability_Zone"]] } } } ], [0]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-table.at000066400000000000000000000064561514270232600226650ustar00rootroot00000000000000AT_BANNER([OVSDB -- tables]) OVSDB_CHECK_POSITIVE_CPY([non-root table with one column], [[parse-table mytable '{"columns": {"name": {"type": "string"}}}']], [[{"columns":{"name":{"type":"string"}}}]]) OVSDB_CHECK_POSITIVE_CPY([immutable table with one column], [[parse-table mytable \ '{"columns": {"name": {"type": "string"}}, "mutable": false}']], [[{"columns":{"name":{"type":"string"}},"mutable":false}]]) OVSDB_CHECK_POSITIVE_CPY([root table with one column], [[parse-table mytable \ '{"columns": {"name": {"type": "string"}}, "isRoot": true}']], [[{"columns":{"name":{"type":"string"}},"isRoot":true}]]) OVSDB_CHECK_POSITIVE_CPY([non-root table with default_is_root=true], [[parse-table mytable '{"columns": {"name": {"type": "string"}}}' true]], [[{"columns":{"name":{"type":"string"}},"isRoot":false}]]) OVSDB_CHECK_POSITIVE_CPY([root table with default_is_root=true], [[parse-table mytable \ '{"columns": {"name": {"type": "string"}}, "isRoot": true}' true]], [[{"columns":{"name":{"type":"string"}}}]]) OVSDB_CHECK_POSITIVE_CPY([table with maxRows of 2], [[parse-table mytable '{"columns": {"name": {"type": "string"}}, "maxRows": 2}']], [[{"columns":{"name":{"type":"string"}},"maxRows":2}]]) OVSDB_CHECK_POSITIVE_CPY([table with index], [[parse-table mytable '{"columns": {"a": {"type": "integer"}, "b": {"type": "string"}}, "indexes": [["b", "a"]]}']], [[{"columns":{"a":{"type":"integer"},"b":{"type":"string"}},"indexes":[["b","a"]]}]]) OVSDB_CHECK_NEGATIVE_CPY([table with syntax error in index], [[parse-table mytable '{"columns": {"a": {"type": "integer"}, "b": {"type": "string"}}, "indexes": [["b", "a"], [0]]}']], [[array of distinct column names expected]]) OVSDB_CHECK_NEGATIVE_CPY([table with empty index], [[parse-table mytable '{"columns": {"a": {"type": "integer"}, "b": {"type": "string"}}, "indexes": [[]]}']], [[index must have at least one column]]) OVSDB_CHECK_NEGATIVE_CPY([table with index of ephemeral column], [[parse-table mytable '{"columns": {"a": {"type": "integer", "ephemeral": true}, "b": {"type": "string"}}, "indexes": [["b", "a"]]}']], [[ephemeral columns (such as a) may not be indexed]]) OVSDB_CHECK_NEGATIVE_CPY([column names may not begin with _], [[parse-table mytable \ '{"columns": {"_column": {"type": "integer"}}}']], [[names beginning with "_" are reserved]], [table]) OVSDB_CHECK_NEGATIVE_CPY([table must have at least one column (1)], [[parse-table mytable '{}']], [[Parsing table schema for table mytable failed: Required 'columns' member is missing.]]) OVSDB_CHECK_NEGATIVE_CPY([table must have at least one column (2)], [[parse-table mytable '{"columns": {}}']], [[table must have at least one column]]) OVSDB_CHECK_NEGATIVE_CPY([table maxRows must be positive], [[parse-table mytable '{"columns": {"name": {"type": "string"}}, "maxRows": 0}']], [[syntax "{"columns":{"name":{"type":"string"}},"maxRows":0}": syntax error: maxRows must be at least 1]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-tool.at000066400000000000000000000500761514270232600225500ustar00rootroot00000000000000AT_BANNER([OVSDB -- ovsdb-tool]) # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS]) # # Creates a database with the given SCHEMA and runs each of the # TRANSACTIONS (which should be a quoted list of quoted strings) # against it with ovsdb-tool one at a time. # # Checks that the overall output is OUTPUT, but UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. # # TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. m4_define([OVSDB_CHECK_EXECUTION], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb file positive $5]) $2 > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) m4_foreach([txn], [$3], [AT_CHECK([ovsdb-tool transact db 'txn'], [0], [stdout], [ignore]) cat stdout >> output ]) AT_CHECK([uuidfilt output], [0], [$4]) AT_CLEANUP]) EXECUTION_EXAMPLES AT_SETUP([transaction comments]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) AT_CHECK([[ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "five", "number": 5}}, {"op": "comment", "comment": "add row for 5"}]']], [0], [stdout], [ignore]) AT_CHECK([uuidfilt stdout], [0], [[[{"uuid":["uuid","<0>"]},{}] ]]) AT_CHECK([grep "add row for 5" db], [0], [ignore]) AT_CLEANUP AT_SETUP([ovsdb-tool compact]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema dnl Make sure that "ovsdb-tool create" works with a dangling symlink, dnl creating the target of the symlink rather than replacing the symlink dnl with a regular file, and that the lockfile gets created relative to dnl the symlink's target. mkdir dir : > dir/.db.~lock~ if test "$IS_WIN32" = "no"; then ln -s dir/db db AT_SKIP_IF([test ! -h db]) fi AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) if test "$IS_WIN32" = "no"; then AT_CHECK([test ! -e .db.~lock]) AT_CHECK([test -h db]) AT_CHECK([test -f dir/db]) fi dnl Do a bunch of random transactions that put crap in the database log. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' ovsdb-tool transact db ' ["ordinals", {"op": "delete", "table": "ordinals", "where": [["number", "==", '$2']]}, {"op": "comment", "comment": "delete row for '"$2"'"}]' ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add back row for '"$pair"'"}]' done]], [0], [stdout], [ignore]) dnl Check that all the crap is in fact in the database log. AT_CHECK([[uuidfilt db | grep -v ^OVSDB | sed 's/"_date":[0-9]*/"_date":0/' | \ sed 's/"_is_diff":true,//' | ovstest test-json --multiple -]], [0], [[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"],["number","name"]]}},"version":"5.1.3"} {"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}} {"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}} {"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}} {"_comment":"add row for one 1","_date":0,"ordinals":{"<2>":{"name":"one","number":1}}} {"_comment":"delete row for 1","_date":0,"ordinals":{"<2>":null}} {"_comment":"add back row for one 1","_date":0,"ordinals":{"<3>":{"name":"one","number":1}}} {"_comment":"add row for two 2","_date":0,"ordinals":{"<4>":{"name":"two","number":2}}} {"_comment":"delete row for 2","_date":0,"ordinals":{"<4>":null}} {"_comment":"add back row for two 2","_date":0,"ordinals":{"<5>":{"name":"two","number":2}}} {"_comment":"add row for three 3","_date":0,"ordinals":{"<6>":{"name":"three","number":3}}} {"_comment":"delete row for 3","_date":0,"ordinals":{"<6>":null}} {"_comment":"add back row for three 3","_date":0,"ordinals":{"<7>":{"name":"three","number":3}}} {"_comment":"add row for four 4","_date":0,"ordinals":{"<8>":{"name":"four","number":4}}} {"_comment":"delete row for 4","_date":0,"ordinals":{"<8>":null}} {"_comment":"add back row for four 4","_date":0,"ordinals":{"<9>":{"name":"four","number":4}}} {"_comment":"add row for five 5","_date":0,"ordinals":{"<10>":{"name":"five","number":5}}} {"_comment":"delete row for 5","_date":0,"ordinals":{"<10>":null}} {"_comment":"add back row for five 5","_date":0,"ordinals":{"<11>":{"name":"five","number":5}}} ]]) dnl Dump out and check the actual database contents. on_exit 'kill `cat ovsdb-server.pid`' AT_CHECK([[ovsdb-server --detach --pidfile --log-file --no-chdir --remote=punix:socket db]], [0], [stdout], [ignore]) AT_CHECK([[ovsdb-client dump unix:socket ordinals]], [0], [stdout], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) dnl Now compact the database in-place. touch .db.tmp.~lock~ AT_CHECK([[ovsdb-tool compact db]], [0], [], [ignore]) dnl Make sure that "db" is still a symlink to dir/db instead of getting dnl replaced by a regular file. if test "$IS_WIN32" = "no"; then AT_CHECK([test ! -e .db.~lock]) AT_CHECK([test -h db]) AT_CHECK([test -f dir/db]) fi dnl We can't fully re-check the contents of the database log, because the dnl order of the records is not predictable, but there should only be 4 lines dnl in it now. AT_CAPTURE_FILE([db]) AT_CHECK([test `wc -l < db` -eq 4]) dnl And check that the dumped data is the same too: AT_CHECK([[ovsdb-server --detach --pidfile --log-file --no-chdir --remote=punix:socket db]], [0], [stdout], [ignore]) AT_CHECK([[ovsdb-client dump unix:socket ordinals]], [0], [stdout], [ignore]) OVSDB_SERVER_SHUTDOWN AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) AT_CLEANUP AT_SETUP([ovsdb-tool convert -- removing a column]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema AT_DATA([new-schema], [[{"name": "ordinals", "tables": { "ordinals": { "columns": { "number": {"type": "integer"}}}}} ]]) touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) dnl Put some data in the database. AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done]], [0], [stdout], [ignore]) dnl Dump out and check the actual database contents. AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ----- ------ <0> five 5 <1> four 4 <2> one 1 <3> three 3 <4> two 2 <5> zero 0 ]) OVSDB_SERVER_SHUTDOWN dnl Now convert the database in-place. touch .db.tmp.~lock~ AT_CHECK([[ovsdb-tool convert db new-schema]], [0], [], [ignore]) dnl We can't fully re-check the contents of the database log, because the dnl order of the records is not predictable, but there should only be 4 lines dnl in it now. AT_CAPTURE_FILE([db]) AT_CHECK([test `wc -l < db` -eq 4]) dnl And check that the dumped data is the same except for the removed column: AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid number ------------------------------------ ------ <0> 0 <1> 1 <2> 2 <3> 3 <4> 4 <5> 5 ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-tool convert -- adding a column]) AT_KEYWORDS([ovsdb file positive]) AT_DATA([schema], [[{"name": "ordinals", "tables": { "ordinals": { "columns": { "number": {"type": "integer"}}}}} ]]) ordinal_schema > new-schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) dnl Put some data in the database. AT_CHECK( [[for number in 0 1 2 3 4 5; do ovsdb-tool transact db ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": '$number'}}, {"op": "comment", "comment": "add row for '"$number"'"}]' done]], [0], [stdout], [ignore]) dnl Dump out and check the actual database contents. AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid number ------------------------------------ ------ <0> 0 <1> 1 <2> 2 <3> 3 <4> 4 <5> 5 ]) OVSDB_SERVER_SHUTDOWN dnl Now convert the database in-place. touch .db.tmp.~lock~ AT_CHECK([[ovsdb-tool convert db new-schema]], [0], [], [ignore]) dnl We can't fully re-check the contents of the database log, because the dnl order of the records is not predictable, but there should only be 4 lines dnl in it now. AT_CAPTURE_FILE([db]) AT_CHECK([test `wc -l < db` -eq 4]) dnl And check that the dumped data is the same except for the added column: AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore]) AT_CHECK([uuidfilt stdout], [0], [dnl ordinals table _uuid name number ------------------------------------ ---- ------ <0> "" 0 <1> "" 1 <2> "" 2 <3> "" 3 <4> "" 4 <5> "" 5 ]) OVSDB_SERVER_SHUTDOWN AT_CLEANUP AT_SETUP([ovsdb-tool unsupported cluster operations]) AT_KEYWORDS([ovsdb file negative compact query transact convert]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft]) AT_CHECK([ovsdb-tool compact db], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool convert db schema], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool needs-conversion db schema], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool query db '[[]]'], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool transact db '[[]]'], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CLEANUP AT_SETUP([ovsdb-tool schema-version, schema-cksum, schema-name]) AT_KEYWORDS([ovsdb file positive schema-version schema-cksum]) ordinal_schema > schema AT_CHECK([ovsdb-tool schema-version schema], [0], [5.1.3 ]) AT_CHECK([ovsdb-tool schema-cksum schema], [0], [12345678 9 ]) AT_CHECK([ovsdb-tool schema-name schema], [0], [ordinals ]) AT_CLEANUP AT_SETUP([ovsdb-tool database inspection commands - standalone]) AT_KEYWORDS([ovsdb file positive db-version db-cksum db-name db-cid db-sid db-local-address]) ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) AT_CHECK([ovsdb-tool db-version db], [0], [5.1.3 ]) AT_CHECK([ovsdb-tool db-cksum db], [0], [12345678 9 ]) AT_CHECK([ovsdb-tool db-name db], [0], [ordinals ]) AT_CHECK([ovsdb-tool db-cid db], [1], [], [ovsdb-tool: db: not a clustered database ]) AT_CHECK([ovsdb-tool db-sid db], [1], [], [ovsdb-tool: db: not a clustered database ]) AT_CHECK([ovsdb-tool db-local-address db], [1], [], [ovsdb-tool: db: not a clustered database ]) AT_CLEANUP AT_SETUP([ovsdb-tool database inspection commands - clustered]) AT_KEYWORDS([ovsdb file negative db-version db-cksum db-name db-cid db-sid db-local-address cluster]) ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create-cluster db schema tcp:1.2.3.4:1234]) AT_CHECK([ovsdb-tool db-version db], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool db-cksum db], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool db-name db], [0], [ordinals ]) AT_CHECK([(ovsdb-tool db-cid db; ovsdb-tool db-sid db) | uuidfilt], [0], [<0> <1> ]) AT_CHECK([ovsdb-tool db-local-address db], [0], [tcp:1.2.3.4:1234 ]) AT_CLEANUP AT_SETUP([ovsdb-tool database inspection commands - joining a cluster]) AT_KEYWORDS([ovsdb file positive db-version db-cksum db-name db-cid db-sid db-local-address cluster join joining]) ordinal_schema > schema touch .db.~lock~ for cid in '' 520cf525-3772-43cc-8268-23bf5b548cf4; do if test -z "$cid"; then cid_option= else cid_option=--cid=$cid fi AT_CHECK([rm -f db && ovsdb-tool $cid_option join-cluster db ordinals tcp:1.2.3.4:1234 tcp:2.3.4.5:1234], [0], [], [ignore]) AT_CHECK([ovsdb-tool db-version db], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool db-cksum db], [1], [], [ovsdb-tool: ovsdb error: db: cannot apply this operation to clustered database file ]) AT_CHECK([ovsdb-tool db-name db], [0], [ordinals ]) if test -z "$cid"; then AT_CHECK([ovsdb-tool db-cid db], [2], [], [db: cluster ID not yet known ]) else AT_CHECK_UNQUOTED([ovsdb-tool db-cid db], [0], [$cid ]) fi AT_CHECK([ovsdb-tool db-sid db | uuidfilt], [0], [<0> ]) AT_CHECK([ovsdb-tool db-local-address db], [0], [tcp:1.2.3.4:1234 ]) done AT_CLEANUP AT_SETUP([ovsdb-tool needs-conversion (no conversion needed)]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) AT_CHECK([ovsdb-tool needs-conversion db schema], [0], [no ]) AT_CLEANUP AT_SETUP([ovsdb-tool needs-conversion (conversion needed)]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema touch .db.~lock~ AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore]) sed 's/5\.1\.3/5.1.4/' < schema > schema2 AT_CHECK([diff schema schema2], [1], [ignore]) AT_CHECK([ovsdb-tool needs-conversion db schema2], [0], [yes ]) AT_CLEANUP AT_SETUP([ovsdb-tool create-cluster with initial data]) AT_KEYWORDS([ovsdb file positive]) # Create a standalone database and put some data in it. ordinal_schema > schema ovsdb-tool create db1 schema AT_CHECK( [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do set -- $pair ovsdb-tool transact db1 ' ["ordinals", {"op": "insert", "table": "ordinals", "row": {"name": "'$1'", "number": '$2'}}, {"op": "comment", "comment": "add row for '"$pair"'"}]' done | uuidfilt]], [0], [[[{"uuid":["uuid","<0>"]},{}] [{"uuid":["uuid","<1>"]},{}] [{"uuid":["uuid","<2>"]},{}] [{"uuid":["uuid","<3>"]},{}] [{"uuid":["uuid","<4>"]},{}] [{"uuid":["uuid","<5>"]},{}] ]], [ignore]) # Dump the data. AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db1]) AT_CHECK([ovsdb-client dump > expout]) OVSDB_SERVER_SHUTDOWN # Create a clustered database from the standalone one. ovsdb-tool create-cluster db2 db1 unix:s1.raft # Dump the data. AT_CHECK([ovsdb-server -vconsole:off -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db2]) AT_CHECK([ovsdb_client_wait ordinals connected]) AT_CHECK([ovsdb-client dump > dump2]) OVSDB_SERVER_SHUTDOWN # Make sure that the clustered data matched the standalone data. AT_CHECK([cat dump2], [0], [expout]) AT_CLEANUP AT_SETUP([ovsdb-tool convert-to-standalone]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft], [0], [stdout], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db >/dev/null 2>&1]) for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket > clusterdump]) OVSDB_SERVER_SHUTDOWN # Convert to standalone database from clustered database. AT_CHECK(ovsdb-tool cluster-to-standalone db1 db) # Check its standalone db AT_CHECK([ovsdb-tool db-is-standalone db1]) # Dump the standalone db data. AT_CHECK([ovsdb-server -vconsole:off -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db1]) AT_CHECK([ovsdb_client_wait ordinals connected]) AT_CHECK([ovsdb-client dump > standalonedump]) OVSDB_SERVER_SHUTDOWN # Make sure both standalone and cluster db data matches. AT_CHECK([diff standalonedump clusterdump]) AT_CLEANUP AT_SETUP([ovsdb-tool convert-to-standalone after schema conversion]) AT_KEYWORDS([ovsdb file positive]) ordinal_schema > schema AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft], [0], [stdout], [ignore]) on_exit 'kill `cat ovsdb-server.pid`' AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket dnl --log-file db >/dev/null 2>&1]) for txn in m4_foreach([txn], [[[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore]) done dnl Change the schema. AT_CHECK([sed 's/5\.1\.3/5.1.4/' < schema > schema2]) AT_CHECK([sed -i'back' -e '/.*"number":.*/a \ "is_seven": {"type": "boolean"}, ' schema2]) dnl Convert the database. AT_CHECK([ovsdb-client convert unix:socket schema2]) dnl Add a new row with a new column. AT_CHECK([ovsdb-client transact unix:socket dnl '[["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 7, "name": "seven", "is_seven": true} }]]'], [0], [ignore], [ignore]) AT_CHECK([ovsdb-client dump unix:socket > clusterdump]) AT_CHECK([uuidfilt clusterdump], [0], [dnl ordinals table _uuid is_seven name number ------------------------------------ -------- ----- ------ <0> false one 1 <1> false two 2 <2> false zero 0 <3> true seven 7 ]) OVSDB_SERVER_SHUTDOWN dnl Convert to standalone database from clustered database. AT_CHECK(ovsdb-tool cluster-to-standalone db1 db) dnl Check it's a standalone db. AT_CHECK([ovsdb-tool db-is-standalone db1]) dnl Dump the standalone db data. AT_CHECK([ovsdb-server -vconsole:off -vfile -vvlog:off --detach --no-chdir dnl --pidfile --log-file --remote=punix:db.sock db1]) AT_CHECK([ovsdb_client_wait ordinals connected]) AT_CHECK([ovsdb-client dump > standalonedump]) OVSDB_SERVER_SHUTDOWN dnl Make sure both standalone and cluster db data matches. AT_CHECK([diff standalonedump clusterdump]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-transaction.at000066400000000000000000000147051514270232600241170ustar00rootroot00000000000000AT_BANNER([OVSDB -- transactions]) OVSDB_CHECK_POSITIVE([empty table, empty transaction], [[transact \ '["print"]' \ '["commit"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl print: commit: print: abort: print:]) OVSDB_CHECK_POSITIVE([nonempty table, empty transaction], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["print"]' \ '["commit"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: print: 1: i=2, j=3 2: i=2, j=3 commit: print: 1: i=2, j=3 2: i=2, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3]) OVSDB_CHECK_POSITIVE([insert, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "1", "2"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 1 2: print: 1: i=2, j=3 2: i=2, j=3 3: i=1, j=2 commit: print: 1: i=2, j=3 2: i=2, j=3 3: i=1, j=2], [transaction]) OVSDB_CHECK_POSITIVE([insert, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "1", "2"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 1 2: print: 1: i=2, j=3 2: i=2, j=3 3: i=1, j=2 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([modify, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["modify", "2", "5", "-1"]' \ '["modify", "1", "-1", "4"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 modify 2 5 -1: modify 1 -1 4: print: 1: i=2, j=4 2: i=5, j=3 commit: print: 1: i=2, j=4 2: i=5, j=3], [transaction]) OVSDB_CHECK_POSITIVE([modify, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["modify", "2", "5", "-1"]' \ '["modify", "1", "-1", "4"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 modify 2 5 -1: modify 1 -1 4: print: 1: i=2, j=4 2: i=5, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([delete, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["delete", "1"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 delete 1: print: 2: i=2, j=3 commit: print: 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([delete, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["delete", "1"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 delete 1: print: 2: i=2, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([modify, delete, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["modify", "1", "5", "6"]' \ '["delete", "1"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 modify 1 5 6: delete 1: print: 2: i=2, j=3 commit: print: 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([modify, delete, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["modify", "1", "5", "6"]' \ '["delete", "1"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 modify 1 5 6: delete 1: print: 2: i=2, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([insert, delete, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "5", "6"]' \ '["delete", "1"]' \ '["delete", "3"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 5 6: delete 1: delete 3: print: 2: i=2, j=3 commit: print: 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([insert, delete, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "5", "6"]' \ '["delete", "1"]' \ '["delete", "3"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 5 6: delete 1: delete 3: print: 2: i=2, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([insert, modify, delete, commit], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "5", "6"]' \ '["delete", "1"]' \ '["modify", "3", "7", "8"]' \ '["delete", "3"]' \ '["print"]' \ '["commit"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 5 6: delete 1: modify 3 7 8: delete 3: print: 2: i=2, j=3 commit: print: 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([insert, modify, delete, abort], [[transact \ '["insert", "1", "2", "3"]' \ '["insert", "2", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["insert", "3", "5", "6"]' \ '["delete", "1"]' \ '["modify", "3", "7", "8"]' \ '["delete", "3"]' \ '["print"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: insert 2 2 3: commit: print: 1: i=2, j=3 2: i=2, j=3 insert 3 5 6: delete 1: modify 3 7 8: delete 3: print: 2: i=2, j=3 abort: print: 1: i=2, j=3 2: i=2, j=3], [transaction]) OVSDB_CHECK_POSITIVE([deletes are aborted cleanly], [[transact \ '["insert", "1", "2", "3"]' \ '["commit"]' \ '["print"]' \ '["delete", "1"]' \ '["abort"]' \ '["print"]' \ '["delete", "1"]' \ '["abort"]' \ '["print"]']], [dnl insert 1 2 3: commit: print: 1: i=2, j=3 delete 1: abort: print: 1: i=2, j=3 delete 1: abort: print: 1: i=2, j=3], [transaction]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-trigger.at000066400000000000000000000137541514270232600232400ustar00rootroot00000000000000AT_BANNER([OVSDB -- triggers]) # This is like OVSDB_CHECK_POSITIVE, except that UUIDs in the output # are replaced by markers of the form where N is a number. The # first unique UUID is replaced by <0>, the next by <1>, and so on. # If a given UUID appears more than once it is always replaced by the # same marker. m4_define([OVSDB_CHECK_TRIGGER], [AT_SETUP([$1]) AT_KEYWORDS([ovsdb execute execution trigger positive $4]) AT_CHECK([test-ovsdb trigger $2], [0], [stdout], []) AT_CHECK([uuidfilt stdout], [0], [$3]) AT_CLEANUP]) OVSDB_CHECK_TRIGGER([trigger fires immediately], ["`ordinal_schema`" [\ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}]}, {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]']], [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{},{"uuid":["uuid","<2>"]}] ]]) OVSDB_CHECK_TRIGGER([trigger times out], ["`ordinal_schema`" [\ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}, {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}]' \ '["advance", 10]']], [[t=0: new trigger 0 t=10: trigger 0 (delayed): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out after 10 ms","error":"timed out"}] ]]) OVSDB_CHECK_TRIGGER([trigger fires after delay], ["`ordinal_schema`" [\ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]' \ '["advance", 5]' \ '["ordinals", {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}]' \ '["advance", 5]' \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]']], [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}] t=5: new trigger 1 t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}] t=10: trigger 1 (delayed): [{}] ]]) OVSDB_CHECK_TRIGGER([delayed trigger modifies database], ["`ordinal_schema`" [\ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]' \ '["advance", 5]' \ '["ordinals", {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}, {"op": "delete", "table": "ordinals", "where": [["number", "<", 2]]}]' \ '["advance", 5]' \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]' \ '["advance", 5]' \ '["ordinals", {"op": "select", "table": "ordinals", "where": []}]']], [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}] t=5: new trigger 1 t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}] t=10: trigger 1 (delayed): [{},{"count":2}] t=15: trigger 3 (immediate): [{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<3>"],"name":"two","number":2}]}] ]]) OVSDB_CHECK_TRIGGER([one delayed trigger wakes up another], ["`ordinal_schema`" [\ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 0, "name": "zero"}}, {"op": "insert", "table": "ordinals", "row": {"number": 1, "name": "one"}}]' \ '["advance", 5]' \ '["ordinals", {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "two", "number": 2}]}, {"op": "delete", "table": "ordinals", "where": [["number", "==", 2]]}, {"op": "insert", "table": "ordinals", "row": {"number": 3, "name": "three"}}]' \ '["ordinals", {"op": "wait", "timeout": 10, "table": "ordinals", "where": [], "columns": ["name", "number"], "until": "==", "rows": [{"name": "zero", "number": 0}, {"name": "one", "number": 1}, {"name": "two", "number": 2}]}, {"op": "delete", "table": "ordinals", "where": [["number", "<", 2]]}]' \ '["advance", 5]' \ '["ordinals", {"op": "insert", "table": "ordinals", "row": {"number": 2, "name": "two"}}]' \ '["advance", 5]' \ '["ordinals", {"op": "select", "table": "ordinals", "where": []}]']], [[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}] t=5: new trigger 1 t=5: new trigger 2 t=10: trigger 3 (immediate): [{"uuid":["uuid","<2>"]}] t=10: trigger 2 (delayed): [{},{"count":2}] t=10: trigger 1 (delayed): [{},{"count":1},{"uuid":["uuid","<3>"]}] t=15: trigger 4 (immediate): [{"rows":[{"_uuid":["uuid","<3>"],"_version":["uuid","<4>"],"name":"three","number":3}]}] ]]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb-types.at000066400000000000000000000172721514270232600227400ustar00rootroot00000000000000AT_BANNER([OVSDB -- atomic types]) OVSDB_CHECK_POSITIVE_CPY([integer], [[parse-atomic-type '["integer"]' ]], ["integer"]) OVSDB_CHECK_POSITIVE_CPY([real], [[parse-atomic-type '["real"]' ]], ["real"]) OVSDB_CHECK_POSITIVE_CPY([boolean], [[parse-atomic-type '["boolean"]' ]], ["boolean"]) OVSDB_CHECK_POSITIVE_CPY([string], [[parse-atomic-type '["string"]' ]], ["string"]) OVSDB_CHECK_POSITIVE_CPY([uuid], [[parse-atomic-type '["uuid"]' ]], ["uuid"]) OVSDB_CHECK_NEGATIVE_CPY([void is not a valid atomic-type], [[parse-atomic-type '["void"]' ]], ["void" is not an atomic-type]) AT_BANNER([OVSDB -- base types]) OVSDB_CHECK_POSITIVE_CPY([integer enum], [[parse-base-type '{"type": "integer", "enum": ["set", [-1, 4, 5]]}' ]], [[{"enum":["set",[-1,4,5]],"type":"integer"}]]) OVSDB_CHECK_POSITIVE_CPY([integer >= 5], [[parse-base-type '{"type": "integer", "minInteger": 5}' ]], [{"minInteger":5,"type":"integer"}]) OVSDB_CHECK_POSITIVE_CPY([integer <= 7], [[parse-base-type '{"type": "integer", "maxInteger": 7}' ]], [{"maxInteger":7,"type":"integer"}]) OVSDB_CHECK_POSITIVE_CPY([integer between -5 and 10], [[parse-base-type '{"type": "integer", "minInteger": -5, "maxInteger": 10}']], [{"maxInteger":10,"minInteger":-5,"type":"integer"}]) OVSDB_CHECK_NEGATIVE_CPY([integer max may not be less than min], [[parse-base-type '{"type": "integer", "minInteger": 5, "maxInteger": 3}']], [minInteger exceeds maxInteger]) OVSDB_CHECK_POSITIVE_CPY([real enum], [[parse-base-type '{"type": "real", "enum": ["set", [1.5, 0, 2.75]]}' ]], [[{"enum":["set",[0,1.5,2.75]],"type":"real"}]]) OVSDB_CHECK_POSITIVE_CPY([real >= -1.5], [[parse-base-type '{"type": "real", "minReal": -1.5}']], [{"minReal":-1.5,"type":"real"}]) OVSDB_CHECK_POSITIVE_CPY([real <= 1e5], [[parse-base-type '{"type": "real", "maxReal": 1e5}']], [{"maxReal":100000,"type":"real"}]) OVSDB_CHECK_POSITIVE_CPY([real between -2.5 and 3.75], [[parse-base-type '{"type": "real", "minReal": -2.5, "maxReal": 3.75}']], [{"maxReal":3.75,"minReal":-2.5,"type":"real"}]) OVSDB_CHECK_NEGATIVE_CPY([real max may not be less than min], [[parse-base-type '{"type": "real", "minReal": 555, "maxReal": 444}']], [minReal exceeds maxReal]) OVSDB_CHECK_POSITIVE_CPY([boolean], [[parse-base-type '[{"type": "boolean"}]' ]], ["boolean"]) OVSDB_CHECK_POSITIVE_CPY([boolean enum], [[parse-base-type '{"type": "boolean", "enum": true}' ]], [[{"enum":true,"type":"boolean"}]]) OVSDB_CHECK_POSITIVE_CPY([string enum], [[parse-base-type '{"type": "string", "enum": ["set", ["def", "abc"]]}']], [[{"enum":["set",["abc","def"]],"type":"string"}]]) OVSDB_CHECK_POSITIVE_CPY([string minLength], [[parse-base-type '{"type": "string", "minLength": 1}']], [{"minLength":1,"type":"string"}]) OVSDB_CHECK_POSITIVE_CPY([string maxLength], [[parse-base-type '{"type": "string", "maxLength": 5}']], [{"maxLength":5,"type":"string"}]) OVSDB_CHECK_POSITIVE_CPY([string minLength and maxLength], [[parse-base-type '{"type": "string", "minLength": 1, "maxLength": 5}']], [{"maxLength":5,"minLength":1,"type":"string"}]) OVSDB_CHECK_NEGATIVE_CPY([maxLength must not be less than minLength], [[parse-base-type '{"type": "string", "minLength": 5, "maxLength": 3}']], [minLength exceeds maxLength]) OVSDB_CHECK_NEGATIVE_CPY([maxLength must not be negative], [[parse-base-type '{"type": "string", "maxLength": -1}']], [maxLength out of valid range 0 to 4294967295]) OVSDB_CHECK_POSITIVE_CPY([uuid enum], [[parse-base-type '{"type": "uuid", "enum": ["uuid", "36bf19c0-ad9d-4232-bb85-b3d73dfe2123"]}' ]], [[{"enum":["uuid","36bf19c0-ad9d-4232-bb85-b3d73dfe2123"],"type":"uuid"}]]) OVSDB_CHECK_POSITIVE_CPY([uuid refTable], [[parse-base-type '{"type": "uuid", "refTable": "myTable"}' ]], [{"refTable":"myTable","type":"uuid"}]) OVSDB_CHECK_NEGATIVE_CPY([uuid refTable must be valid id], [[parse-base-type '{"type": "uuid", "refTable": "a-b-c"}' ]], [Type mismatch for member 'refTable']) OVSDB_CHECK_NEGATIVE_CPY([void is not a valid base-type], [[parse-base-type '["void"]' ]], ["void" is not an atomic-type]) OVSDB_CHECK_NEGATIVE_CPY(["type" member must be present], [[parse-base-type '{}']], [Parsing ovsdb type failed: Required 'type' member is missing.]) AT_BANNER([OVSDB -- simple types]) OVSDB_CHECK_POSITIVE_CPY([simple integer], [[parse-type '["integer"]' ]], ["integer"]) OVSDB_CHECK_POSITIVE_CPY([simple real], [[parse-type '["real"]' ]], ["real"]) OVSDB_CHECK_POSITIVE_CPY([simple boolean], [[parse-type '["boolean"]' ]], ["boolean"]) OVSDB_CHECK_POSITIVE_CPY([simple string], [[parse-type '["string"]' ]], ["string"]) OVSDB_CHECK_POSITIVE_CPY([simple uuid], [[parse-type '["uuid"]' ]], ["uuid"]) OVSDB_CHECK_POSITIVE_CPY([integer in object], [[parse-type '{"key": "integer"}' ]], ["integer"]) OVSDB_CHECK_POSITIVE_CPY([real in object with explicit min and max], [[parse-type '{"key": "real", "min": 1, "max": 1}' ]], ["real"]) OVSDB_CHECK_NEGATIVE_CPY([key type is required], [[parse-type '{}' ]], [Required 'key' member is missing.]) OVSDB_CHECK_NEGATIVE_CPY([void is not a valid type], [[parse-type '["void"]' ]], ["void" is not an atomic-type]) AT_BANNER([OVSDB -- set types]) OVSDB_CHECK_POSITIVE_CPY([optional boolean], [[parse-type '{"key": "boolean", "min": 0}' ]], [[{"key":"boolean","min":0}]], [set]) OVSDB_CHECK_POSITIVE_CPY([set of 1 to 3 uuids], [[parse-type '{"key": "uuid", "min": 1, "max": 3}' ]], [[{"key":"uuid","max":3}]]) OVSDB_CHECK_POSITIVE_CPY([set of 0 to 3 strings], [[parse-type '{"key": "string", "min": 0, "max": 3}' ]], [[{"key":"string","max":3,"min":0}]]) OVSDB_CHECK_POSITIVE_CPY([set of 0 or more integers], [[parse-type '{"key": "integer", "min": 0, "max": "unlimited"}']], [[{"key":"integer","max":"unlimited","min":0}]]) OVSDB_CHECK_POSITIVE_CPY([set of 1 or more reals], [[parse-type '{"key": "real", "min": 1, "max": "unlimited"}']], [[{"key":"real","max":"unlimited"}]]) OVSDB_CHECK_NEGATIVE_CPY([set max cannot be less than min], [[parse-type '{"key": "real", "min": 5, "max": 3}' ]], [ovsdb type fails constraint checks]) OVSDB_CHECK_NEGATIVE_CPY([set max cannot be negative], [[parse-type '{"key": "real", "max": -1}' ]], [bad min or max value]) OVSDB_CHECK_NEGATIVE_CPY([set min cannot be negative], [[parse-type '{"key": "real", "min": -1}' ]], [bad min or max value]) OVSDB_CHECK_NEGATIVE_CPY([set min cannot be greater than one], [[parse-type '{"key": "real", "min": 10, "max": "unlimited"}']], [ovsdb type fails constraint checks]) AT_BANNER([OVSDB -- map types]) OVSDB_CHECK_POSITIVE_CPY([map of 1 integer to boolean], [[parse-type '{"key": "integer", "value": "boolean"}' ]], [[{"key":"integer","value":"boolean"}]]) OVSDB_CHECK_POSITIVE_CPY([map of 1 boolean to integer, explicit min and max], [[parse-type '{"key": "boolean", "value": "integer", "min": 1, "max": 1}' ]], [[{"key":"boolean","value":"integer"}]]) OVSDB_CHECK_POSITIVE_CPY([map of 1 to 5 uuid to real], [[parse-type '{"key": "uuid", "value": "real", "min": 1, "max": 5}' ]], [[{"key":"uuid","max":5,"value":"real"}]]) OVSDB_CHECK_POSITIVE_CPY([map of 0 to 10 string to uuid], [[parse-type '{"key": "string", "value": "uuid", "min": 0, "max": 10}' ]], [[{"key":"string","max":10,"min":0,"value":"uuid"}]]) OVSDB_CHECK_POSITIVE_CPY([map of 1 to 20 real to string], [[parse-type '{"key": "real", "value": "string", "min": 1, "max": 20}' ]], [[{"key":"real","max":20,"value":"string"}]]) OVSDB_CHECK_POSITIVE_CPY([map of 0 or more string to real], [[parse-type '{"key": "string", "value": "real", "min": 0, "max": "unlimited"}' ]], [[{"key":"string","max":"unlimited","min":0,"value":"real"}]]) OVSDB_CHECK_NEGATIVE_CPY([map key type is required], [[parse-type '{"value": "integer"}' ]], [Required 'key' member is missing.]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovsdb.at000066400000000000000000000013641514270232600215710ustar00rootroot00000000000000m4_include([tests/ovsdb-log.at]) m4_include([tests/ovsdb-types.at]) m4_include([tests/ovsdb-data.at]) m4_include([tests/ovsdb-column.at]) m4_include([tests/ovsdb-table.at]) m4_include([tests/ovsdb-row.at]) m4_include([tests/ovsdb-schema.at]) m4_include([tests/ovsdb-condition.at]) m4_include([tests/ovsdb-mutation.at]) m4_include([tests/ovsdb-query.at]) m4_include([tests/ovsdb-transaction.at]) m4_include([tests/ovsdb-execution.at]) m4_include([tests/ovsdb-trigger.at]) m4_include([tests/ovsdb-tool.at]) m4_include([tests/ovsdb-replication.at]) m4_include([tests/ovsdb-server.at]) m4_include([tests/ovsdb-client.at]) m4_include([tests/ovsdb-monitor.at]) m4_include([tests/ovsdb-idl.at]) m4_include([tests/ovsdb-lock.at]) m4_include([tests/ovsdb-rbac.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovstest.c000066400000000000000000000064651514270232600220100ustar00rootroot00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * 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. */ /* The mother of all test programs that links with libopevswitch.la */ #include #undef NDEBUG #include #include #include #include "command-line.h" #include "openvswitch/dynamic-string.h" #include "ovstest.h" #include "util.h" static struct ovs_cmdl_command *commands = NULL; static size_t n_commands = 0; static size_t allocated_commands = 0; static void add_command(struct ovs_cmdl_command *cmd) { const struct ovs_cmdl_command nil = {NULL, NULL, 0, 0, NULL, OVS_RO}; while (n_commands + 1 >= allocated_commands) { commands = x2nrealloc(commands, &allocated_commands, sizeof *cmd); } commands[n_commands] = *cmd; commands[n_commands + 1] = nil; n_commands++; } #define OVSTEST_USAGE \ "TEST [TESTARGS] where 'TEST' is a string, 'TESTARGS' are optional \n"\ "arguments of the TEST" static void flush_help_string(struct ds *ds) { if (ds->length > 2 ) { ds->length -= 2; printf ("%s\n", ds_cstr(ds)); ds_clear(ds); } } static void help(struct ovs_cmdl_context *ctx OVS_UNUSED) { const struct ovs_cmdl_command *p; struct ds test_names = DS_EMPTY_INITIALIZER; const int linesize = 70; printf("%s: the big test executable\n" "usage: %s TEST [TESTARGS]\n" "where TEST is one of the following. \n\n", program_name, program_name); for(p = commands; p->name != NULL; p++) { if (*p->name != '-') { /* Skip internal commands */ ds_put_format(&test_names, "%s, ", p->name); if ((test_names.length) >= linesize) { flush_help_string(&test_names); } } } flush_help_string(&test_names); ds_destroy(&test_names); } static void add_top_level_commands(void) { struct ovs_cmdl_command help_cmd = {"--help", NULL, 0, 0, help, OVS_RO }; add_command(&help_cmd); } void ovstest_register(const char *test_name, ovs_cmdl_handler f) { struct ovs_cmdl_command test_cmd; test_cmd.name = test_name; test_cmd.usage = NULL; test_cmd.min_args = 0; test_cmd.max_args = INT_MAX; test_cmd.handler = f; test_cmd.mode = OVS_RO; add_command(&test_cmd); } static void cleanup(void) { if (allocated_commands) { free(commands); } } int main(int argc, char *argv[]) { set_program_name(argv[0]); if (argc < 2) { ovs_fatal(0, "expect test program to be specified; " "use --help for usage"); } add_top_level_commands(); if (argc > 1) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; ovs_cmdl_run_command(&ctx, commands); } cleanup(); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/ovstest.h000066400000000000000000000047261514270232600220130ustar00rootroot00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * 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 OVSTEST_H #define OVSTEST_H #include "compiler.h" #include "command-line.h" /* Overview * ======== * * OVS tests directory contains many small test programs. One down side * of building them as individual programs is that they all have to be * linked whenever a library function is modified. * * ovstest is an attempt to improve the overall build time by linking * all test programs into a single program, ovs-test. Regardless of * the number of test programs, linking will be done only once to produce * ovstest. * * With ovstest, each test programs now becomes a sub program of ovstest. * For example, 'mytest' program, can now be invoked as 'ovstest mytest'. * * 'ovstest --help' will list all test programs can be invoked. * * The 'Usage' section below documents how to add a new sub program * to ovstest using OVSTEST_REIGSTER macros. */ typedef void (*ovstest_func)(int argc, char *argv[]); void ovstest_register(const char *test_name, ovs_cmdl_handler f); /* Usage * ===== * * For each sub test program, its 'main' program should be named as * '_main()'. * * The 'main' programs should be registered with ovstest as a sub program. * OVSTEST_REGISTER(name, function) * * 'name' will be name of the test program. It is expected as argv[1] when * invoking with ovstest. * * 'function' is the 'main' program mentioned above. * * Example: * ---------- * * Suppose the test program is called my-test.c * ... * * static void * my_test_main(int argc, char *argv[]) * { * .... * } * * OVSTEST_REGISTER("my-test", my_test_main); */ #define OVSTEST_REGISTER(name, function) \ static void \ ovstest_wrapper_##function##__(struct ovs_cmdl_context *ctx) \ { \ function(ctx->argc, ctx->argv); \ } \ OVS_CONSTRUCTOR(register_##function) { \ ovstest_register(name, ovstest_wrapper_##function##__); \ } #endif openvswitch-3.7.0~git20260211.8c6ebf8/tests/packet-type-aware.at000066400000000000000000001462311514270232600240020ustar00rootroot00000000000000AT_BANNER([packet-type-aware pipeline]) AT_SETUP([ptap - legal flow entries in ptap bridge]) OVS_VSWITCHD_START AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl -Oopenflow13 add-flow br0 priority=1,dl_src=11:22:33:44:55:66,eth_type=0x1234,actions=drop ovs-ofctl -Oopenflow14 add-flow br0 priority=1,ip,nw_dst=10.11.12.13,actions=drop ovs-ofctl -Oopenflow15 add-flow br0 priority=1,ipv6,nw_proto=6,actions=drop ovs-ofctl -Oopenflow14 add-flow br0 priority=2,packet_type=\(0,0x0\),dl_src=11:22:33:44:55:66,dl_type=0x4567,actions=drop ovs-ofctl -Oopenflow15 add-flow br0 priority=2,packet_type=\(0,0x0\),arp,arp_tpa=10.11.12.13,actions=drop ovs-ofctl -Oopenflow15 add-flow br0 priority=3,packet_type=\(1,0x806\),arp_tpa=10.11.12.13,actions=drop ovs-ofctl -Oopenflow13 add-flow br0 priority=3,packet_type=\(1,0x800\),nw_dst=10.11.12.13,actions=drop ovs-ofctl -Oopenflow14 add-flow br0 priority=3,packet_type=\(1,0x86dd\),ipv6_dst=1234:5678::/32,actions=drop ], [0]) AT_CHECK([ovs-ofctl -Oopenflow15 dump-flows br0 | ofctl_strip | sort | grep actions], [0], [dnl priority=1,dl_src=11:22:33:44:55:66,dl_type=0x1234 actions=drop priority=1,ip,nw_dst=10.11.12.13 actions=drop priority=1,tcp6 actions=drop priority=2,arp,arp_tpa=10.11.12.13 actions=drop priority=2,dl_src=11:22:33:44:55:66,dl_type=0x4567 actions=drop priority=3,packet_type=(1,0x800),nw_dst=10.11.12.13 actions=drop priority=3,packet_type=(1,0x806),arp_tpa=10.11.12.13 actions=drop priority=3,packet_type=(1,0x86dd),ipv6_dst=1234:5678::/32 actions=drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ptap - triangle bridge setup with L2 and L3 GRE tunnels]) ######################## # GRE tunneling test setup for PTAP bridge # # 192.168.10.10 192.168.10.20 192.168.10.30 # n1 n2 n3 # | | | # +------o------+ +------o------+ +------o------+ # | br-in1 | | br-in2 | | br-in3 | # | | | (PTAP) | | | # +------o------+ +------o------+ +------o------+ # gre gre gre # 10.0.0.1 (10.0.0.2) (10.0.0.3) # (20.0.0.1) 20.0.0.2 (20.0.0.3) # (30.0.0.1) LOCAL (30.0.0.2) LOCAL 30.0.0.3 LOCAL # +-----------o-+ +-----------o-+ +-----------o-+ # | br-p1 | | br-p2 | | br-p3 | # +------o------+ +------o------+ +------o------+ # p1-0 | | p2-0 | p3-0 # p0-1 | | p0-2 | p0-3 # +--o------------------------o-------------------------o--+ # | br0 | # +--------------------------------------------------------+ #" # GRE tunnel ports: # No Bridge Name Packet-type Remote bridge & ports # ----------------------------------------------------------------------- # 1020 br-in1 gre-12 legacy-l2 br-in2 2010 (ptap) # 1021 br-in1 gre-12_l3 legacy-l3 same # 1030 br-in1 gre-13 legacy-l2 br-in3 3010 (l2) # 2010 br-in2 gre-21 ptap br-in1 1020 (l2), 1021 (l3) # 2030 br-in2 gre-23 ptap br-in3 3020 (l2), 3021 (l3) # 3010 br-in3 gre-31 legacy-l2 br-in1 1030 (l2) # 3020 br-in3 gre-32 legacy-l2 br-in2 2010 (ptap) # 3021 br-in3 gre-32_l3 legacy-l3 same HWADDR_BRP1=aa:55:00:00:00:01 HWADDR_BRP2=aa:55:00:00:00:02 HWADDR_BRP3=aa:55:00:00:00:03 OVS_VSWITCHD_START([dnl -- add-br br-in1 \ -- set bridge br-in1 datapath_type=dummy fail-mode=secure \ -- add-br br-in2 \ -- set bridge br-in2 datapath_type=dummy fail-mode=secure \ -- add-br br-in3 \ -- set bridge br-in3 datapath_type=dummy fail-mode=secure \ -- add-br br-p1 -- \ -- set bridge br-p1 datapath_type=dummy fail-mode=secure other-config:hwaddr=\"$HWADDR_BRP1\" \ -- add-br br-p2 -- \ -- set bridge br-p2 datapath_type=dummy fail-mode=secure other-config:hwaddr=\"$HWADDR_BRP2\" \ -- add-br br-p3 -- \ -- set bridge br-p3 datapath_type=dummy fail-mode=secure other-config:hwaddr=\"$HWADDR_BRP3\" \ -- add-port br-p1 p1-0 \ -- set interface p1-0 type=patch options:peer=p0-1 ofport_request=2 \ -- add-port br-p2 p2-0 \ -- set interface p2-0 type=patch options:peer=p0-2 ofport_request=2 \ -- add-port br-p3 p3-0 \ -- set interface p3-0 type=patch options:peer=p0-3 ofport_request=2 \ -- add-port br0 p0-1 \ -- set interface p0-1 type=patch options:peer=p1-0 ofport_request=10 \ -- add-port br0 p0-2 \ -- set interface p0-2 type=patch options:peer=p2-0 ofport_request=20 \ -- add-port br0 p0-3 \ -- set interface p0-3 type=patch options:peer=p3-0 ofport_request=30 \ -- add-port br-in1 gre12 \ -- set interface gre12 type=gre options:remote_ip=10.0.0.2 \ ofport_request=1020 \ -- add-port br-in1 gre12_l3 \ -- set interface gre12_l3 type=gre options:remote_ip=10.0.0.2 \ ofport_request=1021 options:packet_type=legacy_l3 \ -- add-port br-in1 gre13 \ -- set interface gre13 type=gre options:remote_ip=10.0.0.3 \ ofport_request=1030 \ -- add-port br-in2 gre21 \ -- set interface gre21 type=gre options:remote_ip=20.0.0.1 \ ofport_request=2010 options:packet_type=ptap \ -- add-port br-in2 gre23 \ -- set interface gre23 type=gre options:remote_ip=20.0.0.3 \ ofport_request=2030 options:packet_type=ptap \ -- add-port br-in3 gre31 \ -- set interface gre31 type=gre options:remote_ip=30.0.0.1 \ ofport_request=3010 \ -- add-port br-in3 gre32 \ -- set interface gre32 type=gre options:remote_ip=30.0.0.2 \ ofport_request=3020 \ -- add-port br-in3 gre32_l3 \ -- set interface gre32_l3 type=gre options:remote_ip=30.0.0.2 \ ofport_request=3021 options:packet_type=legacy_l3 ]) # Setup bridge infrastructure AT_CHECK([ # Populate the MAC table of br0 ovs-ofctl del-flows br0 && ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP1,actions=10 && ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP2,actions=20 && ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP3,actions=30 && ovs-ofctl del-flows br-in1 && ovs-ofctl del-flows br-in2 && ovs-ofctl del-flows br-in3 && ovs-ofctl del-flows br-p1 && ovs-ofctl del-flows br-p2 && ovs-ofctl del-flows br-p3 ], [0]) ### Setup GRE tunnels AT_CHECK([ ovs-appctl netdev-dummy/ip4addr br-p1 10.0.0.1/24 && ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 && ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2 && ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3 && ovs-appctl netdev-dummy/ip4addr br-p2 20.0.0.2/24 && ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1 && ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2 && ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3 && ovs-appctl netdev-dummy/ip4addr br-p3 30.0.0.3/24 && ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1 && ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2 && ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3 ], [0], [ignore]) AT_CHECK([ ovs-appctl ovs/route/show | grep Cached: | sort ], [0], [dnl Cached: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1 Cached: 10.0.0.1/32 dev br-p1 SRC 10.0.0.1 local Cached: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2 Cached: 20.0.0.2/32 dev br-p2 SRC 20.0.0.2 local Cached: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3 Cached: 30.0.0.3/32 dev br-p3 SRC 30.0.0.3 local ]) AT_CHECK([ ovs-appctl tnl/neigh/show | grep br-p | sort ], [0], [ignore]) ### Flows in br-pto twist TEP IP addresses in tunnel IP headers AT_CHECK([ ovs-ofctl add-flow br-p1 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:20.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:30.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL ovs-ofctl add-flow br-p2 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:10.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:30.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL ovs-ofctl add-flow br-p3 in_port:LOCAL,actions=2 ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:10.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:20.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ], [0]) AT_CHECK([ ovs-ofctl dump-flows br-p1 | ofctl_strip | sort | grep actions ovs-ofctl dump-flows br-p2 | ofctl_strip | sort | grep actions ovs-ofctl dump-flows br-p3 | ofctl_strip | sort | grep actions ], [0], [dnl in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=20.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ip,in_port=2,nw_dst=30.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ip,in_port=2,nw_dst=30.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ip,in_port=2,nw_dst=20.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ]) ### Setup test ports for traffic injection N1_IP=192.168.10.10 N2_IP=192.168.10.20 N3_IP=192.168.10.30 N1_MAC=aa:55:aa:55:00:01 N2_MAC=aa:55:aa:55:00:02 N3_MAC=aa:55:aa:55:00:03 N1_OFPORT=10 N2_OFPORT=20 N3_OFPORT=30 AT_CHECK([ ovs-vsctl \ -- add-port br-in1 n1 \ -- set interface n1 type=dummy ofport_request=$N1_OFPORT \ options:tx_pcap=n1.pcap \ -- add-port br-in2 n2 \ -- set interface n2 type=dummy ofport_request=$N2_OFPORT \ options:tx_pcap=n2.pcap \ -- add-port br-in3 n3 \ -- set interface n3 type=dummy ofport_request=$N3_OFPORT \ options:tx_pcap=n3.pcap ], [0]) #N1_DPPORT=$(ovs-appctl dpif/show | grep "n1 10" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N2_DPPORT=$(ovs-appctl dpif/show | grep "n2 20" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N3_DPPORT=$(ovs-appctl dpif/show | grep "n3 30" | sed 's|.*/\([[0-9]]*\):.*|\1|') ### Verify datapath configuration AT_CHECK([ ovs-appctl dpif/show | grep -v hit | sed 's./[[0-9]]\{1,\}..' ], [0], [dnl br-in1: br-in1 65534: (dummy-internal) gre12 1020: (gre: remote_ip=10.0.0.2) gre12_l3 1021: (gre: packet_type=legacy_l3, remote_ip=10.0.0.2) gre13 1030: (gre: remote_ip=10.0.0.3) n1 10: (dummy) br-in2: br-in2 65534: (dummy-internal) gre21 2010: (gre: packet_type=ptap, remote_ip=20.0.0.1) gre23 2030: (gre: packet_type=ptap, remote_ip=20.0.0.3) n2 20: (dummy) br-in3: br-in3 65534: (dummy-internal) gre31 3010: (gre: remote_ip=30.0.0.1) gre32 3020: (gre: remote_ip=30.0.0.2) gre32_l3 3021: (gre: packet_type=legacy_l3, remote_ip=30.0.0.2) n3 30: (dummy) br-p1: br-p1 65534: (dummy-internal) p1-0 2/none: (patch: peer=p0-1) br-p2: br-p2 65534: (dummy-internal) p2-0 2/none: (patch: peer=p0-2) br-p3: br-p3 65534: (dummy-internal) p3-0 2/none: (patch: peer=p0-3) br0: br0 65534: (dummy-internal) p0-1 10/none: (patch: peer=p1-0) p0-2 20/none: (patch: peer=p2-0) p0-3 30/none: (patch: peer=p3-0) ]) ### Test L3 forwarding flows AT_CHECK([ ovs-ofctl add-flow br-in1 ip,nw_dst=$N1_IP,actions=mod_dl_dst:$N1_MAC,$N1_OFPORT # Local route to N1 ovs-ofctl add-flow br-in1 ip,nw_dst=$N2_IP,actions=1020 # Route to N2 via the L2 tunnel to br-in2 ovs-ofctl add-flow br-in1 ip,nw_dst=$N3_IP,actions=1030 # Route to N3 direct through L2 tunnel ovs-ofctl add-flow br-in2 ip,nw_dst=$N2_IP,actions=mod_dl_dst:$N2_MAC,$N2_OFPORT # Local route to N2 for ethernet packets ovs-ofctl add-flow br-in2 ip,nw_dst=$N1_IP,actions=2010 # Route to N1 for ethernet packet ovs-ofctl add-flow br-in2 packet_type=\(1,0x800\),nw_dst=$N1_IP,actions=2010 # Route to N1 for IP packets ovs-ofctl add-flow br-in2 ip,nw_dst=$N3_IP,actions=2010 # Indirect route to N3 via br-in1 for ethernet packet ovs-ofctl add-flow br-in2 packet_type=\(1,0x800\),nw_dst=$N3_IP,actions=2030 # Direct route to N3 for IP packets ovs-ofctl add-flow br-in3 ip,nw_dst=$N3_IP,actions=mod_dl_dst:$N3_MAC,$N3_OFPORT # Local route to N1 ovs-ofctl add-flow br-in3 ip,nw_dst=$N2_IP,actions=3020 # Route to N2 via the L2 tunnel ovs-ofctl add-flow br-in3 ip,nw_dst=$N1_IP,actions=3021 # Route to N1 via br-in2 through L3 tunnel ], [0]) AT_CHECK([ ovs-ofctl dump-flows br-in1 | ofctl_strip | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=mod_dl_dst:aa:55:aa:55:00:01,output:10 ip,nw_dst=192.168.10.20 actions=output:1020 ip,nw_dst=192.168.10.30 actions=output:1030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:2010 ip,nw_dst=192.168.10.20 actions=mod_dl_dst:aa:55:aa:55:00:02,output:20 ip,nw_dst=192.168.10.30 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.30 actions=output:2030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:3021 ip,nw_dst=192.168.10.20 actions=output:3020 ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) ### Inject ICMP Echo request test packets ovs-appctl vlog/set any:file:dbg # N1 to N3, via the L2 GRE tunnel between br-in1 and br-in3 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=30.0.0.1,dst=30.0.0.3),in_port(gre_sys),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:set(eth(dst=aa:55:aa:55:00:03)),n3 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N1 to N2 via the L2 GRE tunnel between br-in1 and br-in2 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 461e7d1a95a13a6dd2099cab080045000054500b40004001552fc0a80a0ac0a80a140800531f09a90001e9509a580000000055ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 461e7d1a95a13a6dd2099cab08004500005450934000400154a7c0a80a0ac0a80a140800f41d09a90002ea509a5800000000b3ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=20.0.0.1,dst=20.0.0.2),in_port(gre_sys),packet_type(ns=0,id=0),eth(dst=46:1e:7d:1a:95:a1),eth_type(0x0800),ipv4(dst=192.168.10.20,frag=no), packets:1, bytes:98, used:0.0s, actions:set(eth(dst=aa:55:aa:55:00:02)),n2 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N2 to N1 via the L2 GRE tunnel between br-in2 and br-in1 AT_CHECK([ ovs-appctl netdev-dummy/receive n2 3a6dd2099cab461e7d1a95a10800450000542c1f40004001791bc0a80a14c0a80a0a0800154b0b6800011b519a580000000054cf0e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n2 3a6dd2099cab461e7d1a95a10800450000542c744000400178c6c0a80a14c0a80a0a08003f420b6800021c519a580000000029d70e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.10,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:01,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.1,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p2)),set(ipv4(src=10.0.0.2,dst=10.0.0.1)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=10.0.0.2,dst=10.0.0.1),in_port(gre_sys),packet_type(ns=0,id=0),eth(dst=3a:6d:d2:09:9c:ab),eth_type(0x0800),ipv4(dst=192.168.10.10,frag=no), packets:1, bytes:98, used:0.0s, actions:set(eth(dst=aa:55:aa:55:00:01)),n1 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N2 to N3 via br-in1 using the L2 GRE tunnel between br-in2 and br-in1 and the L2 GRE tunnel between br-in1 and br-in3 AT_CHECK([ ovs-appctl netdev-dummy/receive n2 1e2ce92a669e461e7d1a95a1080045000054f7d440004001ad51c0a80a14c0a80a1e08000e760c1e000131519a580000000047ee0b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n2 1e2ce92a669e461e7d1a95a1080045000054f89540004001ac90c0a80a14c0a80a1e0800736f0c1e000232519a5800000000e1f30b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:01,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.1,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p2)),set(ipv4(src=10.0.0.2,dst=10.0.0.1)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=10.0.0.2,dst=10.0.0.1),in_port(gre_sys),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=30.0.0.1,dst=30.0.0.3),in_port(gre_sys),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:set(eth(dst=aa:55:aa:55:00:03)),n3 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N3 to N1 via br-in2 using the L3 GRE tunnel between br-in3 and br-in2 and the L3 GRE tunnel between br-in2 and br-in1 AT_CHECK([ ovs-appctl netdev-dummy/receive n3 3a6dd2099cab1e2ce92a669e080045000054b80440004001ed2bc0a80a1ec0a80a0a0800e17a77d5015e64509a5800000000d3d50c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n3 3a6dd2099cab1e2ce92a669e080045000054b8a240004001ec8dc0a80a1ec0a80a0a0800627177d5015f65509a580000000051de0c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.10,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:03,dl_type=0x0800),ipv4(src=30.0.0.3,dst=30.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br-p3)),set(ipv4(src=20.0.0.3,dst=20.0.0.2)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=10.0.0.2,dst=10.0.0.1),in_port(gre_sys),packet_type(ns=1,id=0x800),eth_type(0x0800),ipv4(dst=192.168.10.10,frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:01),n1 recirc_id(0),tunnel(src=20.0.0.3,dst=20.0.0.2),in_port(gre_sys),packet_type(ns=1,id=0x800),eth_type(0x0800),ipv4(dst=192.168.10.10,tos=0/0x3,frag=no), packets:1, bytes:84, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:01,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.1,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br-p2)),set(ipv4(src=10.0.0.2,dst=10.0.0.1)),tnl_pop(gre_sys) ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N3 to N2 via L3 GRE tunnel between br-in3 and br-in2 AT_CHECK([ ovs-appctl netdev-dummy/receive n3 461e7d1a95a11e2ce92a669e080045000054e5b540004001bf70c0a80a1ec0a80a140800b3f1065b000188509a580000000050360c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n3 461e7d1a95a11e2ce92a669e080045000054e5cf40004001bf56c0a80a1ec0a80a140800a2ed065b000289509a580000000060390c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:03,dl_type=0x0800),ipv4(src=30.0.0.3,dst=30.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x6558))),out_port(br-p3)),set(ipv4(src=20.0.0.3,dst=20.0.0.2)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=20.0.0.3,dst=20.0.0.2),in_port(gre_sys),packet_type(ns=0,id=0),eth(dst=46:1e:7d:1a:95:a1),eth_type(0x0800),ipv4(dst=192.168.10.20,frag=no), packets:1, bytes:98, used:0.0s, actions:set(eth(dst=aa:55:aa:55:00:02)),n2 ]) ### Check the received packets AT_CHECK([ ovs-pcap n1.pcap ], [0], [dnl aa55aa550001461e7d1a95a10800450000542c1f40004001791bc0a80a14c0a80a0a0800154b0b6800011b519a580000000054cf0e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550001461e7d1a95a10800450000542c744000400178c6c0a80a14c0a80a0a08003f420b6800021c519a580000000029d70e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550001000000000000080045000054b80440004001ed2bc0a80a1ec0a80a0a0800e17a77d5015e64509a5800000000d3d50c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550001000000000000080045000054b8a240004001ec8dc0a80a1ec0a80a0a0800627177d5015f65509a580000000051de0c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) AT_CHECK([ ovs-pcap n2.pcap ], [0], [dnl aa55aa5500023a6dd2099cab080045000054500b40004001552fc0a80a0ac0a80a140800531f09a90001e9509a580000000055ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa5500023a6dd2099cab08004500005450934000400154a7c0a80a0ac0a80a140800f41d09a90002ea509a5800000000b3ba030000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa5500021e2ce92a669e080045000054e5b540004001bf70c0a80a1ec0a80a140800b3f1065b000188509a580000000050360c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa5500021e2ce92a669e080045000054e5cf40004001bf56c0a80a1ec0a80a140800a2ed065b000289509a580000000060390c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) AT_CHECK([ ovs-pcap n3.pcap ], [0], [dnl aa55aa5500033a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa5500033a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550003461e7d1a95a1080045000054f7d440004001ad51c0a80a14c0a80a1e08000e760c1e000131519a580000000047ee0b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550003461e7d1a95a1080045000054f89540004001ac90c0a80a14c0a80a1e0800736f0c1e000232519a5800000000e1f30b0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) # N3 to N2, from L3 GRE to PTAP port between br-in3 and br-in2. Dropping L3 packet on L2 dummy port in br-in2. # Strips 'n_packets=...' from ovs-ofctl output. strip_n_packets () { sed 's/n_packets=[[0-9]]*, //' } # Strips 'n_bytes=...' from ovs-ofctl output. strip_n_bytes () { sed 's/n_bytes=[[0-9]]*, //' } # Modify flow rules to receive L3 packet in br-in2. AT_CHECK([ ovs-ofctl add-flow br-in2 packet_type=\(1,0x800\),nw_dst=$N2_IP,actions=$N2_OFPORT # Route L3 packet to N2 in br-in2 ovs-ofctl add-flow br-in3 ip,nw_dst=$N2_IP,actions=3021 # Route to N2 via the L3 tunnel ], [0]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | strip_n_bytes | strip_n_packets | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:2010 ip,nw_dst=192.168.10.20 actions=mod_dl_dst:aa:55:aa:55:00:02,output:20 ip,nw_dst=192.168.10.30 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.20 actions=output:20 packet_type=(1,0x800),nw_dst=192.168.10.30 actions=output:2030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | strip_n_bytes | strip_n_packets | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:3021 ip,nw_dst=192.168.10.20 actions=output:3021 ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) # Clear up megaflow cache ovs-appctl time/warp 11000 # N3 to N2 via L3 GRE tunnel between br-in3 and br-in2 AT_CHECK([ ovs-appctl netdev-dummy/receive n3 461e7d1a95a11e2ce92a669e080045000054e5b540004001bf70c0a80a1ec0a80a140800b3f1065b000188509a580000000050360c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n3 461e7d1a95a11e2ce92a669e080045000054e5cf40004001bf56c0a80a1ec0a80a140800a2ed065b000289509a580000000060390c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:03,dl_type=0x0800),ipv4(src=30.0.0.3,dst=30.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br-p3)),set(ipv4(src=20.0.0.3,dst=20.0.0.2)),tnl_pop(gre_sys) recirc_id(0),tunnel(src=20.0.0.3,dst=20.0.0.2),in_port(gre_sys),packet_type(ns=1,id=0x800),eth_type(0x0800),ipv4(dst=192.168.10.20,frag=no), packets:1, bytes:84, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"]) AT_CLEANUP AT_SETUP([ptap - check decap() prerequisits]) OVS_VSWITCHD_START # Decap IP header, then set IP destination address. This should fail. AT_CHECK([ ovs-ofctl add-flow br0 "in_port=1,packet_type=(1,0x800),actions=decap(),set_field:1.1.1.1->nw_dst" ], [1], [stdout], [stderr]) AT_CHECK([ cat stderr | cut -d '|' -f 3- ], [0], [dnl ofp_actions|WARN|set_field ip_dst lacks correct prerequisites ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT) ]) # Decap Ethernet header, then set IP destination address. This should work. AT_CHECK([ ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=1,ip,actions=decap(),set_field:1.1.1.1->nw_dst" ], [0]) # Decap IP header, then set metadata. This should work. AT_CHECK([ ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=1,packet_type=(1,0x800),actions=decap(),set_field:1234->metadata" ], [0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ptap - check encap/decap VLAN tagged Ethernet frame]) OVS_VSWITCHD_START([dnl -- add-port br0 n1 -- set interface n1 type=dummy ofport_request=1 \ -- add-port br0 n2 -- set interface n2 type=dummy ofport_request=2 \ -- add-port br0 p1 -- set interface p1 type=patch options:peer=p2 ofport_request=3 \ -- add-port br0 p2 -- set interface p2 type=patch options:peer=p1 ofport_request=4 ]) # Decap VLAN tagged Ethernet frames -> should be dropped AT_CHECK([ ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=1,actions=push_vlan:0x8100,mod_vlan_vid:100,decap(),3" ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=4,actions=encap(ethernet),2" ], [0]) AT_CHECK([ ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop ]) # Encap(ethernet) on Ethernet frame -> should be droped AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=1,actions=3" ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=4,actions=encap(ethernet),2" ]) ovs-appctl time/warp 11000 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop ]) # Encap(ethernet) on VLAN tagged Ethernet frame -> should be droped AT_CHECK([ ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=1,actions=push_vlan:0x8100,mod_vlan_vid:100,encap(ethernet),3" ovs-ofctl add-flow br0 -OOpenFlow13 "in_port=4,actions=2" ]) ovs-appctl time/warp 11000 AT_CHECK([ ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ptap - L3 over patch port]) ######################## # L3 over patch port # # (192.168.10.10) (192.168.10.30) # n0 n1 # | | # +--o------+ +--o------+ # | br0 | | br1 | # +------o--+ +--o---o--+ # p0 | p1 | gre1 (ptap) # +---------------+ 10.0.0.1 # # LOCAL # +------o--+ # | br2 | # +------o--+ # | # n2 # 10.0.0.2 HWADDR_BRP2=aa:55:00:00:00:02 OVS_VSWITCHD_START([dnl -- add-br br1 \ -- set bridge br1 datapath_type=dummy fail-mode=secure \ -- add-br br2 \ -- set bridge br2 datapath_type=dummy fail-mode=secure \ other_config:hwaddr=\"$HWADDR_BRP2\" \ -- add-port br0 p0 \ -- set interface p0 type=patch options:peer=p1 ofport_request=10 \ -- add-port br1 p1 \ -- set interface p1 type=patch options:peer=p0 ofport_request=20 \ -- add-port br0 n0 \ -- set interface n0 type=dummy ofport_request=30 \ -- add-port br1 n1 \ -- set interface n1 type=dummy options:tx_pcap=n1.pcap ofport_request=40 \ -- add-port br2 n2 \ -- set interface n2 type=dummy options:tx_pcap=n2.pcap ofport_request=50 \ -- add-port br1 gre1 \ -- set interface gre1 type=gre options:remote_ip=10.0.0.2 \ options:packet_type=ptap ofport_request=100 ]) ### Verify datapath configuration AT_CHECK([ ovs-appctl dpif/show | grep -v hit | sed 's./[[0-9]]\{1,\}..' ], [0], [dnl br0: br0 65534: (dummy-internal) n0 30: (dummy) p0 10/none: (patch: peer=p1) br1: br1 65534: (dummy-internal) gre1 100: (gre: packet_type=ptap, remote_ip=10.0.0.2) n1 40: (dummy) p1 20/none: (patch: peer=p0) br2: br2 65534: (dummy-internal) n2 50: (dummy) ]) AT_CHECK([ ovs-appctl netdev-dummy/ip4addr br2 10.0.0.1/24 && ovs-appctl tnl/arp/set br2 10.0.0.2 de:af:be:ef:ba:be ], [0], [ignore]) AT_CHECK([ ovs-appctl ovs/route/show | grep Cached: | sort ], [0], [dnl Cached: 10.0.0.0/24 dev br2 SRC 10.0.0.1 Cached: 10.0.0.1/32 dev br2 SRC 10.0.0.1 local ]) AT_CHECK([ ovs-ofctl del-flows br0 && ovs-ofctl del-flows br1 && ovs-ofctl del-flows br2 && ovs-ofctl add-flow br0 in_port=n0,actions=decap,output=p0 -OOpenFlow13 && ovs-ofctl add-flow br1 in_port=p1,actions=output=gre1 && ovs-ofctl add-flow br2 in_port=LOCAL,actions=output=n2 ], [0]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br0 | ofctl_strip | grep actions], [0], [dnl in_port=30 actions=decap(),output:10 ]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br1 | ofctl_strip | grep actions], [0], [dnl reset_counts in_port=20 actions=output:100 ]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br2 | ofctl_strip | grep actions], [0], [dnl reset_counts in_port=LOCAL actions=output:50 ]) AT_CHECK([ ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=de:af:be:ef:ba:be,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br2)),n2 ]) AT_CHECK([ ovs-pcap n2.pcap ], [0], [dnl deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) dnl output L3 to ports with different packet_type properties ovs-appctl time/warp 11000 ## L3 packet over L2 legacy port AT_CHECK([ ovs-ofctl del-flows br0 && ovs-ofctl del-flows br1 && ovs-ofctl del-flows br2 && ovs-ofctl add-flow br0 -OOpenFlow13 in_port=n0,actions=decap,output=p0 && ovs-ofctl add-flow br1 in_port=p1,actions=output=n1 ], [0]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br0 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=30 actions=decap(),output:10 ]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br1 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=20 actions=output:40 ]) AT_CHECK([ ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop ]) AT_CHECK([ ovs-pcap n1.pcap ], [0], [dnl ]) ## L3 packet over ptap and L2 legacy port AT_CHECK([ ovs-ofctl del-flows br0 && ovs-ofctl del-flows br1 && ovs-ofctl del-flows br2 && ovs-ofctl add-flow br0 in_port=n0,actions=decap,output=p0 -OOpenFlow13 && ovs-ofctl add-flow br1 in_port=p1,actions=output=n1,gre1 && ovs-ofctl add-flow br2 in_port=LOCAL,actions=output=n2 ], [0]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br0 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=30 actions=decap(),output:10 ]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br1 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=20 actions=output:40,output:100 ]) AT_CHECK([ovs-ofctl -OOpenFlow13 dump-flows br2 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=LOCAL actions=output:50 ]) AT_CHECK([ ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=de:af:be:ef:ba:be,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br2)),n2 ]) AT_CHECK([ ovs-pcap n1.pcap ], [0], [dnl ]) AT_CHECK([ ovs-pcap n2.pcap ], [0], [dnl deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) ## L2 packet over L3 legacy port and L2 legacy port ovs-appctl time/warp 11000 AT_CHECK([ ovs-vsctl set interface gre1 type=gre options:remote_ip=10.0.0.2 \ options:packet_type=legacy_l3 ofport_request=100 ], [0]) ### Verify datapath configuration AT_CHECK([ ovs-appctl dpif/show | grep -v hit | sed 's./[[0-9]]\{1,\}..' ], [0], [dnl br0: br0 65534: (dummy-internal) n0 30: (dummy) p0 10/none: (patch: peer=p1) br1: br1 65534: (dummy-internal) gre1 100: (gre: packet_type=legacy_l3, remote_ip=10.0.0.2) n1 40: (dummy) p1 20/none: (patch: peer=p0) br2: br2 65534: (dummy-internal) n2 50: (dummy) ]) AT_CHECK([ ovs-ofctl del-flows br0 && ovs-ofctl del-flows br1 && ovs-ofctl del-flows br2 && ovs-ofctl add-flow br0 in_port=n0,actions=output=p0 && ovs-ofctl add-flow br1 in_port=p1,actions=output=n1,gre1 && ovs-ofctl add-flow br2 in_port=LOCAL,actions=output=n2 ], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=30 actions=output:10 ]) AT_CHECK([ovs-ofctl dump-flows br1 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=20 actions=output:40,output:100 ]) AT_CHECK([ovs-ofctl dump-flows br2 | ofctl_strip | grep actions | sed 's/reset_counts //'], [0], [dnl in_port=LOCAL actions=output:50 ]) AT_CHECK([ ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n0 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:n1,pop_eth,tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=de:af:be:ef:ba:be,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br2)),n2 ]) AT_CHECK([ ovs-pcap n1.pcap ], [0], [dnl 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) AT_CHECK([ ovs-pcap n2.pcap ], [0], [dnl deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 deafbeefbabeaa550000000208004500006c00004000402f26610a0000010a00000200000800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ptap - recirculate after packet_type change]) ######################## # --<--+ # LOCAL | # +--o------+ # | int-br | # +------o--+ # L3 tunnel (remote : 20.0.0.2/24) # +--o------+ # | br0 | <- 20.0.0.1/24 # +------o--+ # p0 | # +--<-- OVS_VSWITCHD_START([dnl -- set bridge br0 other-config:hwaddr=aa:bb:cc:00:00:01 \ -- add-port br0 p0 \ -- set interface p0 type=dummy other_config:hwaddr=aa:bb:cc:00:00:01 ofport_request=1 \ -- add-br int-br -- set bridge int-br datapath_type=dummy \ -- add-port int-br tunnel \ -- set interface tunnel type=gre options:packet_type=legacy_l3 options:remote_ip=20.0.0.2 ofport_request=2 ]) ### Verify datapath configuration AT_CHECK([ ovs-appctl dpif/show | grep -v hit | sed 's./[[0-9]]\{1,\}..' ], [0], [dnl br0: br0 65534: (dummy-internal) p0 1: (dummy) int-br: int-br 65534: (dummy-internal) tunnel 2: (gre: packet_type=legacy_l3, remote_ip=20.0.0.2) ]) AT_CHECK([ ovs-appctl netdev-dummy/ip4addr br0 20.0.0.1/24 && ovs-appctl tnl/neigh/set br0 20.0.0.1 aa:bb:cc:00:00:01 && ovs-appctl tnl/neigh/set br0 20.0.0.2 aa:bb:cc:00:00:02 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl ovs/route/show | grep Cached: | sort ],[0], [dnl Cached: 20.0.0.0/24 dev br0 SRC 20.0.0.1 Cached: 20.0.0.1/32 dev br0 SRC 20.0.0.1 local ]) AT_CHECK([ ovs-appctl tnl/neigh/show | grep br | tr -s ' ' | sort ],[0], [dnl 20.0.0.1 aa:bb:cc:00:00:01 br0 20.0.0.2 aa:bb:cc:00:00:02 br0 ]) # Goto_table after pop_mpls triggers recirculation. AT_CHECK([ ovs-ofctl del-flows br0 && ovs-ofctl del-flows int-br && ovs-ofctl add-flow br0 "actions=normal" ovs-ofctl add-flow int-br "table=0,in_port=tunnel,actions=pop_mpls:0x800,goto_table:20" && ovs-ofctl add-flow int-br "table=20,actions=dec_ttl,output:LOCAL" ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-ofctl dump-flows br0 -OOpenFlow13 | ofctl_strip | grep actions | sed 's/reset_counts //' ], [0], [dnl actions=NORMAL ]) AT_CHECK([ ovs-ofctl dump-flows int-br -OOpenFlow13 | ofctl_strip | grep actions | sed 's/reset_counts //' ], [0], [dnl in_port=2 actions=pop_mpls:0x0800,resubmit(,20) table=20, actions=dec_ttl,LOCAL ]) # Receive MPLS over L3 GRE packet in underlay bridge. AT_CHECK([ ovs-appctl netdev-dummy/receive p0 aabbcc000001aabbcc00000208004500007000004000402f125d140000021400000100008847003e71404500005470f5400040013445c0a80a14c0a80a0a08004e1adbfc0001566b575a00000000604f010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p0 aabbcc000001aabbcc00000208004500007000004000402f125d140000021400000100008847003e71404500005470f5400040013445c0a80a14c0a80a0a08004e1adbfc0001566b575a00000000604f010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl netdev-dummy/receive p0 aabbcc000001aabbcc00000208004500007000004000402f125d140000021400000100008847003e71404500005470f5400040013445c0a80a14c0a80a0a08004e1adbfc0001566b575a00000000604f010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive p0 aabbcc000001aabbcc00000208004500007000004000402f125d140000021400000100008847003e71404500005470f5400040013445c0a80a14c0a80a0a08004e1adbfc0001566b575a00000000604f010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ], [0], [ignore]) ovs-appctl time/warp 1000 # After packet has been received on L3 port in overlay bridge, its packet_type changes to Ethernet. # Resubmit after pop_mpls recirculates the L2 packet. AT_CHECK([ ovs-appctl dpctl/dump-flows --names dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from the main thread: recirc_id(0),in_port(p0),packet_type(ns=0,id=0),eth(src=aa:bb:cc:00:00:02,dst=aa:bb:cc:00:00:01),eth_type(0x0800),ipv4(dst=20.0.0.1,proto=47,frag=no), packets:3, bytes:378, used:0.0s, actions:tnl_pop(gre_sys) recirc_id(0),tunnel(src=20.0.0.2,dst=20.0.0.1),in_port(gre_sys),packet_type(ns=1,id=0x8847),eth_type(0x8847),mpls(label=999/0x0,tc=0/0,ttl=64/0x0,bos=1/1), packets:3, bytes:264, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),pop_mpls(eth_type=0x800),recirc(0x1) recirc_id(0x1),tunnel(src=20.0.0.2,dst=20.0.0.1),in_port(gre_sys),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=1,ttl=64,frag=no), packets:3, bytes:294, used:0.0s, actions:set(ipv4(ttl=63)),int-br ]) ovs-appctl time/warp 1000 AT_CHECK([ ovs-ofctl dump-ports int-br LOCAL ovs-ofctl dump-ports int-br 2 ], [0], [OFPST_PORT reply (xid=0x2): 1 ports port LOCAL: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=4, bytes=392, drop=?, errs=?, coll=? OFPST_PORT reply (xid=0x2): 1 ports port 2: rx pkts=4, bytes=352, drop=?, errs=?, frame=?, over=?, crc=? tx pkts=0, bytes=0, drop=?, errs=?, coll=? ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/pmd.at000066400000000000000000002050751514270232600212410ustar00rootroot00000000000000AT_BANNER([PMD]) m4_divert_push([PREPARE_TESTS]) # Given the output of `ovs-appctl dpif-netdev/pmd-rxq-show`, prints a list # of every rxq (one per line) in the form: # port_name rxq_id numa_id core_id parse_pmd_rxq_show () { awk '/pmd thread/ {numa=$4; core=substr($6, 1, length($6) - 1)} /^ port:/ {print $2, $4, numa, core}' | sort } # Given the output of `ovs-appctl dpif-netdev/pmd-rxq-show`, # and with queues for each core on one line, prints the rxqs # of the core on one line # 'port:' port_name 'queue_id:' rxq_id rxq_id rxq_id rxq_id parse_pmd_rxq_show_group () { awk '/port:/ {print $1, $2, $3, $4, $13, $22, $31}' } # Given the output of `ovs-appctl dpctl/dump-flows`, prints a list of flows # (one per line), with the pmd_id at the beginning of the line # flow_dump_prepend_pmd () { awk '/flow-dump from the main/ {pmd_id=-1; next} /flow-dump from pmd/ {pmd_id=$7; next} {print pmd_id, $0}' | sort } m4_divert_pop([PREPARE_TESTS]) dnl CHECK_PMD_THREADS_CREATED([n_threads], [numa_id], [+line]) dnl dnl Whaits for creation of 'n_threads' or at least 1 thread if $1 not dnl passed. Checking starts from line number 'line' in ovs-vswithd.log . m4_define([CHECK_PMD_THREADS_CREATED], [ PATTERN="There are [[0-9]]* pmd threads on numa node $2" line_st=$3 if [[ -z "$line_st" ]] then line_st="+0" fi OVS_WAIT_UNTIL([tail -n $line_st ovs-vswitchd.log | grep "$PATTERN"]) N_THREADS=$(tail -n $line_st ovs-vswitchd.log | grep "$PATTERN" | tail -1 | sed -e 's/.* \([[0-9]]*\) pmd .*/\1/') if [[ -z "$1" ]] then AT_CHECK([test "$N_THREADS" -gt 0]) else AT_CHECK([test "$N_THREADS" -eq "$1"]) fi ]) dnl CHECK_DP_SLEEP_MAX([max_sleep], [+line]) dnl dnl Checks correct pmd load based sleep value for the datapath. dnl Checking starts from line number 'line' in ovs-vswithd.log . m4_define([CHECK_DP_SLEEP_MAX], [ SLEEP_TIME="Default PMD thread max sleep: *[$1] us." line_st=$2 if [[ -z "$line_st" ]] then line_st="+0" fi OVS_WAIT_UNTIL([tail -n $line_st ovs-vswitchd.log | grep "$SLEEP_TIME"]) ]) dnl CHECK_PMD_SLEEP_MAX([core_id], [numa_id], [max_sleep], [+line]) dnl dnl Checks max sleep time of each pmd with core_id. dnl Checking starts from line number 'line' in ovs-vswithd.log . m4_define([CHECK_PMD_SLEEP_MAX], [ PATTERN="PMD thread on numa_id: *[$1], core id: *[$2], max sleep: *[$3] us." line_st=$4 if [[ -z "$line_st" ]] then line_st="+0" fi OVS_WAIT_UNTIL([tail -n $line_st ovs-vswitchd.log | grep "$PATTERN"]) ]) m4_define([SED_NUMA_CORE_PATTERN], ["s/\(numa_id \)[[0-9]]*\( core_id \)[[0-9]]*:/\1\2:/"]) m4_define([DUMMY_NUMA], [--dummy-numa="0,0,0,0"]) AT_SETUP([PMD - creating a thread/add-port]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd], [], [], [DUMMY_NUMA]) CHECK_CPU_DISCOVERED() CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | sed SED_NUMA_CORE_PATTERN], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id core_id : isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy-pmd: n_rxq=1, n_txq=1, numa_id=0) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - multiqueue support]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd], [], [], [DUMMY_NUMA]) CHECK_CPU_DISCOVERED() CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-vsctl set interface p0 options:n_rxq=8]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy-pmd: n_rxq=8, n_txq=1, numa_id=0) ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | sed SED_NUMA_CORE_PATTERN], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id core_id : isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - pmd-cpu-mask/distribution of rx queues]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8], [], [], [DUMMY_NUMA]) CHECK_CPU_DISCOVERED(2) CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy-pmd: n_rxq=8, n_txq=1, numa_id=0) ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | sed SED_NUMA_CORE_PATTERN], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id core_id : isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x3]) CHECK_PMD_THREADS_CREATED([2], [], [+$TMP]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 2 4 6 port: p0 queue-id: 1 3 5 7 ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 3 4 7 port: p0 queue-id: 1 2 5 6 ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 2 4 6 port: p0 queue-id: 1 3 5 7 ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1]) CHECK_PMD_THREADS_CREATED([1], [], [+$TMP]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | sed SED_NUMA_CORE_PATTERN], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id core_id : isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - pmd-cpu-mask - dual NUMA]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8 options:numa_id=1 -- set Open_vSwitch . other_config:pmd-cpu-mask=1], [], [], [--dummy-numa 1,1,0,0]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) CHECK_CPU_DISCOVERED(4) CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy-pmd: n_rxq=8, n_txq=1, numa_id=1) ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | sed SED_NUMA_CORE_PATTERN], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id core_id : isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) # Force cross-numa polling TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xc]) CHECK_PMD_THREADS_CREATED([2], [0], [+$TMP]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 1. Port 'p0' rx queue 7 will be assigned to a pmd on numa node 0. This may lead to reduced performance."]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 3 4 7 port: p0 queue-id: 1 2 5 6 ]) # Check other assignment types for cross-numa polling TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 1. Port 'p0' rx queue 7 will be assigned to a pmd on numa node 0. This may lead to reduced performance."]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 2 4 6 port: p0 queue-id: 1 3 5 7 ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 1. Port 'p0' rx queue 7 will be assigned to a pmd on numa node 0. This may lead to reduced performance."]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 2 4 6 port: p0 queue-id: 1 3 5 7 ]) # Switch back to same numa TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x3]) CHECK_PMD_THREADS_CREATED([2], [1], [+$TMP]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | sort], [0], [dnl port: p0 queue-id: 0 2 4 6 port: p0 queue-id: 1 3 5 7 ]) # Check local numa is only used if available TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x6]) CHECK_PMD_THREADS_CREATED([1], [0], [+$TMP]) CHECK_PMD_THREADS_CREATED([1], [1], [+$TMP]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id 1 core_id 1: isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL pmd thread numa_id 0 core_id 2: isolated : false ]) # Check other assignment types TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id 1 core_id 1: isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL pmd thread numa_id 0 core_id 2: isolated : false ]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id 1 core_id 1: isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL pmd thread numa_id 0 core_id 2: isolated : false ]) # Switch back from mixed numa to single numa TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1]) CHECK_PMD_THREADS_CREATED([1], [1], [+$TMP]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show], [0], [dnl Displaying last 60 seconds pmd usage % pmd thread numa_id 1 core_id 0: isolated : false port: p0 queue-id: 0 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 1 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 2 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 3 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 4 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 5 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 6 (enabled) pmd usage: NOT AVAIL port: p0 queue-id: 7 (enabled) pmd usage: NOT AVAIL overhead: NOT AVAIL ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - pmd-cpu-mask - multi NUMA]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy-pmd options:n_rxq=4 \ -- set Interface p0 options:numa_id=0 \ -- set Open_vSwitch . other_config:pmd-cpu-mask=0xf \ -- set open_vswitch . other_config:pmd-rxq-assign=cycles], [], [], [--dummy-numa 1,2,1,2]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."]) # check all pmds from both non-local numas are assigned an rxq AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."]) # check all pmds from both non-local numas are assigned an rxq AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4]) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."]) # check all pmds from both non-local numas are assigned an rxq AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - stats]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 ofport_request=7 type=dummy-pmd options:n_rxq=4], [], [], [DUMMY_NUMA]) CHECK_CPU_DISCOVERED() CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:emc-insert-inv-prob=1]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:smc-enable=true]) sleep 1 AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 7/1: (dummy-pmd: n_rxq=4, n_txq=1, numa_id=0) ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 12], [0], [dnl pmd thread numa_id core_id : packets received: 0 packet recirculations: 0 avg. datapath passes per packet: 0.00 phwol hits: 0 mfex opt hits: 0 simple match hits: 0 emc hits: 0 smc hits: 0 megaflow hits: 0 avg. subtable lookups per megaflow hit: 0.00 miss with success upcall: 0 miss with failed upcall: 0 ]) ovs-appctl time/stop ovs-appctl time/warp 100 ( for i in `seq 0 19`; do pkt="in_port(7),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl netdev-dummy/receive p0 $pkt]) done ) ovs-appctl time/warp 100 AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl recirc_id(0),dp_hash(0),skb_priority(0),in_port(1),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0) ]) AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout], [0], [dnl recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(frag=no), actions: ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 12], [0], [dnl pmd thread numa_id core_id : packets received: 20 packet recirculations: 0 avg. datapath passes per packet: 1.00 phwol hits: 0 mfex opt hits: 0 simple match hits: 0 emc hits: 19 smc hits: 0 megaflow hits: 0 avg. subtable lookups per megaflow hit: 0.00 miss with success upcall: 1 miss with failed upcall: 0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - pmd-rxq-show pmd usage time]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd], [], [], [DUMMY_NUMA]) #CHECK_CPU_DISCOVERED() #CHECK_PMD_THREADS_CREATED() AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs -1 | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 0 | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 1 | grep Displaying], [0], [dnl Displaying last 5 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 5 | grep Displaying], [0], [dnl Displaying last 5 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 6 | grep Displaying], [0], [dnl Displaying last 10 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 51 | grep Displaying], [0], [dnl Displaying last 55 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 55 | grep Displaying], [0], [dnl Displaying last 55 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 56 | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 60 | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show -secs 61 | grep Displaying], [0], [dnl Displaying last 60 seconds pmd usage % ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl Reconfigure the number of rx queues of a port, make sure that all the dnl queues are polled by the datapath and try to send a couple of packets. AT_SETUP([PMD - reconfigure n_rxq]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=2 -- \ add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 ], [], [], [--dummy-numa 0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=controller]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p1 1 0 0 p2 0 0 0 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-vsctl set interface p1 options:n_rxq=4]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p1 1 0 0 p1 2 0 0 p1 3 0 0 p2 0 0 0 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 3 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) dnl Check resetting to default number of rx queues after removal from the db. AT_CHECK([ovs-vsctl remove interface p1 options n_rxq]) AT_CHECK([ovs-appctl dpif/show | grep p1], [0], [dnl p1 1/1: (dummy-pmd: n_rxq=1, n_txq=1, numa_id=0) ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl There was a bug where OVS failed to create a ukey and install a megaflow dnl if a packet with the exact same flow was received by two different pmd dnl threads. This is a regression test for that bug. AT_SETUP([PMD - same flow multiple threads]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=2 -- \ set Open_vSwitch . other_config:pmd-cpu-mask=3 ], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=controller]) dnl Make sure that the queues are on different cores. There's no way to dnl control which queue is on which thread, we just need to make sure that dnl two threads (core_id) show up in pmd-rxq-show AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show | cut -f 4 -d ' ' | sort], [0], [dnl 0 1 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Make sure that both flows have been installed AT_CHECK([ovs-appctl dpctl/dump-flows | flow_dump_prepend_pmd], [0], [dnl 0 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) 1 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - change numa node]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=2 -- \ add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 options:n_rxq=2 -- \ set Open_vSwitch . other_config:pmd-cpu-mask=7 ], [], [], [--dummy-numa 0,1,8]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=controller]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p1 1 0 0 p2 0 0 0 p2 1 0 0 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 --qid 1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-vsctl set Interface p2 options:numa_id=1]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p1 1 0 0 p2 0 1 1 p2 1 1 1 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-vsctl set Interface p1 options:numa_id=8]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 8 2 p1 1 8 2 p2 0 1 1 p2 1 1 1 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=2 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - non pmd device]) OVS_VSWITCHD_START( [add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=1 -- \ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \ set Interface br0 options:tx_pcap=br0.pcap -- \ set Open_vSwitch . other_config:pmd-cpu-mask=1 ], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `ovs-pcap br0.pcap | wc -l` -ge 2]) AT_CHECK([ovs-pcap br0.pcap], [0], [dnl 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f ]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=2]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 ]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) AT_CHECK([ovs-appctl netdev-dummy/receive p2 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `ovs-pcap br0.pcap | wc -l` -ge 4]) AT_CHECK([ovs-pcap br0.pcap], [0], [dnl 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f 50540000000a50540000000908004500005c000000004001669f0a0000020a000001080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - add remove ports]) OVS_VSWITCHD_START( [], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 actions=controller]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=1]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 ]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) AT_CHECK([ovs-vsctl del-port br0 p1]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=1]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 --qid 0 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=106 in_port=1 (via action) data_len=106 (unbuffered) icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0 icmp_csum:13fc ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - rxq affinity]) OVS_VSWITCHD_START( [], [], [], [--dummy-numa 0,0,0,0,0,0,0,0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 actions=controller]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1fe]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=4 other_config:pmd-rxq-affinity="0:3,1:7,2:2,3:8"]) dnl The rxqs should be on the requested cores. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 3 p1 1 0 7 p1 2 0 2 p1 3 0 8 ]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=6]) dnl We removed the cores requested by some queues from pmd-cpu-mask. dnl Those queues will be polled by remaining non-isolated pmds. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 1 p1 2 0 2 p1 3 0 1 ]) # Check they are pinned when those pmds are available again AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1fe]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 3 p1 1 0 7 p1 2 0 2 p1 3 0 8 ]) AT_CHECK([ovs-vsctl remove Interface p1 other_config pmd-rxq-affinity]) dnl We removed the rxq-affinity request. dpif-netdev should assign queues dnl in a round robin fashion. We just make sure that every rxq is being dnl polled again. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show | cut -f 1,2 -d ' ' | sort], [0], [dnl p1 0 p1 1 p1 2 p1 3 ]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=6]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity='0:1']) dnl We explicitly requested core 1 for queue 0. Core 1 becomes isolated and dnl every other queue goes to core 2. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 2 p1 2 0 2 p1 3 0 2 ]) OVS_VSWITCHD_STOP(["/cannot be pinned with port/d"]) AT_CLEANUP AT_SETUP([PMD - rxq affinity - non-isolate]) OVS_VSWITCHD_START( [], [], [], [--dummy-numa 0,0,0,0,0,0,0,0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 actions=controller]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x1fe]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=4 other_config:pmd-rxq-affinity="0:3,1:7,2:2,3:8"]) dnl The rxqs should be on the requested cores. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 3 p1 1 0 7 p1 2 0 2 p1 3 0 8 ]) # change rxq assignment algorithm AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) dnl The rxqs should be on the requested cores. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 3 p1 1 0 7 p1 2 0 2 p1 3 0 8 ]) # try to pin & non-isolate TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-isolate=false]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "pmd-rxq-affinity does not isolate PMD core"]) # should not impact - all rxqs are still pinned dnl The rxqs should be on the requested cores. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 3 p1 1 0 7 p1 2 0 2 p1 3 0 8 ]) # remove some pinning - see if non-isolate pmd are used for ovs rxq assignment of other rxqs AT_CHECK([ovs-vsctl remove Interface p1 other_config pmd-rxq-affinity]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity='0:1']) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=6]) dnl We explicitly requested core 1 for queue 0. Core 1 is not isolated so it is dnl use for other rxqs. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 2 p1 2 0 1 p1 3 0 2 ]) # change to algorithm that does not support pin & non-isolate TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "pmd-rxq-isolate can only be set false when using pmd-rxq-assign=group"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "pmd-rxq-affinity isolates PMD core"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"]) dnl We explicitly requested core 1 for queue 0. Core 1 becomes isolated and dnl every other queue goes to core 2. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 2 p1 2 0 2 p1 3 0 2 ]) # change rxq assignment algorithm to one that support pin & non-isolate TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "pmd-rxq-affinity does not isolate PMD core"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"]) #dnl We explicitly requested core 1 for queue 0. Core 1 becomes isolated and #dnl every other queue goes to core 2. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 2 p1 2 0 1 p1 3 0 2 ]) OVS_VSWITCHD_STOP(["/cannot be pinned with port/d /pmd-rxq-isolate can only be set false when using pmd-rxq-assign=group/d"]) AT_CLEANUP AT_SETUP([PMD - rxq affinity - NUMA]) OVS_VSWITCHD_START( [], [], [], [--dummy-numa 0,0,0,1,1,8,8]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-ofctl add-flow br0 actions=controller]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=7e]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=2 options:numa_id=0 other_config:pmd-rxq-affinity="0:1,1:2"]) dnl The rxqs should be on the requested cores. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 1 p1 1 0 2 ]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity="0:3,1:4"]) dnl We moved the queues to different contiguous numa node. Expecting threads on dnl NUMA node 1 to be created. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 1 3 p1 1 1 4 ]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity="0:5,1:6"]) dnl We moved the queues to different non-contiguous numa node. Expecting threads on dnl NUMA node 8 to be created. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 8 5 p1 1 8 6 ]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity="0:3,1:1"]) dnl Queues splitted between contiguous NUMA nodes. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 1 3 p1 1 0 1 ]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity="0:5,1:1"]) dnl Queues splitted between non-contiguous NUMA nodes. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 8 5 p1 1 0 1 ]) AT_CHECK([ovs-vsctl remove Interface p1 other_config pmd-rxq-affinity]) dnl We removed the rxq-affinity request. dpif-netdev should assign queues dnl in a round robin fashion. We just make sure that every rxq is being dnl polled again. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show | cut -f 1,2 -d ' ' | sort], [0], [dnl p1 0 p1 1 ]) AT_CHECK([ovs-vsctl set Interface p1 other_config:pmd-rxq-affinity='0:3']) dnl We explicitly requesting NUMA node 1 for queue 0. dnl Queue 1 should be polled by thread from NUMA node 0. AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show | cut -f 1,2,3 -d ' '], [0], [dnl p1 0 1 p1 1 0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - monitor threads]) OVS_VSWITCHD_START( [], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) dnl The two devices are connected together externally using net.sock AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd ofport_request=1 options:n_rxq=1 options:pstream=punix:$OVS_RUNDIR/net.sock]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=dummy-pmd ofport_request=2 options:n_rxq=1 options:stream=unix:$OVS_RUNDIR/net.sock]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p2 0 0 0 ]) dnl Enable bfd with a very aggressive interval. This will make the monitor very dnl busy, and uncover race conditions with the main thread. AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true bfd:min_rx=1 bfd:min_tx=1]) AT_CHECK([ovs-vsctl set Interface p2 bfd:enable=true bfd:min_rx=1 bfd:min_tx=1]) AT_CHECK([ovs-vsctl wait-until Interface p1 bfd_status:forwarding=true \ -- wait-until Interface p2 bfd_status:forwarding=true]) dnl Trigger reconfiguration of the datapath AT_CHECK([ovs-vsctl set Interface p1 options:n_rxq=2]) AT_CHECK([ovs-vsctl set Interface p2 options:n_rxq=2]) dnl Make sure that reconfiguration succeded AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 p1 1 0 0 p2 0 0 0 p2 1 0 0 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - dpctl]) OVS_VSWITCHD_START( [del-br br0], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg]) AT_CHECK([ovs-appctl dpctl/add-dp dummy@dp0]) AT_CHECK([ovs-appctl dpctl/add-if dummy@dp0 p1,type=dummy-pmd]) AT_CHECK([ovs-appctl dpctl/add-if dummy@dp0 p2,type=dummy]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show dp0 | parse_pmd_rxq_show], [0], [dnl p1 0 0 0 ]) AT_CHECK([ovs-appctl dpctl/show dummy@dp0], [0], [dnl dummy@dp0: lookups: hit:0 missed:0 lost:0 flows: 0 port 0: dp0 (dummy-internal) port 1: p1 (dummy-pmd: n_rxq=1, n_txq=1, numa_id=0) port 2: p2 (dummy) ]) AT_CHECK([ovs-appctl dpctl/add-flow dummy@dp0 'in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234)' 2], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@dp0 | sort], [0], [dnl flow-dump from pmd on cpu core: 0 flow-dump from the main thread: recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 ]) dnl Check pmd filtering option. AT_CHECK([ovs-appctl dpctl/dump-flows dummy@dp0 pmd=0], [0], [dnl recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@dp0 pmd=-1], [0], [dnl recirc_id(0),in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234), packets:0, bytes:0, used:never, actions:2 ]) AT_CHECK([ovs-appctl dpctl/del-flow dummy@dp0 'in_port(1),eth(src=00:00:00:00:00:01,dst=00:00:00:00:00:02),eth_type(0x1234)'], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-flows dummy@dp0], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/del-dp dummy@dp0], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - dpif configuration]) OVS_VSWITCHD_START([], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd]) AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_scalar], [0], [dnl DPIF implementation set to dpif_scalar. ]) AT_CHECK([ovs-vsctl show], [], [stdout]) AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-get | grep "dpif_scalar"], [], [dnl dpif_scalar (pmds: 0) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - dpcls configuration]) OVS_VSWITCHD_START([], [], [], [--dummy-numa 0,0]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy-pmd]) AT_CHECK([ovs-vsctl show], [], [stdout]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set autovalidator 3], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep autovalidator], [], [dnl autovalidator (Use count: 0, Priority: 3) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic 4], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep generic], [], [dnl generic (Use count: 0, Priority: 4) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic 8], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep generic], [], [dnl generic (Use count: 0, Priority: 8) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set autovalidator 8], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep autovalidator], [], [dnl autovalidator (Use count: 0, Priority: 8) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic 0], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep generic], [], [dnl generic (Use count: 0, Priority: 0) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic 255], [0], [dnl Lookup priority change affected 0 dpcls ports and 0 subtables. ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-info-get | grep generic], [], [dnl generic (Use count: 0, Priority: 255) ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic -1], [2], [], [dnl error converting priority, use integer in range 0-255 ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpif-netdev/subtable-lookup-prio-set generic 300], [2], [], [dnl error converting priority, use integer in range 0-255 ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - pmd sleep]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8 options:numa_id=1], [], [], [--dummy-numa 0,0,0,1,1,8,8]) dnl Check default CHECK_DP_SLEEP_MAX([0], []) CHECK_PMD_SLEEP_MAX([0], [0], [0], []) CHECK_PMD_SLEEP_MAX([1], [3], [0], []) CHECK_PMD_SLEEP_MAX([8], [5], [0], []) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 0 us pmd thread numa_id 8 core_id 5: max sleep: 0 us ]) dnl Check low value max sleep get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="1"]) CHECK_DP_SLEEP_MAX([1], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [1], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [1], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [1], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 1 us pmd thread numa_id 0 core_id 0: max sleep: 1 us pmd thread numa_id 1 core_id 3: max sleep: 1 us pmd thread numa_id 8 core_id 5: max sleep: 1 us ]) dnl Check high value max sleep get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="10000"]) CHECK_DP_SLEEP_MAX([10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [10000], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 10000 us pmd thread numa_id 0 core_id 0: max sleep: 10000 us pmd thread numa_id 1 core_id 3: max sleep: 10000 us pmd thread numa_id 8 core_id 5: max sleep: 10000 us ]) dnl Check setting max sleep to zero get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="0"]) CHECK_DP_SLEEP_MAX([0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [0], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 0 us pmd thread numa_id 8 core_id 5: max sleep: 0 us ]) dnl Check above high value max sleep get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="10001"]) CHECK_DP_SLEEP_MAX([10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [10000], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [10000], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 10000 us pmd thread numa_id 0 core_id 0: max sleep: 10000 us pmd thread numa_id 1 core_id 3: max sleep: 10000 us pmd thread numa_id 8 core_id 5: max sleep: 10000 us ]) dnl Check rounding get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="490"]) CHECK_DP_SLEEP_MAX([490], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [490], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [490], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [490], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 490 us pmd thread numa_id 0 core_id 0: max sleep: 490 us pmd thread numa_id 1 core_id 3: max sleep: 490 us pmd thread numa_id 8 core_id 5: max sleep: 490 us ]) dnl Check rounding get_log_next_line_num AT_CHECK([ovs-vsctl set open_vswitch . other_config:pmd-sleep-max="499"]) CHECK_DP_SLEEP_MAX([499], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [499], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [499], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [499], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 499 us pmd thread numa_id 0 core_id 0: max sleep: 499 us pmd thread numa_id 1 core_id 3: max sleep: 499 us pmd thread numa_id 8 core_id 5: max sleep: 499 us ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - per PMD sleep]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8 options:numa_id=1], [], [], [--dummy-numa 0,0,0,1,1,8,8]) dnl Check system default. CHECK_DP_SLEEP_MAX([0], []) CHECK_PMD_SLEEP_MAX([0], [0], [0], []) CHECK_PMD_SLEEP_MAX([1], [3], [0], []) CHECK_PMD_SLEEP_MAX([8], [5], [0], []) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 0 us pmd thread numa_id 8 core_id 5: max sleep: 0 us ]) dnl Only per PMD. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=3:300,0:100,5:400]) CHECK_DP_SLEEP_MAX([0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [100], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [300], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [400], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 100 us pmd thread numa_id 1 core_id 3: max sleep: 300 us pmd thread numa_id 8 core_id 5: max sleep: 400 us ]) dnl Mix of not used default and per-PMD. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=50,3:300,0:100,5:200]) CHECK_DP_SLEEP_MAX([50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [100], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [200], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [200], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 50 us pmd thread numa_id 0 core_id 0: max sleep: 100 us pmd thread numa_id 1 core_id 3: max sleep: 300 us pmd thread numa_id 8 core_id 5: max sleep: 200 us ]) dnl Remove a per-pmd entry and use default. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=50,3:300]) CHECK_DP_SLEEP_MAX([50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [300], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [50], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 50 us pmd thread numa_id 0 core_id 0: max sleep: 50 us pmd thread numa_id 1 core_id 3: max sleep: 300 us pmd thread numa_id 8 core_id 5: max sleep: 50 us ]) dnl Mix and change values. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=3:400,200]) CHECK_DP_SLEEP_MAX([200], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [200], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [400], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [200], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 200 us pmd thread numa_id 0 core_id 0: max sleep: 200 us pmd thread numa_id 1 core_id 3: max sleep: 400 us pmd thread numa_id 8 core_id 5: max sleep: 200 us ]) dnl Add values for pmds that don't exist yet. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=2:600,50,3:300,0:100,6:400,5:200]) CHECK_DP_SLEEP_MAX([50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [100], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [300], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [200], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 50 us pmd thread numa_id 0 core_id 0: max sleep: 100 us pmd thread numa_id 1 core_id 3: max sleep: 300 us pmd thread numa_id 8 core_id 5: max sleep: 200 us ]) dnl Add more cores. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=7f]) CHECK_PMD_SLEEP_MAX([0], [1], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [2], [600], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [4], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [6],[400], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 50 us pmd thread numa_id 0 core_id 0: max sleep: 100 us pmd thread numa_id 0 core_id 1: max sleep: 50 us pmd thread numa_id 0 core_id 2: max sleep: 600 us pmd thread numa_id 1 core_id 3: max sleep: 300 us pmd thread numa_id 1 core_id 4: max sleep: 50 us pmd thread numa_id 8 core_id 5: max sleep: 200 us pmd thread numa_id 8 core_id 6: max sleep: 400 us ]) dnl Go back to just a global value. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=90]) CHECK_DP_SLEEP_MAX([90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [1], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [2], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [4], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [90], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [6], [90], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 90 us pmd thread numa_id 0 core_id 0: max sleep: 90 us pmd thread numa_id 0 core_id 1: max sleep: 90 us pmd thread numa_id 0 core_id 2: max sleep: 90 us pmd thread numa_id 1 core_id 3: max sleep: 90 us pmd thread numa_id 1 core_id 4: max sleep: 90 us pmd thread numa_id 8 core_id 5: max sleep: 90 us pmd thread numa_id 8 core_id 6: max sleep: 90 us ]) dnl Try invalid value. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=qwe]) CHECK_DP_SLEEP_MAX([0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [1], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [2], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [4], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [6], [0], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 0 us pmd thread numa_id 0 core_id 1: max sleep: 0 us pmd thread numa_id 0 core_id 2: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 0 us pmd thread numa_id 1 core_id 4: max sleep: 0 us pmd thread numa_id 8 core_id 5: max sleep: 0 us pmd thread numa_id 8 core_id 6: max sleep: 0 us ]) dnl Try invalid key:value. get_log_next_line_num AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-sleep-max=50,1:qwe,2:0]) CHECK_DP_SLEEP_MAX([50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [1], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [2], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [4], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [50], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [6], [50], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 50 us pmd thread numa_id 0 core_id 0: max sleep: 50 us pmd thread numa_id 0 core_id 1: max sleep: 50 us pmd thread numa_id 0 core_id 2: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 50 us pmd thread numa_id 1 core_id 4: max sleep: 50 us pmd thread numa_id 8 core_id 5: max sleep: 50 us pmd thread numa_id 8 core_id 6: max sleep: 50 us ]) dnl Remove config. get_log_next_line_num AT_CHECK([ovs-vsctl remove Open_vSwitch . other_config pmd-sleep-max]) CHECK_DP_SLEEP_MAX([0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [0], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [1], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([0], [2], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [3], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([1], [4], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [5], [0], [+$LINENUM]) CHECK_PMD_SLEEP_MAX([8], [6], [0], [+$LINENUM]) AT_CHECK([ovs-appctl dpif-netdev/pmd-sleep-show], [0], [dnl Default max sleep: 0 us pmd thread numa_id 0 core_id 0: max sleep: 0 us pmd thread numa_id 0 core_id 1: max sleep: 0 us pmd thread numa_id 0 core_id 2: max sleep: 0 us pmd thread numa_id 1 core_id 3: max sleep: 0 us pmd thread numa_id 1 core_id 4: max sleep: 0 us pmd thread numa_id 8 core_id 5: max sleep: 0 us pmd thread numa_id 8 core_id 6: max sleep: 0 us ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([PMD - revalidator modify overlapping flows]) OVS_VSWITCHD_START( [add-port br0 p1 \ -- set bridge br0 datapath-type=dummy \ -- set interface p1 type=dummy-pmd \ -- add-port br0 p2 \ -- set interface p2 type=dummy-pmd ], [], [], [DUMMY_NUMA]) dnl Add one OpenFlow rule and generate a megaflow. AT_CHECK([ovs-ofctl add-flow br0 'table=0,in_port=p1,ip,nw_dst=10.1.2.0/24,actions=p2']) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.2.2,proto=6),tcp(src=1,dst=2)']) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-flows | sed 's/.*core: [[0-9]]*//'], [ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.2.2/255.255.255.0,frag=no), packets:0, bytes:0, used:never, actions:2]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.2.2,proto=6),tcp(src=1,dst=2)']) dnl Replace OpenFlow rules, trigger the revalidation. AT_CHECK([echo 'table=0,in_port=p1,ip,nw_dst=10.1.0.0/16 actions=ct(commit)' | dnl ovs-ofctl --bundle replace-flows br0 -]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Prevent flows from expiring. AT_CHECK([ovs-appctl time/stop]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.0.2,proto=6),tcp(src=1,dst=2)']) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-flows | sed 's/.*core: [[0-9]]*//' | strip_xout_keep_actions], [ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.0.2/255.255.0.0,frag=no), packets:0, bytes:0, used:never, actions:ct(commit) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.2.2/255.255.255.0,frag=no), packets:0, bytes:0, used:0.0s, actions:ct(commit)]) dnl Send more 10.1.0.2 to make 10.1.0.0/16 tuple prepend 10.1.2.0/24 tuple in the pvector of subtables. for i in $(seq 0 256); do AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.1.0.2,proto=6),tcp(src=1,dst=2)']) done dnl Warp time enough to trigger subtable optimization. AT_CHECK([ovs-appctl time/warp 500 2000], [0], [ignore]) AT_CHECK([echo 'table=0,in_port=p1,ip,nw_dst=10.1.0.0/16 actions=p2' | dnl ovs-ofctl --bundle replace-flows br0 -]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*core: [[0-9]]*//' | strip_xout_keep_actions], [0], [ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.0.2/255.255.0.0,frag=no), packets:0, bytes:0, used:0.0s, actions:2 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.2.2/255.255.255.0,frag=no), packets:0, bytes:0, used:0.0s, actions:2 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/pytest.at000066400000000000000000000003731514270232600220030ustar00rootroot00000000000000AT_BANNER([Python unit tests]) # Run pytest unit tests. AT_SETUP([Pytest unit tests - Python3]) AT_KEYWORDS([python]) AT_SKIP_IF([test "$HAVE_PYTEST" = "no"]) AT_CHECK([$PYTHON3 -m pytest $top_srcdir/python/ovs],[0], [ignore], [ignore]) AT_CLEANUP() openvswitch-3.7.0~git20260211.8c6ebf8/tests/reconnect.at000066400000000000000000000626061514270232600224420ustar00rootroot00000000000000AT_BANNER([reconnect library]) m4_define([__RECONNECT_CHECK], [AT_SETUP([$1]) AT_KEYWORDS([reconnect]) AT_DATA([input], [$2]) AT_CHECK([$3], [0], [$4]) AT_CLEANUP]) m4_define([RECONNECT_CHECK], [__RECONNECT_CHECK( [$1 - C], [$2], [ovstest test-reconnect < input], [$3]) __RECONNECT_CHECK( [$1 - Python3], [$2], [$PYTHON3 $srcdir/test-reconnect.py < input], [$3])]) ###################################################################### RECONNECT_CHECK([nothing happens if not enabled], [run timeout ], [### t=1000 ### run timeout no timeout ]) ###################################################################### RECONNECT_CHECK([quick connect, idle disconnect], [enable # Connection succeeds. run connected # Try timeout without noting that we tried to receive. # Timeout should be scheduled to the next probe interval. timeout run # Once we reached the timeout, it should not expire until the receive actually # attempted. However, we still need to wake up as soon as possible in order to # have a chance to mark the receive attempt even if nothing was received. timeout run # Short time advance past the original probe interval, but not expired still. timeout run # Now disable the receive-attempted feature and timeout again. receive-attempted LLONG_MAX timeout run # Idle timeout kills connection. timeout run disconnected ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # Connection succeeds. run should connect connected in ACTIVE for 0 ms (0 ms backoff) created 1000, last activity 1000, last connected 1000 1 successful connections out of 1 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Try timeout without noting that we tried to receive. # Timeout should be scheduled to the next probe interval. timeout advance 5000 ms ### t=6000 ### in ACTIVE for 5000 ms (0 ms backoff) run # Once we reached the timeout, it should not expire until the receive actually # attempted. However, we still need to wake up as soon as possible in order to # have a chance to mark the receive attempt even if nothing was received. timeout advance 1 ms ### t=6001 ### in ACTIVE for 5001 ms (0 ms backoff) run # Short time advance past the original probe interval, but not expired still. timeout advance 1 ms ### t=6002 ### in ACTIVE for 5002 ms (0 ms backoff) run # Now disable the receive-attempted feature and timeout again. receive-attempted LLONG_MAX timeout advance 0 ms run should send probe in IDLE for 0 ms (0 ms backoff) # Idle timeout kills connection. timeout advance 5000 ms ### t=11002 ### in IDLE for 5000 ms (0 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (1000 ms backoff) 1 successful connections out of 1 attempts, seqno 2 disconnected disconnected at 11002 ms (0 ms ago) ]) ###################################################################### RECONNECT_CHECK([slow connect, idle disconnect], [enable # Start connecting. run connecting # Connect after 500 ms. advance 500 run connected # Try timeout without noting that we tried to receive. # Timeout should be scheduled to the next probe interval. timeout run # Once we reached the timeout, it should not expire until the receive actually # attempted. However, we still need to wake up as soon as possible in order to # have a chance to mark the receive attempt even if nothing was received. timeout run # Short time advance past the original probe interval, but not expired still. timeout run # Now disable the receive-attempted feature and timeout again. receive-attempted LLONG_MAX timeout run # Idle timeout kills connection. timeout run disconnected ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # Start connecting. run should connect connecting in CONNECTING for 0 ms (0 ms backoff) # Connect after 500 ms. advance 500 ### t=1500 ### in CONNECTING for 500 ms (0 ms backoff) run connected in ACTIVE for 0 ms (0 ms backoff) created 1000, last activity 1000, last connected 1500 1 successful connections out of 1 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Try timeout without noting that we tried to receive. # Timeout should be scheduled to the next probe interval. timeout advance 5000 ms ### t=6500 ### in ACTIVE for 5000 ms (0 ms backoff) run # Once we reached the timeout, it should not expire until the receive actually # attempted. However, we still need to wake up as soon as possible in order to # have a chance to mark the receive attempt even if nothing was received. timeout advance 1 ms ### t=6501 ### in ACTIVE for 5001 ms (0 ms backoff) run # Short time advance past the original probe interval, but not expired still. timeout advance 1 ms ### t=6502 ### in ACTIVE for 5002 ms (0 ms backoff) run # Now disable the receive-attempted feature and timeout again. receive-attempted LLONG_MAX timeout advance 0 ms run should send probe in IDLE for 0 ms (0 ms backoff) # Idle timeout kills connection. timeout advance 5000 ms ### t=11502 ### in IDLE for 5000 ms (0 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (1000 ms backoff) 1 successful connections out of 1 attempts, seqno 2 disconnected disconnected at 11502 ms (0 ms ago) ]) ###################################################################### RECONNECT_CHECK([connect backs off], [enable # First connection attempt fails after 1000 ms. run connecting run timeout run connect-failed # Back off for 1000 ms. timeout run # Second connection attempt fails after 1000 ms. connecting timeout run connect-failed # Back off for 2000 ms. timeout run # Third connection attempt fails after 2000 ms. connecting timeout run connect-failed # Back off for 4000 ms. timeout run # Third connection attempt fails after 4000 ms. connecting timeout run connect-failed # Back off for 8000 ms. timeout run # Third connection attempt fails after 8000 ms. connecting timeout run connect-failed # Back off for 8000 ms. timeout run # Fourth connection attempt fails after 8000 ms. connecting timeout run connect-failed ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # First connection attempt fails after 1000 ms. run should connect connecting in CONNECTING for 0 ms (0 ms backoff) run timeout advance 1000 ms ### t=2000 ### in CONNECTING for 1000 ms (0 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 1 attempts, seqno 0 # Back off for 1000 ms. timeout advance 1000 ms ### t=3000 ### in BACKOFF for 1000 ms (1000 ms backoff) run should connect # Second connection attempt fails after 1000 ms. connecting in CONNECTING for 0 ms (1000 ms backoff) timeout advance 1000 ms ### t=4000 ### in CONNECTING for 1000 ms (1000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (2000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 2000 ms. timeout advance 2000 ms ### t=6000 ### in BACKOFF for 2000 ms (2000 ms backoff) run should connect # Third connection attempt fails after 2000 ms. connecting in CONNECTING for 0 ms (2000 ms backoff) timeout advance 2000 ms ### t=8000 ### in CONNECTING for 2000 ms (2000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (4000 ms backoff) 0 successful connections out of 3 attempts, seqno 0 # Back off for 4000 ms. timeout advance 4000 ms ### t=12000 ### in BACKOFF for 4000 ms (4000 ms backoff) run should connect # Third connection attempt fails after 4000 ms. connecting in CONNECTING for 0 ms (4000 ms backoff) timeout advance 4000 ms ### t=16000 ### in CONNECTING for 4000 ms (4000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (8000 ms backoff) 0 successful connections out of 4 attempts, seqno 0 # Back off for 8000 ms. timeout advance 8000 ms ### t=24000 ### in BACKOFF for 8000 ms (8000 ms backoff) run should connect # Third connection attempt fails after 8000 ms. connecting in CONNECTING for 0 ms (8000 ms backoff) timeout advance 8000 ms ### t=32000 ### in CONNECTING for 8000 ms (8000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (8000 ms backoff) 0 successful connections out of 5 attempts, seqno 0 # Back off for 8000 ms. timeout advance 8000 ms ### t=40000 ### in BACKOFF for 8000 ms (8000 ms backoff) run should connect # Fourth connection attempt fails after 8000 ms. connecting in CONNECTING for 0 ms (8000 ms backoff) timeout advance 8000 ms ### t=48000 ### in CONNECTING for 8000 ms (8000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (8000 ms backoff) 0 successful connections out of 6 attempts, seqno 0 ]) ###################################################################### RECONNECT_CHECK([connections with no data preserve backoff], [receive-attempted LLONG_MAX enable # First connect, then idle timeout kills connection. run connected timeout run timeout run disconnected # Back off for 1000 ms. timeout run # Second connect, then idle timeout kills connection. run connected timeout run timeout run disconnected # Back off for 2000 ms. timeout run # Third connect, then idle timeout kills connection. run connected timeout run timeout run disconnected # Back off for 4000 ms. timeout ], [### t=1000 ### receive-attempted LLONG_MAX enable in BACKOFF for 0 ms (0 ms backoff) # First connect, then idle timeout kills connection. run should connect connected in ACTIVE for 0 ms (0 ms backoff) created 1000, last activity 1000, last connected 1000 1 successful connections out of 1 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total timeout advance 5000 ms ### t=6000 ### in ACTIVE for 5000 ms (0 ms backoff) run should send probe in IDLE for 0 ms (0 ms backoff) timeout advance 5000 ms ### t=11000 ### in IDLE for 5000 ms (0 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (1000 ms backoff) 1 successful connections out of 1 attempts, seqno 2 disconnected disconnected at 11000 ms (0 ms ago) # Back off for 1000 ms. timeout advance 1000 ms ### t=12000 ### in BACKOFF for 1000 ms (1000 ms backoff) last connected 11000 ms ago, connected 10000 ms total run should connect # Second connect, then idle timeout kills connection. run should connect connected in ACTIVE for 0 ms (1000 ms backoff) created 1000, last activity 1000, last connected 12000 2 successful connections out of 2 attempts, seqno 3 connected last connected 0 ms ago, connected 10000 ms total timeout advance 5000 ms ### t=17000 ### in ACTIVE for 5000 ms (1000 ms backoff) run should send probe in IDLE for 0 ms (1000 ms backoff) timeout advance 5000 ms ### t=22000 ### in IDLE for 5000 ms (1000 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (2000 ms backoff) 2 successful connections out of 2 attempts, seqno 4 disconnected disconnected at 22000 ms (0 ms ago) # Back off for 2000 ms. timeout advance 2000 ms ### t=24000 ### in BACKOFF for 2000 ms (2000 ms backoff) last connected 12000 ms ago, connected 20000 ms total run should connect # Third connect, then idle timeout kills connection. run should connect connected in ACTIVE for 0 ms (2000 ms backoff) created 1000, last activity 1000, last connected 24000 3 successful connections out of 3 attempts, seqno 5 connected last connected 0 ms ago, connected 20000 ms total timeout advance 5000 ms ### t=29000 ### in ACTIVE for 5000 ms (2000 ms backoff) run should send probe in IDLE for 0 ms (2000 ms backoff) timeout advance 5000 ms ### t=34000 ### in IDLE for 5000 ms (2000 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (4000 ms backoff) 3 successful connections out of 3 attempts, seqno 6 disconnected disconnected at 34000 ms (0 ms ago) # Back off for 4000 ms. timeout advance 4000 ms ### t=38000 ### in BACKOFF for 4000 ms (4000 ms backoff) last connected 14000 ms ago, connected 30000 ms total ]) ###################################################################### RECONNECT_CHECK([brief connection preserves backoff], [enable # First connection attempt fails after 1000 ms. run connecting run timeout run connect-failed # Back off for 1000 ms. timeout run # Second connection attempt fails after 1000 ms. connecting timeout run connect-failed # Back off for 2000 ms. timeout run # Third connection attempt succeeds after 500 ms. connecting advance 500 run connected # Connection drops after another 250 ms. advance 250 disconnected run # Back off for 4000 ms. timeout run ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # First connection attempt fails after 1000 ms. run should connect connecting in CONNECTING for 0 ms (0 ms backoff) run timeout advance 1000 ms ### t=2000 ### in CONNECTING for 1000 ms (0 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 1 attempts, seqno 0 # Back off for 1000 ms. timeout advance 1000 ms ### t=3000 ### in BACKOFF for 1000 ms (1000 ms backoff) run should connect # Second connection attempt fails after 1000 ms. connecting in CONNECTING for 0 ms (1000 ms backoff) timeout advance 1000 ms ### t=4000 ### in CONNECTING for 1000 ms (1000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (2000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 2000 ms. timeout advance 2000 ms ### t=6000 ### in BACKOFF for 2000 ms (2000 ms backoff) run should connect # Third connection attempt succeeds after 500 ms. connecting in CONNECTING for 0 ms (2000 ms backoff) advance 500 ### t=6500 ### in CONNECTING for 500 ms (2000 ms backoff) run connected in ACTIVE for 0 ms (2000 ms backoff) created 1000, last activity 1000, last connected 6500 1 successful connections out of 3 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Connection drops after another 250 ms. advance 250 ### t=6750 ### in ACTIVE for 250 ms (2000 ms backoff) disconnected in BACKOFF for 0 ms (4000 ms backoff) 1 successful connections out of 3 attempts, seqno 2 disconnected disconnected at 6750 ms (0 ms ago) run # Back off for 4000 ms. timeout advance 4000 ms ### t=10750 ### in BACKOFF for 4000 ms (4000 ms backoff) last connected 4250 ms ago, connected 250 ms total run should connect ]) ###################################################################### RECONNECT_CHECK([brief connection with data preserves backoff], [enable # First connection attempt fails after 1000 ms. run connecting run timeout run connect-failed # Back off for 1000 ms. timeout run # Second connection attempt fails after 1000 ms. connecting timeout run connect-failed # Back off for 2000 ms. timeout run # Third connection attempt succeeds after 500 ms. connecting advance 500 run connected # Connection receives 3 chunks of data spaced 250 ms apart. advance 250 run activity advance 250 run activity advance 250 run activity # Connection drops. disconnected run # Back off for 4000 ms. timeout run ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # First connection attempt fails after 1000 ms. run should connect connecting in CONNECTING for 0 ms (0 ms backoff) run timeout advance 1000 ms ### t=2000 ### in CONNECTING for 1000 ms (0 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 1 attempts, seqno 0 # Back off for 1000 ms. timeout advance 1000 ms ### t=3000 ### in BACKOFF for 1000 ms (1000 ms backoff) run should connect # Second connection attempt fails after 1000 ms. connecting in CONNECTING for 0 ms (1000 ms backoff) timeout advance 1000 ms ### t=4000 ### in CONNECTING for 1000 ms (1000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (2000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 2000 ms. timeout advance 2000 ms ### t=6000 ### in BACKOFF for 2000 ms (2000 ms backoff) run should connect # Third connection attempt succeeds after 500 ms. connecting in CONNECTING for 0 ms (2000 ms backoff) advance 500 ### t=6500 ### in CONNECTING for 500 ms (2000 ms backoff) run connected in ACTIVE for 0 ms (2000 ms backoff) created 1000, last activity 1000, last connected 6500 1 successful connections out of 3 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Connection receives 3 chunks of data spaced 250 ms apart. advance 250 ### t=6750 ### in ACTIVE for 250 ms (2000 ms backoff) run activity created 1000, last activity 6750, last connected 6500 advance 250 ### t=7000 ### in ACTIVE for 500 ms (2000 ms backoff) run activity created 1000, last activity 7000, last connected 6500 advance 250 ### t=7250 ### in ACTIVE for 750 ms (2000 ms backoff) run activity created 1000, last activity 7250, last connected 6500 # Connection drops. disconnected in BACKOFF for 0 ms (4000 ms backoff) 1 successful connections out of 3 attempts, seqno 2 disconnected disconnected at 7250 ms (0 ms ago) run # Back off for 4000 ms. timeout advance 4000 ms ### t=11250 ### in BACKOFF for 4000 ms (4000 ms backoff) last connected 4750 ms ago, connected 750 ms total run should connect ]) ###################################################################### RECONNECT_CHECK([long connection resets backoff], [enable # First connection attempt fails after 1000 ms. run connecting run timeout run connect-failed # Back off for 1000 ms. timeout run # Second connection attempt fails after 1000 ms. connecting timeout run connect-failed # Back off for 2000 ms. timeout run # Third connection attempt succeeds after 500 ms. connecting advance 500 run connected # Connection receives 3 chunks of data spaced 2000 ms apart. advance 2000 run activity advance 2000 run activity advance 2000 run activity # Connection drops. disconnected run # Back off for 1000 ms. timeout run ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # First connection attempt fails after 1000 ms. run should connect connecting in CONNECTING for 0 ms (0 ms backoff) run timeout advance 1000 ms ### t=2000 ### in CONNECTING for 1000 ms (0 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 1 attempts, seqno 0 # Back off for 1000 ms. timeout advance 1000 ms ### t=3000 ### in BACKOFF for 1000 ms (1000 ms backoff) run should connect # Second connection attempt fails after 1000 ms. connecting in CONNECTING for 0 ms (1000 ms backoff) timeout advance 1000 ms ### t=4000 ### in CONNECTING for 1000 ms (1000 ms backoff) run should disconnect connect-failed in BACKOFF for 0 ms (2000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 2000 ms. timeout advance 2000 ms ### t=6000 ### in BACKOFF for 2000 ms (2000 ms backoff) run should connect # Third connection attempt succeeds after 500 ms. connecting in CONNECTING for 0 ms (2000 ms backoff) advance 500 ### t=6500 ### in CONNECTING for 500 ms (2000 ms backoff) run connected in ACTIVE for 0 ms (2000 ms backoff) created 1000, last activity 1000, last connected 6500 1 successful connections out of 3 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Connection receives 3 chunks of data spaced 2000 ms apart. advance 2000 ### t=8500 ### in ACTIVE for 2000 ms (2000 ms backoff) run activity created 1000, last activity 8500, last connected 6500 advance 2000 ### t=10500 ### in ACTIVE for 4000 ms (2000 ms backoff) run activity created 1000, last activity 10500, last connected 6500 advance 2000 ### t=12500 ### in ACTIVE for 6000 ms (2000 ms backoff) run activity created 1000, last activity 12500, last connected 6500 # Connection drops. disconnected in BACKOFF for 0 ms (1000 ms backoff) 1 successful connections out of 3 attempts, seqno 2 disconnected disconnected at 12500 ms (0 ms ago) run # Back off for 1000 ms. timeout advance 1000 ms ### t=13500 ### in BACKOFF for 1000 ms (1000 ms backoff) last connected 7000 ms ago, connected 6000 ms total run should connect ]) ###################################################################### RECONNECT_CHECK([connection attempt fails quickly], [enable # Connection fails quickly. run connect-failed ECONNREFUSED # Back off for 1000 ms. run timeout # Connection fails quickly again. run connect-failed ECONNREFUSED # Back off for 2000 ms. run timeout ], [### t=1000 ### enable in BACKOFF for 0 ms (0 ms backoff) # Connection fails quickly. run should connect connect-failed ECONNREFUSED in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 1 attempts, seqno 0 # Back off for 1000 ms. run timeout advance 1000 ms ### t=2000 ### in BACKOFF for 1000 ms (1000 ms backoff) # Connection fails quickly again. run should connect connect-failed ECONNREFUSED in BACKOFF for 0 ms (2000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 2000 ms. run timeout advance 2000 ms ### t=4000 ### in BACKOFF for 2000 ms (2000 ms backoff) ]) ###################################################################### RECONNECT_CHECK([backoff-free tries work], [set-backoff-free-tries 2 enable # Connection fails quickly. run connect-failed ECONNREFUSED # No backoff. run timeout # Connection fails quickly again. run connect-failed ECONNREFUSED # Back off for 1000 ms. run timeout ], [### t=1000 ### set-backoff-free-tries 2 enable in BACKOFF for 0 ms (0 ms backoff) # Connection fails quickly. run should connect connect-failed ECONNREFUSED 0 successful connections out of 1 attempts, seqno 0 # No backoff. run should connect timeout advance 0 ms # Connection fails quickly again. run should connect connect-failed ECONNREFUSED in BACKOFF for 0 ms (1000 ms backoff) 0 successful connections out of 2 attempts, seqno 0 # Back off for 1000 ms. run timeout advance 1000 ms ### t=2000 ### in BACKOFF for 1000 ms (1000 ms backoff) ]) ###################################################################### RECONNECT_CHECK([max-tries of 1 honored], [receive-attempted LLONG_MAX set-max-tries 1 enable # Connection succeeds. run connected # Send inactivity probe. timeout run # Idle timeout kills connection. timeout run disconnected ], [### t=1000 ### receive-attempted LLONG_MAX set-max-tries 1 1 tries left enable in BACKOFF for 0 ms (0 ms backoff) 0 tries left # Connection succeeds. run should connect connected in ACTIVE for 0 ms (0 ms backoff) created 1000, last activity 1000, last connected 1000 1 successful connections out of 1 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total # Send inactivity probe. timeout advance 5000 ms ### t=6000 ### in ACTIVE for 5000 ms (0 ms backoff) run should send probe in IDLE for 0 ms (0 ms backoff) # Idle timeout kills connection. timeout advance 5000 ms ### t=11000 ### in IDLE for 5000 ms (0 ms backoff) run should disconnect disconnected in VOID for 0 ms (0 ms backoff) 1 successful connections out of 1 attempts, seqno 2 disconnected disconnected at 11000 ms (0 ms ago) ]) ###################################################################### RECONNECT_CHECK([max-tries of 0 honored], [set-max-tries 0 enable run timeout ], [### t=1000 ### set-max-tries 0 0 tries left enable run timeout no timeout ]) ###################################################################### RECONNECT_CHECK([passive mode], [passive enable # Start listening. timeout run listening # Listening never times out. timeout run # Listening failed (accept() returned funny error?). Back off and try again. listen-error 0 timeout run listening # Connection accepted. connected activity advance 1000 activity # Connection times out. timeout receive-attempted LLONG_MAX timeout run timeout run disconnected # Start listening again. timeout run listening ], [### t=1000 ### passive enable in BACKOFF for 0 ms (0 ms backoff) # Start listening. timeout advance 0 ms run should connect listening in LISTENING for 0 ms (0 ms backoff) # Listening never times out. timeout no timeout run # Listening failed (accept() returned funny error?). Back off and try again. listen-error 0 in BACKOFF for 0 ms (1000 ms backoff) timeout advance 1000 ms ### t=2000 ### in BACKOFF for 1000 ms (1000 ms backoff) run should connect listening in LISTENING for 0 ms (1000 ms backoff) # Connection accepted. connected in ACTIVE for 0 ms (1000 ms backoff) created 1000, last activity 1000, last connected 2000 1 successful connections out of 1 attempts, seqno 1 connected last connected 0 ms ago, connected 0 ms total activity created 1000, last activity 2000, last connected 2000 advance 1000 ### t=3000 ### in ACTIVE for 1000 ms (1000 ms backoff) activity created 1000, last activity 3000, last connected 2000 # Connection times out. timeout advance 5000 ms ### t=8000 ### in ACTIVE for 6000 ms (1000 ms backoff) receive-attempted LLONG_MAX timeout advance 0 ms run should send probe in IDLE for 0 ms (1000 ms backoff) timeout advance 5000 ms ### t=13000 ### in IDLE for 5000 ms (1000 ms backoff) run should disconnect disconnected in BACKOFF for 0 ms (0 ms backoff) 1 successful connections out of 1 attempts, seqno 2 disconnected disconnected at 13000 ms (0 ms ago) # Start listening again. timeout advance 0 ms run should connect listening in LISTENING for 0 ms (0 ms backoff) ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/rstp.at000066400000000000000000000227361514270232600214520ustar00rootroot00000000000000AT_BANNER([Rapid Spanning Tree Protocol unit tests]) AT_SETUP([RSTP Single bridge]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-num1], [bridge 0 0x111 = a b run 1000 check 0 = root ]) AT_CHECK([ovstest test-rstp test-rstp-num1], [0], []) AT_CLEANUP AT_SETUP([RSTP Link failure]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-num2], [bridge 0 0x111 = a b bridge 1 0x222 = a c bridge 2 0x333 = b c run 1000 check 0 = root check 1 = F:200000 F check 2 = F:200000 Di # Link b goes down bridge 2 = X c run 1000 check 1 = F:200000 F check 2 = D F:400000 ]) AT_CHECK([ovstest test-rstp test-rstp-num2], [0], []) AT_CLEANUP AT_SETUP([RSTP Double link Failure]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-num3], [bridge 0 0x111 = a b bridge 1 0x222 = a c d bridge 2 0x333 = b c e bridge 3 0x444 = d f bridge 4 0x555 = e f run 1000 check 0 = root check 1 = F:200000 F F check 2 = F:200000 Di F check 3 = F:400000 F check 4 = F:400000 Di # Link b goes down bridge 2 = X c e run 1000 check 0 = root check 1 = F:200000 F F check 2 = D F:400000 F check 3 = F:400000 F check 4 = F:600000 Di # Link e goes down bridge 4 = X f run 1000 check 0 = root check 1 = F:200000 F F check 2 = D F:400000 F check 3 = F:400000 F check 4 = D F:600000 # Link f cost changes bridge 4 = X f:100000 run 1000 check 4 = D F:500000 # Bridge 4 becomes root and bridge 4 ^ 31000 run 1000 check 4 = root ]) AT_CHECK([ovstest test-rstp test-rstp-num3], [0], []) AT_CLEANUP AT_SETUP([RSTP example from IEEE 802.1D-2004 figures 17.4 and 17.5]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-ieee802.1d-2004-fig17.4], [bridge 0 0x111 = a b e c bridge 1 0x222 = a b d f bridge 2 0x333 = c d l j h g bridge 3 0x444 = e f n m k i bridge 4 0x555 = g i 0 0 bridge 5 0x666 = h k 0 0 bridge 6 0x777 = j m 0 0 bridge 7 0x888 = l n 0 0 run 1000 check 0 = root check 1 = F:200000 Di F F check 2 = F:200000 Di F F F F check 3 = F:200000 Di F F F F check 4 = F:400000 Di F F check 5 = F:400000 Di F F check 6 = F:400000 Di F F check 7 = F:400000 Di F F # Now connect two ports of bridge 7 to the same LAN. bridge 7 = l n o o # Same results except for bridge 7: run 1000 check 0 = root check 1 = F:200000 Di F F check 2 = F:200000 Di F F F F check 3 = F:200000 Di F F F F check 4 = F:400000 Di F F check 5 = F:400000 Di F F check 6 = F:400000 Di F F check 7 = F:400000 Di F Di ]) AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.4], [0], []) AT_CLEANUP AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.6]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-ieee802.1d-2004-fig17.6], [bridge 0 0x111 = a b l bridge 1 0x222 = b c d bridge 2 0x333 = d e f bridge 3 0x444 = f g h bridge 4 0x555 = j h i bridge 5 0x666 = l j k run 1000 check 0 = root check 1 = F:200000 F F check 2 = F:400000 F F check 3 = F:600000 F Di check 4 = F:400000 F F check 5 = F:200000 F F ]) AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.6], [0], []) AT_CLEANUP AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.7]) AT_KEYWORDS([RSTP]) AT_DATA([test-rstp-ieee802.1d-2004-fig17.7], [bridge 0 0x000 = b bridge 1 0x111 = a b d f h g e c bridge 2 0x222 = g h j l n m k i run 1000 check 0 = root check 1 = F F:200000 F F F F F F check 2 = Di F:400000 F F F F F F # Link g priority increment bridge 1 = a b d f h g^112 e c run 1000 check 0 = root check 1 = F F:200000 F F F F F F check 2 = F:400000 Di F F F F F F ]) AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.7], [0], []) AT_CLEANUP m4_define([FILTER_STP_TOPOLOGY], [[ grep 'STP state changed' | sed ' s/.*ofproto_dpif|.*|// ']]) AT_SETUP([RSTP - dummy interface]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [set port br0 other_config:rstp-enable=false -- \ set bridge br0 rstp_enable=true -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ set port br1 other_config:rstp-enable=false -- \ set bridge br1 rstp_enable=false -- \ ]) AT_CHECK([ovs-vsctl add-port br0 p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \ set port p1 other_config:rstp-enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br0 p7 -- \ set interface p7 ofport_request=7 type=dummy -- \ set port p7 other_config:rstp-enable=false -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p2 -- \ set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \ set port p2 other_config:rstp-enable=false -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p8 -- \ set interface p8 ofport_request=8 type=dummy -- \ set port p8 other_config:rstp-enable=false -- \ ]) # # RSTP needs link to be in admin-state up, netdev-dummy is by default down # AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p1 up], [], [dnl OK ]) # # Turn RSTP on in br1 after the ports have been added. # AT_CHECK([ovs-vsctl set bridge br1 rstp_enable=true]) ovs-appctl time/stop AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"]) AT_CHECK([ovs-vsctl set port p2 other_config:rstp-enable=true]) OVS_WAIT_UNTIL([test `cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY | wc -l` -ge 2]) AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl port p1: RSTP state changed from Disabled to Discarding port p2: RSTP state changed from Disabled to Discarding ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl >> RSTP not in forwarding state, skipping output ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl >> RSTP not in forwarding state, skipping output ]) # # RSTP needs link to be in admin-state up, netdev-dummy is by default down # AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 up], [], [dnl OK ]) # give time for RSTP to move initially ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 OVS_WAIT_UNTIL([test `cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY | wc -l` -ge 4]) AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl port p1: RSTP state changed from Disabled to Discarding port p2: RSTP state changed from Disabled to Discarding port p2: RSTP state changed from Discarding to Forwarding port p1: RSTP state changed from Discarding to Forwarding ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 2 ]) AT_CHECK([ovs-vsctl del-br br1]) AT_CHECK([ovs-vsctl del-port br0 p7]) AT_CHECK([ovs-vsctl del-port br0 p1]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([RSTP - patch ports]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 being connected patch ports. OVS_VSWITCHD_START( [set port br0 other_config:rstp-enable=false -- \ set bridge br0 rstp-enable=true ]) AT_CHECK([add_of_br 1 \ set port br1 other_config:rstp-enable=false -- \ set bridge br1 rstp-enable=true]) ovs-appctl time/stop AT_CHECK([ovs-vsctl \ add-port br0 p1 -- \ set interface p1 type=patch options:peer=p2 ofport_request=1 -- \ set port p1 other_config:rstp-enable=true -- \ add-port br1 p2 -- \ set interface p2 type=patch options:peer=p1 ofport_request=2 -- \ set port p2 other_config:rstp-enable=true -- \ ]) AT_CHECK([ovs-vsctl \ add-port br0 p7 -- \ set interface p7 ofport_request=7 type=dummy -- \ set port p7 other_config:rstp-enable=false -- \ add-port br1 p8 -- \ set interface p8 ofport_request=8 type=dummy -- \ set port p8 other_config:rstp-enable=false -- \ ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"]) # Give time for RSTP to synchronize. ovs-appctl time/warp 5000 500 OVS_WAIT_UNTIL_EQUAL([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [dnl port p1: RSTP state changed from Disabled to Discarding port p2: RSTP state changed from Disabled to Discarding port p2: RSTP state changed from Discarding to Forwarding port p1: RSTP state changed from Discarding to Forwarding]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 8 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 7 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/run-oftest000077500000000000000000000047431514270232600221660ustar00rootroot00000000000000#! /bin/sh set -e run () { echo "$@" "$@" || exit 1 } # Put built tools early in $PATH. builddir=`pwd` if test ! -e vswitchd/ovs-vswitchd; then echo >&2 'not in build directory, please change directory or run via \"make check-oftest' exit 1 fi PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH; export PATH # Find srcdir. case $srcdir in '') srcdir=$builddir ;; /*) ;; *) srcdir=`pwd`/$srcdir ;; esac if test ! -e "$srcdir"/README.rst; then echo >&2 'source directory not found, please set $srcdir or run via \"make check-oftest' exit 1 fi # Make sure oftest is available. if test X"$OFT" = X; then OFT=oft fi if ($OFT --version) >/dev/null 2>&1; then : else echo >&2 'OFTest "oft" binary not found or cannot be run, please add to $PATH or set $OFT' exit 1 fi # Create sandbox. rm -rf sandbox mkdir sandbox cd sandbox sandbox=`pwd` # Set up environment for OVS programs to sandbox themselves. OVS_RUNDIR=$sandbox; export OVS_RUNDIR OVS_LOGDIR=$sandbox; export OVS_LOGDIR OVS_DBDIR=$sandbox; export OVS_DBDIR OVS_SYSCONFDIR=$sandbox; export OVS_SYSCONFDIR trap 'kill `cat *.pid`' 0 1 2 3 13 14 15 # Create database and start ovsdb-server. touch .conf.db.~lock~ rm -f conf.db run ovsdb-tool create conf.db "$srcdir"/vswitchd/vswitch.ovsschema run ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file \ --remote=punix:"$sandbox"/db.sock # Start ovs-vswitchd. run ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file \ --enable-dummy --disable-system -vvconn -vnetdev_dummy # Add a bridge and some ports for OFTest to use, # and configure ovs-vswitchd to connect to OFTest. run ovs-vsctl --no-wait \ -- add-br br0 \ -- set bridge br0 datapath-type=dummy fail-mode=secure for port in p1 p2 p3 p4; do run ovs-vsctl --no-wait \ -- add-port br0 $port \ -- set interface $port type=dummy \ options:pstream=punix:$OVS_RUNDIR/$port done run ovs-vsctl \ -- set-controller br0 tcp:127.0.0.1:6653 \ -- set controller br0 connection-mode=out-of-band max-backoff=1000 # Run OFTest. run $OFT -P ovs-dummy $OFTFLAGS; status=$? cat <&2 'not in build directory, please change directory or run via \"make check-ryu' exit 1 fi PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH; export PATH # Find srcdir. case $srcdir in '') srcdir=$builddir ;; /*) ;; *) srcdir=`pwd`/$srcdir ;; esac if test ! -e "$srcdir"/README.rst; then echo >&2 'source directory not found, please set $srcdir or run via \"make check-ryu' exit 1 fi # Make sure ryu is available. if test X"$RYUDIR" = X; then RYUDIR=$srcdir/../ryu fi PYTHONPATH=$RYUDIR:$PYTHONPATH; export PYTHONPATH PATH=$RYUDIR/bin:$PATH; export PATH if (ryu-manager --version) >/dev/null 2>&1; then : else echo >&2 '"ryu-manager" binary not found or cannot be run, please set $RYUDIR' exit 1 fi # Create sandbox. rm -rf sandbox mkdir sandbox cd sandbox sandbox=`pwd` # Set up environment for OVS programs to sandbox themselves. OVS_RUNDIR=$sandbox; export OVS_RUNDIR OVS_LOGDIR=$sandbox; export OVS_LOGDIR OVS_DBDIR=$sandbox; export OVS_DBDIR OVS_SYSCONFDIR=$sandbox; export OVS_SYSCONFDIR for signal in 0 1 2 3 13 14 15; do trap 'kill `cat $sandbox/*.pid`; trap - $signal; kill -$signal $$' $signal done # Create database and start ovsdb-server. touch .conf.db.~lock~ rm -f conf.db run ovsdb-tool create conf.db "$srcdir"/vswitchd/vswitch.ovsschema run ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file \ --remote=punix:"$sandbox"/db.sock # Start ovs-vswitchd. run ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file \ --enable-dummy --disable-system -vvconn -vnetdev_dummy # Add bridges for Ryu to use, and configure them to connect to Ryu. for config in \ 'br0 0000000000000001 a b pstream=punix' \ 'br1 0000000000000002 c d stream=unix' do set $config bridge=$1 dpid=$2 port1=$3 port2=$4 stream_mode=$5 run ovs-vsctl --no-wait \ -- add-br $bridge \ -- set bridge $bridge \ datapath-type=dummy fail-mode=secure \ protocols='[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]' \ other-config:datapath-id=$dpid \ -- set-controller $bridge tcp:127.0.0.1:6653 \ -- set controller $bridge connection-mode=out-of-band \ max-backoff=1000 \ -- add-port $bridge $port1 \ -- set interface $port1 ofport_request=1 type=dummy \ options:${stream_mode}:"$sandbox"/p1.sock \ -- add-port $bridge $port2 \ -- set interface $port2 ofport_request=2 type=dummy \ options:${stream_mode}:"$sandbox"/p2.sock done logs= run_app() { app=$1 cat < "$sandbox/ryu.pid" i=0 while sleep 1; do if grep -q -E 'TEST_FINISHED|Test end|uncaught exception' "$logfile" \ >/dev/null then break fi i=`expr $i + 1` if test $i -ge 600; then echo "--- TIMEOUT after $i seconds" break fi done kill $pid wait } # Run Ryu. cd $RYUDIR for app in \ ryu/tests/switch/tester.py do run_app $app done # tweak OVS setup because the following tests assume single bridge. run ovs-vsctl -- del-br br1 for app in \ ryu/tests/integrated/test_add_flow_v10.py \ ryu/tests/integrated/test_request_reply_v12.py \ ryu/tests/integrated/test_add_flow_v12_actions.py \ ryu/tests/integrated/test_add_flow_v12_matches.py do run_app $app done cat <:/ ']]) m4_define([FILTER_STP_TOPOLOGY_LISTENING], [[ grep 'disabled to listening' | sed ' s/.*ofproto_dpif|.*|port .*:/port <>:/ ']]) m4_define([FILTER_STP_TOPOLOGY_FORWARDING], [[ grep 'learning to forwarding' | sed ' s/.*ofproto_dpif|.*|port .*:/port <>:/ ']]) AT_SETUP([STP - dummy interface]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 connected via unix domain socket OVS_VSWITCHD_START( [set port br0 other_config:stp-enable=false -- \ set bridge br0 stp_enable=true -- \ add-br br1 -- \ set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ fail-mode=secure -- \ set port br1 other_config:stp-enable=false -- \ set bridge br1 stp_enable=true --]) AT_CHECK([ovs-appctl vlog/set ofproto_dpif:dbg]) AT_CHECK([ovs-vsctl add-port br0 p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \ set port p1 other_config:stp-enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br0 p7 -- \ set interface p7 ofport_request=7 type=dummy -- \ set port p7 other_config:stp-enable=false -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p2 -- \ set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \ set port p2 other_config:stp-enable=true -- \ ]) AT_CHECK([ovs-vsctl add-port br1 p8 -- \ set interface p8 ofport_request=8 type=dummy -- \ set port p8 other_config:stp-enable=false -- \ ]) ovs-appctl netdev-dummy/set-admin-state up ovs-appctl time/stop AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"]) # give time for STP to move initially ovs-appctl time/warp 6000 3000 AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_LISTENING], [0], [dnl port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl >> STP not in forwarding state, skipping output ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl >> STP not in forwarding state, skipping output ]) # give time for STP to synchronize ovs-appctl time/warp 30000 3000 AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from listening to learning port <>: STP state changed from listening to learning port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 2 ]) AT_CLEANUP AT_SETUP([STP - patch ports]) # Create br0 with interfaces p1 and p7 # and br1 with interfaces p2 and p8 # with p1 and p2 being connected patch ports. OVS_VSWITCHD_START( [set port br0 other_config:stp-enable=false -- \ set bridge br0 stp-enable=true ]) AT_CHECK([add_of_br 1 \ set port br1 other_config:stp-enable=false -- \ set bridge br1 stp-enable=true]) ovs-appctl time/stop AT_CHECK([ovs-vsctl \ add-port br0 p1 -- \ set interface p1 type=patch options:peer=p2 ofport_request=1 -- \ set port p1 other_config:stp-enable=true -- \ add-port br1 p2 -- \ set interface p2 type=patch options:peer=p1 ofport_request=2 -- \ set port p2 other_config:stp-enable=true -- \ ]) AT_CHECK([ovs-vsctl \ add-port br0 p7 -- \ set interface p7 ofport_request=7 type=dummy -- \ set port p7 other_config:stp-enable=false -- \ add-port br1 p8 -- \ set interface p8 ofport_request=8 type=dummy -- \ set port p8 other_config:stp-enable=false -- \ ]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"]) AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"]) # Give time for STP to synchronize. ovs-appctl time/warp 30000 3000 OVS_WAIT_UNTIL_EQUAL([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [dnl port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from listening to learning port <>: STP state changed from listening to learning port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 8 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl Datapath actions: 7 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([STP - flush the fdb and mdb when topology changed]) OVS_VSWITCHD_START([]) # setting as below, the br0 will be root bridge and p5 will be blocked. AT_CHECK([ ovs-vsctl -- \ set port br0 other_config:stp-enable=false -- \ set bridge br0 datapath-type=dummy -- \ set bridge br0 stp_enable=true mcast_snooping_enable=true \ other-config:hwaddr=aa:66:aa:66:00:00 -- \ add-br br1 -- \ set port br1 other_config:stp-enable=false -- \ set bridge br1 datapath-type=dummy -- \ set bridge br1 stp_enable=true mcast_snooping_enable=true \ other-config:hwaddr=aa:66:aa:66:00:01 -- \ add-br br2 -- \ set port br2 other_config:stp-enable=false -- \ set bridge br2 datapath-type=dummy -- \ set bridge br2 stp_enable=true mcast_snooping_enable=true \ other-config:hwaddr=aa:66:aa:66:00:02 ], [0]) AT_CHECK([ovs-appctl vlog/set ofproto_dpif:dbg]) AT_CHECK([ovs-appctl vlog/set ofproto_dpif_xlate:dbg]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-ofctl add-flow br2 action=normal]) AT_CHECK([ ovs-vsctl add-port br0 p1 -- \ set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 ovs-vsctl add-port br0 p2 -- \ set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p6.sock ofport_request=2 ovs-vsctl add-port br1 p3 -- \ set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3 ovs-vsctl add-port br1 p4 -- \ set interface p4 type=dummy options:pstream=punix:$OVS_RUNDIR/p4.sock ofport_request=4 ovs-vsctl add-port br2 p5 -- \ set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p4.sock ofport_request=5 ovs-vsctl add-port br2 p6 -- \ set interface p6 type=dummy options:pstream=punix:$OVS_RUNDIR/p6.sock ofport_request=6 ], [0]) ovs-appctl netdev-dummy/set-admin-state up ovs-appctl time/stop # give time for STP to move initially ovs-appctl time/warp 6000 3000 AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_LISTENING], [0], [dnl port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening port <>: STP state changed from disabled to listening ]) # give time for STP to synchronize ovs-appctl time/warp 30000 3000 AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_FORWARDING], [0], [dnl port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding port <>: STP state changed from learning to forwarding ]) # When topology is changed or the root brdige receives the TCN BPDU, the # root bridge will start the topology change timer. We should wait the # topology change timer to stop after 35s (max age 20 + forward delay 15). # After 35s, the root bridge will stop send CONF BPDU with # STP_CONFIG_TOPOLOGY_CHANGE flag and the topology will be stable. More # importantly, we should make time warp (in a second) because the hold timer # of stp ports will stop after 1s. So the root bridge can send quickly # topology change ack (other bridges may send TCN BPDU to root bridge) for # avoiding root brdige to flush fdb and mdb frequently. ovs-appctl time/warp 36000 1000 # root bridge sends query packet # we don't want to lose that message, so send it twice AT_CHECK([ovs-appctl netdev-dummy/receive br0 \ '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000']) ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl netdev-dummy/receive br0 \ '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000']) OVS_WAIT_UNTIL([ovs-appctl fdb/show br0 | grep '00:0c:29:a0:27:d1']) OVS_WAIT_UNTIL([ovs-appctl fdb/show br1 | grep '00:0c:29:a0:27:d1']) OVS_WAIT_UNTIL([ovs-appctl fdb/show br2 | grep '00:0c:29:a0:27:d1']) OVS_WAIT_UNTIL([ovs-appctl mdb/show br0 | grep 'querier']) OVS_WAIT_UNTIL([ovs-appctl mdb/show br1 | grep 'querier']) OVS_WAIT_UNTIL([ovs-appctl mdb/show br2 | grep 'querier']) # Make sure that validation of flows was before changing of topology. ovs-appctl revalidator/wait # del p2 on the br0, the topology will be changed AT_CHECK([ovs-vsctl del-port br0 p2]) # give time for STP to synchronize ovs-appctl time/warp 36000 3000 # check fdb and mdb AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl port VLAN MAC Age ]) AT_CHECK([ovs-appctl fdb/show br1], [0], [dnl port VLAN MAC Age ]) AT_CHECK([ovs-appctl fdb/show br2], [0], [dnl port VLAN MAC Age ]) AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl port VLAN protocol GROUP Age ]) AT_CHECK([ovs-appctl mdb/show br1], [0], [dnl port VLAN protocol GROUP Age ]) AT_CHECK([ovs-appctl mdb/show br2], [0], [dnl port VLAN protocol GROUP Age ]) AT_CLEANUP AT_SETUP([STP - check link-state when stp is running]) OVS_VSWITCHD_START([]) AT_CHECK([ ovs-vsctl -- \ set port br0 other_config:stp-enable=false -- \ set bridge br0 datapath-type=dummy stp_enable=true \ other-config:hwaddr=aa:66:aa:66:00:00 ], [0]) AT_CHECK([ ovs-vsctl add-port br0 p1 -- \ set interface p1 type=dummy -- \ set port p1 other_config:stp-port-num=1 ovs-vsctl add-port br0 p2 -- \ set interface p2 type=dummy -- \ set port p2 other_config:stp-port-num=2 ], [0]) ovs-appctl netdev-dummy/set-admin-state up ovs-appctl time/stop # give time for STP to move initially ovs-appctl time/warp 31000 1000 AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl p1 designated forwarding 2 128.1 ]) AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl p2 designated forwarding 2 128.2 ]) # add a stp port AT_CHECK([ ovs-vsctl add-port br0 p3 -- \ set interface p3 type=dummy -- \ set port p3 other_config:stp-port-num=3 ], [0]) ovs-appctl netdev-dummy/set-admin-state p3 down # We should not show the p3 because its link-state is down AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl p1 designated forwarding 2 128.1 ]) AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl p2 designated forwarding 2 128.2 ]) AT_CHECK([ovs-appctl stp/show br0 | grep p3], [1], [dnl ]) ovs-appctl netdev-dummy/set-admin-state p3 up AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl p1 designated forwarding 2 128.1 ]) AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl p2 designated forwarding 2 128.2 ]) AT_CHECK([ovs-appctl stp/show br0 | grep p3], [0], [dnl p3 designated listening 2 128.3 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-afxdp-macros.at000066400000000000000000000021421514270232600243550ustar00rootroot00000000000000# Add port to ovs bridge by using afxdp mode. # This will use generic XDP support in the veth driver. m4_define([ADD_VETH], [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77]) CONFIGURE_VETH_OFFLOADS([$1]) AT_CHECK([ip link set $1 netns $2]) AT_CHECK([ip link set dev ovs-$1 up]) AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \ set interface ovs-$1 external-ids:iface-id="$1" type="afxdp"]) NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7]) NS_CHECK_EXEC([$2], [ip link set dev $1 up]) if test -n "$5"; then NS_CHECK_EXEC([$2], [ip link set dev $1 address $5]) fi if test -n "$6"; then NS_CHECK_EXEC([$2], [ip route add default via $6]) fi on_exit 'ip link del ovs-$1' ] ) m4_define([OVS_CHECK_8021AD], [AT_SKIP_IF([:])]) # CONFIGURE_VETH_OFFLOADS([VETH]) # # Disable TX offloads and VLAN offloads for veths used in AF_XDP. m4_define([CONFIGURE_VETH_OFFLOADS], [AT_CHECK([ethtool -K $1 tx off], [0], [ignore], [ignore]) AT_CHECK([ethtool -K $1 txvlan off], [0], [ignore], [ignore]) ] ) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-afxdp-testsuite.at000066400000000000000000000016711514270232600251300ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2018, 2019 Nicira, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-userspace-macros.at]) m4_include([tests/system-afxdp-macros.at]) m4_include([tests/system-afxdp.at]) m4_include([tests/system-traffic.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-afxdp.at000066400000000000000000000025711514270232600231010ustar00rootroot00000000000000AT_BANNER([AF_XDP]) AT_SETUP([AF_XDP - infinite re-addition of failed ports]) AT_KEYWORDS([afxdp infinite]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl del-port ovs-p0]) AT_CHECK([ovs-vsctl add-port br0 ovs-p0 -- \ set interface ovs-p0 type=afxdp options:n_rxq=42], [65], [], [stderr]) OVS_WAIT_UNTIL([grep "ovs-p0: could not set configuration" ovs-vswitchd.log]) sleep 5 AT_CHECK([grep "ovs-p0: could not set configuration" ovs-vswitchd.log | wc -l], [0], [1 ]) OVS_TRAFFIC_VSWITCHD_STOP(["/ovs-p0: Too big 'n_rxq'/d /ovs-p0: could not set configuration/d"]) AT_CLEANUP AT_SETUP([AF_XDP - ping between pmd and non-pmd ports]) AT_KEYWORDS([afxdp nonpmd]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl del-port ovs-p0]) AT_CHECK([ovs-vsctl add-port br0 ovs-p0 -- \ set interface ovs-p0 type=afxdp-nonpmd options:n_rxq=1], [0], [], [stderr]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-common-macros.at000066400000000000000000000337541514270232600245600ustar00rootroot00000000000000# DEL_NAMESPACES(ns [, ns ... ]) # # Delete namespaces from the running OS m4_define([DEL_NAMESPACES], [m4_foreach([ns], [$@], [echo removing namespace ns; ip netns del ns])] ) # ADD_NAMESPACES(ns [, ns ... ]) # # Add new namespaces, if ns exists, the old one # will be remove before new ones are installed. m4_define([ADD_NAMESPACES], [m4_foreach([ns], [$@], [DEL_NAMESPACES(ns) AT_CHECK([ip netns add ns || return 77]) on_exit 'DEL_NAMESPACES(ns)' ip netns exec ns sysctl -w net.netfilter.nf_conntrack_helper=0 ]) ] ) # NS_EXEC([namespace], [command]) # # Execute 'command' in 'namespace' m4_define([NS_EXEC], [ip netns exec $1 sh << NS_EXEC_HEREDOC $2 NS_EXEC_HEREDOC]) # NS_CHECK_EXEC([namespace], [command], other_params...) # # Wrapper for AT_CHECK that executes 'command' inside 'namespace'. # 'other_params' as passed as they are to AT_CHECK. m4_define([NS_CHECK_EXEC], [ AT_CHECK([NS_EXEC([$1], [$2])], m4_shift(m4_shift($@))) ] ) # ADD_BR([name], [vsctl-args]) # # Expands into the proper ovs-vsctl commands to create a bridge with the # appropriate type, and allows additional arguments to be passed. m4_define([ADD_BR], [ovs-vsctl _ADD_BR([$1]) -- $2]) # ADD_INT([port], [namespace], [ovs-br], [ip_addr]) # # Add an internal port to 'ovs-br', then shift it into 'namespace' and # configure it with 'ip_addr' (specified in CIDR notation). m4_define([ADD_INT], [ AT_CHECK([ovs-vsctl add-port $3 $1 -- set int $1 type=internal]) AT_CHECK([ip link set $1 netns $2]) NS_CHECK_EXEC([$2], [ip addr add $4 dev $1]) NS_CHECK_EXEC([$2], [ip link set dev $1 up]) ] ) # ADD_VETH([port], [namespace], [ovs-br], [ip_addr] [mac_addr], [gateway], # [ip_addr_flags]) # # Add a pair of veth ports. 'port' will be added to name space 'namespace', # and "ovs-'port'" will be added to ovs bridge 'ovs-br'. # # The 'port' in 'namespace' will be brought up with static IP address # with 'ip_addr' in CIDR notation. # # Optionally, one can specify the 'mac_addr' for 'port' and the default # 'gateway'. # # The existing 'port' or 'ovs-port' will be removed before new ones are added. # m4_define([ADD_VETH], [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77]) on_exit 'echo removing interface ovs-$1; ip link del ovs-$1' CONFIGURE_VETH_OFFLOADS([$1]) AT_CHECK([ip link set $1 netns $2]) AT_CHECK([ip link set dev ovs-$1 up]) AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \ set interface ovs-$1 external-ids:iface-id="$1"]) NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7]) NS_CHECK_EXEC([$2], [ip link set dev $1 up]) if test -n "$5"; then NS_CHECK_EXEC([$2], [ip link set dev $1 address $5]) fi if test -n "$6"; then NS_CHECK_EXEC([$2], [ip route add default via $6]) fi ] ) # ADD_VETH_BOND([ports], [namespace], [ovs-br], [bond], [mode], [ip_addr]) # # Add a set of veth port pairs. Ports named in the list 'ports' will be added # to 'namespace', and the corresponding port names, prefixed by 'ovs-' will # be included in an OVS bond 'bond' which is added to bridge 'ovs-br'. # # The 'bond' in 'namespace' will be brought up with static IP address # with 'ip_addr' in CIDR notation. # m4_define([ADD_VETH_BOND], [ BONDPORTS="" for port in $1; do AT_CHECK([ip link add $port type veth peer name ovs-$port]) CONFIGURE_VETH_OFFLOADS([$port]) AT_CHECK([ip link set $port netns $2]) AT_CHECK([ip link set dev ovs-$port up]) BONDPORTS="$BONDPORTS ovs-$port" on_exit 'ip link del ovs-$port' done NS_CHECK_EXEC([$2], [ip link add name $4 type bond]) case "$(echo $5 | sed 's/.*lacp=//' | sed 's/ .*//')" in active|passive) NS_CHECK_EXEC([$2], [sh -c "echo 802.3ad > /sys/class/net/$4/bonding/mode"]) NS_CHECK_EXEC([$2], [sh -c "echo 100 > /sys/class/net/$4/bonding/miimon"]) ;; esac for port in $1; do NS_CHECK_EXEC([$2], [ip link set dev $port master $4]) done NS_CHECK_EXEC([$2], [ip addr add $6 dev $4]) NS_CHECK_EXEC([$2], [ip link set dev $4 up]) AT_CHECK([ovs-vsctl add-bond $3 ovs-$4 $BONDPORTS $5]) on_exit 'ip link del ovs-$4' ] ) # ADD_VETH_NS([ns1], [port1], [ip_addr1], [ns2], [port2], [ip_addr2]) # # Add a pair of veth ports in 'ns1' and 'ns2'. The port names are 'port1' # and 'port2' respectively, and the IP addresses 'ip_addr1' and 'ip_addr2' # are assigned to each port. m4_define([ADD_VETH_NS], [ AT_CHECK([ip link add $2 type veth peer name $5]), AT_CHECK([ip link set $2 netns $1]) AT_CHECK([ip link set $5 netns $4]) NS_CHECK_EXEC([$1], [ip link set $2 up]) NS_CHECK_EXEC([$4], [ip link set $5 up]) NS_CHECK_EXEC([$1], [ip addr add $3 dev $2]) NS_CHECK_EXEC([$4], [ip addr add $6 dev $5]) ] ) # ADD_VLAN([port], [namespace], [vlan-id], [ip-addr]) # # Add a VLAN device named 'port' within 'namespace'. It will be configured # with the ID 'vlan-id' and the address 'ip-addr'. m4_define([ADD_VLAN], [ NS_CHECK_EXEC([$2], [ip link add link $1 name $1.$3 type vlan proto 802.1q id $3]) NS_CHECK_EXEC([$2], [ip link set dev $1.$3 up]) NS_CHECK_EXEC([$2], [ip addr add dev $1.$3 $4]) ] ) # ADD_SVLAN([port], [namespace], [vlan-id], [ip-addr]) # # Add a SVLAN device named 'port' within 'namespace'. It will be configured # with the ID 'vlan-id' and the address 'ip-addr'. m4_define([ADD_SVLAN], [ NS_CHECK_EXEC([$2], [ip link add link $1 name $1.$3 type vlan proto 802.1ad id $3]) NS_CHECK_EXEC([$2], [ip link set dev $1.$3 up]) NS_CHECK_EXEC([$2], [ip addr add dev $1.$3 $4]) NS_CHECK_EXEC([$2], [ip link set $1.$3 mtu 1496]) ] ) # ADD_CVLAN([port], [namespace], [vlan-id], [ip-addr]) # # Similar to ADD_VLAN(), but sets MTU. Lower MTU here instead of increase MTU # on bridge/SVLAN because older kernels didn't work. # m4_define([ADD_CVLAN], [ ADD_VLAN([$1], [$2], [$3], [$4]) NS_CHECK_EXEC([$2], [ip link set $1.$3 mtu 1492]) ] ) # ADD_OVS_TUNNEL([type], [bridge], [port], [remote-addr], [overlay-addr], # [tunnel-args]) # # Add an ovs-based tunnel device in the root namespace, with name 'port' and # type 'type'. The tunnel device will be configured as point-to-point with the # 'remote-addr' as the underlay address of the remote tunnel endpoint. # # 'port will be configured with the address 'overlay-addr'. # m4_define([ADD_OVS_TUNNEL], [AT_CHECK([ovs-vsctl add-port $2 $3 -- \ set int $3 type=$1 options:remote_ip=$4 $6]) AT_CHECK([ip addr add dev $2 $5]) AT_CHECK([ip link set dev $2 up]) AT_CHECK([ip link set dev $2 mtu 1450]) on_exit 'ip addr del dev $2 $5' ] ) # ADD_OVS_TUNNEL6([type], [bridge], [port], [remote-addr], [overlay-addr], # [tunnel-args]) # # Same as ADD_OVS_TUNNEL, but drops MTU enough for the IPv6 underlay. # m4_define([ADD_OVS_TUNNEL6], [ADD_OVS_TUNNEL([$1], [$2], [$3], [$4], [$5], [$6]) AT_CHECK([ip link set dev $2 mtu 1430]) ] ) # ADD_NATIVE_TUNNEL([type], [port], [namespace], [remote-addr], [overlay-addr], # [type-args], [link-args]) # # Add a native tunnel device within 'namespace', with name 'port' and type # 'type'. The tunnel device will be configured as point-to-point with the # 'remote-addr' as the underlay address of the remote tunnel endpoint (as # viewed from the perspective of that namespace). # # 'port' will be configured with the address 'overlay-addr'. 'type-args' is # made available so that additional arguments can be passed to "ip link add" # for configuring specific link type's arguments, for instance to configure # the vxlan destination port. 'link-args' is made for arguments passed to # "ip link set", for instance to configure MAC address. # m4_define([ADD_NATIVE_TUNNEL], [NS_CHECK_EXEC([$3], [ip link add dev $2 type $1 remote $4 $6]) NS_CHECK_EXEC([$3], [ip addr add dev $2 $5]) NS_CHECK_EXEC([$3], [ip link set dev $2 mtu 1450 $7 up]) ] ) # ADD_NATIVE_TUNNEL6([type], [port], [namespace], [remote-addr], [overlay-addr], # [type-args], [link-args]) # # Same as ADD_NATIVE_TUNNEL, but drops MTU enough for the IPv6 underlay. # m4_define([ADD_NATIVE_TUNNEL6], [ADD_NATIVE_TUNNEL([$1], [$2], [$3], [$4], [$5], [$6], [$7]) NS_CHECK_EXEC([$3], [ip link set dev $2 mtu 1430]) ] ) # FORMAT_PING([]) # # Strip variant pieces from ping output so the output can be reliably compared. # m4_define([FORMAT_PING], [grep "transmitted" | sed 's/time.*ms$/time 0ms/']) # STRIP_MONITOR_CSUM([]) # # Strip the csum value from ovs-ofctl monitor. # m4_define([STRIP_MONITOR_CSUM], [grep "csum:" | sed 's/csum:.*/csum: /']) # FORMAT_CT([ip-addr]) # # Strip content from the piped input which would differ from test to test # and limit the output to the rows containing 'ip-addr'. # m4_define([FORMAT_CT], [[grep "dst=$1" | sed -e 's/port=[0-9]*/port=/g' -e 's/id=[0-9]*/id=/g' -e 's/state=[0-9_A-Z]*/state=/g' | sort | uniq]]) # NETNS_DAEMONIZE([namespace], [command], [pidfile]) # # Run 'command' as a background process within 'namespace' and record its pid # to 'pidfile' to allow cleanup on exit. # m4_define([NETNS_DAEMONIZE], [ip netns exec $1 $2 & echo $! > $3 echo "kill \`cat $3\`" >> cleanup ] ) # OVS_GET_HTTP([url], [optional_curl_arguments]) # # Do a HTTP get; we are currently using the curl command. # m4_define([OVS_GET_HTTP], [curl $1 --retry 3 --max-time 1 --retry-connrefused -v $2] ) # OVS_GET_FTP([url], [optional_curl_arguments]) # # Do a passive FTP get; we are currently using the curl command. # m4_define([OVS_GET_FTP], [curl ftp://$1 --retry 3 --max-time 1 --retry-connrefused \ -v $2] ) # OVS_GET_FTP_ACTIVE([url], [optional_curl_arguments]) # # Do an active FTP get; we are currently using the curl command. # m4_define([OVS_GET_FTP_ACTIVE], [curl ftp://$1 --retry 3 --max-time 1 --retry-connrefused -v \ --ftp-port - $2] ) # OVS_CHECK_FIREWALL() # # Check if firewalld is active, skip the test if it is on. # The following command currently only supports RHEL and CentOS. m4_define([OVS_CHECK_FIREWALL], [AT_SKIP_IF([systemctl status firewalld 2>&1 | grep running > /dev/null])]) # OVS_START_L7([namespace], [protocol], [port]) # # Start a server serving 'protocol' on port 'port' within 'namespace'. # If 'port' is not specified, the standard one for 'protocol' will be used. # The server will exit when the test finishes. # m4_define([OVS_START_L7], [PIDFILE=$(mktemp $2XXX.pid) NETNS_DAEMONIZE([$1], [[$PYTHON3 $srcdir/test-l7.py $2 $3]], [$PIDFILE]) dnl netstat doesn't print http over IPv6 as "http6"; drop the number. PROTO=$(echo $2 | sed -e 's/\([[a-zA-Z]]*\).*/\1/') if test -z "$3"; then OVS_WAIT_UNTIL([NS_EXEC([$1], [netstat -l | grep $PROTO])]) else OVS_WAIT_UNTIL([NS_EXEC([$1], [netstat -ln | grep :$3])]) fi ] ) # OFPROTO_CLEAR_DURATION_IDLE([]) # # Clear the duration from the piped input which would differ from test to test # m4_define([OFPROTO_CLEAR_DURATION_IDLE], [[sed -e 's/duration=.*s,/duration=,/g' -e 's/idle_age=[0-9]*,/idle_age=,/g']]) # OVS_CHECK_TC_QDISC() # # Macro to skip tests when tc qdisc can't be applied on a OVS port. m4_define([OVS_CHECK_TC_QDISC], [AT_SKIP_IF([test $HAVE_TC = no])]) # OVS_CHECK_TUNNEL_TSO() # # Macro to be used in general tunneling tests that could be also # used by system-tso. In that case, tunneling is not supported and # the test should be skipped. m4_define([OVS_CHECK_TUNNEL_TSO], [m4_ifdef([CHECK_SYSTEM_TSO], [AT_SKIP_IF(:)])]) # OVS_CHECK_VXLAN() # # Do basic check for vxlan functionality, skip the test if it's not there. m4_define([OVS_CHECK_VXLAN], [AT_SKIP_IF([! ip link add foo type vxlan help 2>&1 | grep dstport >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_VXLAN_UDP6ZEROCSUM() m4_define([OVS_CHECK_VXLAN_UDP6ZEROCSUM], [AT_SKIP_IF([! ip link add foo type vxlan help 2>&1 | grep udp6zerocsum >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_VXLAN_GPE() m4_define([OVS_CHECK_VXLAN_GPE], [OVS_CHECK_VXLAN() AT_SKIP_IF([! ip link add foo type vxlan help 2>&1 | grep gpe >/dev/null])]) # OVS_CHECK_GRE() m4_define([OVS_CHECK_GRE], [AT_SKIP_IF([! ip link add foo type gretap help 2>&1 | grep gretap >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_ERSPAN() m4_define([OVS_CHECK_ERSPAN], [AT_SKIP_IF([! ip link add foo type erspan help 2>&1 | grep erspan >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_GRE_L3() m4_define([OVS_CHECK_GRE_L3], [AT_SKIP_IF([! ip link add foo type gre help 2>&1 | grep "gre " >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_GENEVE() m4_define([OVS_CHECK_GENEVE], [AT_SKIP_IF([! ip link add foo type geneve help 2>&1 | grep geneve >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_GENEVE_UDP6ZEROCSUM() m4_define([OVS_CHECK_GENEVE_UDP6ZEROCSUM], [AT_SKIP_IF([! ip link add foo type geneve help 2>&1 | grep udp6zerocsum >/dev/null]) OVS_CHECK_FIREWALL()]) # OVS_CHECK_8021AD() m4_define([OVS_CHECK_8021AD], [AT_SKIP_IF([! grep -q "VLAN header stack length probed as" ovs-vswitchd.log]) AT_SKIP_IF([[test `sed -n 's/.*VLAN header stack length probed as \([0-9]\+\).*/\1/p' ovs-vswitchd.log` -lt 2]])]) # OVS_CHECK_IPROUTE_ENCAP() m4_define([OVS_CHECK_IPROUTE_ENCAP], [AT_SKIP_IF([! ip route help 2>&1 |grep encap >/dev/null])]) # OVS_CHECK_CT_CLEAR() m4_define([OVS_CHECK_CT_CLEAR], [AT_SKIP_IF([! grep -q "Datapath supports ct_clear action" ovs-vswitchd.log])]) # OVS_CHECK_GITHUB_ACTION m4_define([OVS_CHECK_GITHUB_ACTION], [AT_SKIP_IF([test "$GITHUB_ACTIONS" = "true"])]) # OVS_CHECK_DROP_ACTION() m4_define([OVS_CHECK_DROP_ACTION], [AT_SKIP_IF([! grep -q "Datapath supports explicit drop action" ovs-vswitchd.log])]) # OVS_CHECK_PSAMPLE() m4_define([OVS_CHECK_PSAMPLE], [AT_SKIP_IF([! grep -q "Datapath supports psample action" ovs-vswitchd.log])]) # OVS_CHECK_XT() m4_define([OVS_CHECK_XT], [AT_SKIP_IF([test $HAVE_IPTABLES = no && test $HAVE_NFT = no])]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-find-device.py000077500000000000000000000024131514270232600251160ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2024 Red Hat, Inc. # # 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. from pathlib import Path import os import sys # The tester might want to select a PCI device, if so, trust it. if 'DPDK_PCI_ADDR' in os.environ: print(os.environ['DPDK_PCI_ADDR']) sys.exit(0) for device in sorted(Path('/sys/bus/pci/devices').iterdir()): class_path = device / 'class' # Only consider Network class devices if class_path.read_text().strip() != '0x020000': continue kmod_path = device / 'driver' / 'module' kmod_name = kmod_path.resolve().name # Only care about devices bound to vfio_pci or igb_uio. if kmod_name not in ['vfio_pci', 'igb_uio']: continue print(device.resolve().name) sys.exit(0) sys.exit(1) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-macros.at000066400000000000000000000150211514270232600241750ustar00rootroot00000000000000# OVS_DPDK_PRE_CHECK() # # Check prerequisites for DPDK tests. Following settings are checked: # - Hugepages # m4_define([OVS_DPDK_PRE_CHECK], [dnl Check Hugepages AT_CHECK([cat /proc/meminfo], [], [stdout]) AT_SKIP_IF([grep -E 'HugePages_Free: *0' stdout], [], [stdout]) ]) # OVS_DPDK_PRE_PHY_SKIP() # # Skip any phy related tests if the PHY variable is not set. # This is done by checking for a bound driver. # m4_define([OVS_DPDK_PRE_PHY_SKIP], [dnl Perform the precheck OVS_DPDK_PRE_CHECK() dnl Check if a device is available for DPDK AT_SKIP_IF([ ! $abs_top_srcdir/tests/system-dpdk-find-device.py > DPDK_PCI_ADDR ]) ]) # OVS_DPDK_START() # # Start ovsdb-server. Set dpdk-init to initialize DPDK. Start ovs-vswitchd. # m4_define([OVS_DPDK_START], [dnl start ovs dpdk OVS_DPDK_START_OVSDB($3) dnl Enable DPDK functionality AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) OVS_DPDK_START_VSWITCHD([$1], [$2]) ]) # OVS_DPDK_START_OVSDB() # # Create an empty database and start ovsdb-server. # m4_define([OVS_DPDK_START_OVSDB], [dnl Create database. AT_CHECK([touch .conf.db.~lock~]) AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) dnl Start ovsdb-server. AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [stdout], [stderr]) on_exit "kill `cat ovsdb-server.pid`" AT_CHECK([[sed < stderr ' /vlog|INFO|opened log file/d /ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) AT_CAPTURE_FILE([ovsdb-server.log]) dnl Initialize database. AT_CHECK([ovs-vsctl --no-wait init $1]) ]) # OVS_DPDK_START_VSWITCHD() # # Add special configuration for dpdk-init. Start ovs-vswitchd. # m4_define([OVS_DPDK_START_VSWITCHD], [dnl Change DPDK drivers log levels so that tests only catch errors AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-extra="--log-level=pmd.*:error $1"]) dnl Start ovs-vswitchd. AT_CHECK([ovs-vswitchd $2 --detach --no-chdir --pidfile --log-file -vvconn -vofproto_dpif -vunixctl], [0], [stdout], [stderr]) AT_CAPTURE_FILE([ovs-vswitchd.log]) on_exit "kill_ovs_vswitchd `cat ovs-vswitchd.pid`" ]) m4_define([OVS_DPDK_STOP_VSWITCHD], [OVS_VSWITCHD_STOP([dnl $1";/does not exist. The Open vSwitch kernel module is probably not loaded./d /does not support MTU configuration,/d /EAL: No \(available\|free\) .*hugepages reported/d /Failed to enable flow control/d /ice_vsi_config_outer_vlan_stripping(): Single VLAN mode (SVM) does not support qinq/d /Rx checksum offload is not supported on/d /TELEMETRY: No legacy callbacks, legacy socket not created/d"]) ]) # OVS_DPDK_CHECK_TESTPMD() # # Check dpdk-testpmd availability. # m4_define([OVS_DPDK_CHECK_TESTPMD], [AT_SKIP_IF([! which dpdk-testpmd >/dev/null 2>/dev/null]) ]) # OVS_DPDK_START_TESTPMD() # # Start dpdk-testpmd in background. # m4_define([OVS_DPDK_START_TESTPMD], [AT_CHECK([lscpu], [], [stdout]) AT_CHECK([cat stdout | grep "NUMA node(s)" | awk '{c=1; while (c++<$(3)) {printf "512,"}; print "512"}' > NUMA_NODE]) eal_options="$DPDK_EAL_OPTIONS --in-memory --socket-mem="$(cat NUMA_NODE)" --single-file-segments --no-pci --file-prefix testpmd" options="$1" test "$options" != "${options%% -- *}" || options="$options -- " eal_options="$eal_options ${options%% -- *}" testpmd_options="-a --stats-period 2 ${options#* -- }" echo "dpdk-testpmd $eal_options -- $testpmd_options" >testpmd.cmd dpdk-testpmd $eal_options -- $testpmd_options >testpmd.log 2>&1 & \ echo $! > testpmd.pid on_exit "kill -9 `cat testpmd.pid`" ]) # OVS_DPDK_STOP_TESTPMD() # # Stop background dpdk-testpmd. # m4_define([OVS_DPDK_STOP_TESTPMD], [AT_CHECK([kill `cat testpmd.pid`]) OVS_WAIT([kill -0 `cat testpmd.pid`], [kill -9 `cat testpmd.pid`]) ]) # OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args]) # # Creates a database and starts ovsdb-server, starts ovs-vswitchd # connected to that database, calls ovs-vsctl to create a bridge named # br0 with predictable settings, passing 'vsctl-args' as additional # commands to ovs-vsctl. If 'vsctl-args' causes ovs-vsctl to provide # output (e.g. because it includes "create" commands) then 'vsctl-output' # specifies the expected output after filtering through uuidfilt. # 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init' # before starting ovs-vswitchd. m4_define([OVS_TRAFFIC_VSWITCHD_START], [ OVS_DPDK_PRE_CHECK() OVS_WAIT_WHILE([ip link show ovs-netdev]) dnl For functional tests, no need for DPDK PCI probing. OVS_DPDK_START([--no-pci], [--disable-system], [$3]) dnl Add bridges, ports, etc. OVS_WAIT_WHILE([ip link show br0]) AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) ]) # OVS_TRAFFIC_VSWITCHD_STOP([ALLOWLIST], [extra_cmds]) # # Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files # for messages with severity WARN or higher and signaling an error if any # is present. The optional ALLOWLIST may contain shell-quoted "sed" # commands to delete any warnings that are actually expected, e.g.: # # OVS_TRAFFIC_VSWITCHD_STOP(["/expected error/d"]) # # 'extra_cmds' are shell commands to be executed after OVS_VSWITCHD_STOP() is # invoked. They can be used to perform additional cleanups such as name space # removal. m4_define([OVS_TRAFFIC_VSWITCHD_STOP], [OVS_DPDK_STOP_VSWITCHD([$1]) AT_CHECK([:; $2]) ]) # Plug a veth into OVS via DPDK net/af_xdp. m4_define([ADD_VETH], [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77]) CONFIGURE_VETH_OFFLOADS([$1]) AT_CHECK([ip link set $1 netns $2]) AT_CHECK([ip link set dev ovs-$1 up]) AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \ set interface ovs-$1 external-ids:iface-id="$1" -- \ set interface ovs-$1 type=dpdk -- \ set interface ovs-$1 options:dpdk-devargs=net_af_xdp$1,iface=ovs-$1]) NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7]) NS_CHECK_EXEC([$2], [ip link set dev $1 up]) if test -n "$5"; then NS_CHECK_EXEC([$2], [ip link set dev $1 address $5]) fi if test -n "$6"; then NS_CHECK_EXEC([$2], [ip route add default via $6]) fi on_exit 'ip link del ovs-$1' ] ) m4_define([OVS_CHECK_8021AD], [AT_SKIP_IF([:])]) m4_define([OVS_CHECK_TC_QDISC], [AT_SKIP_IF([:])]) m4_define([CONFIGURE_VETH_OFFLOADS], [AT_CHECK([ethtool -K $1 tx off], [0], [ignore], [ignore]) AT_CHECK([ethtool -K $1 txvlan off], [0], [ignore], [ignore])] ) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-offloads-macros.at000066400000000000000000000173271514270232600260030ustar00rootroot00000000000000AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc. 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.]) # Macro to exclude tests that will fail with dpdk offload enabled. # We currently have the below tests disabled in system-traffic.at # for the following reasons: # # For dpdk offload_mutex there is no kernel side of the simulated VETH # interface. Therefore the following tests are disabled, as they assume # kernel ports exist since they have dependencies on their behavior. # - 'datapath - netdev offload software fallback' # # DPDK offload does not update flow statistics when a hardware offloaded flow # is removed, which means the statistics are incorrect. Until this changes, # the following test needs to be disabled: # - 'datapath - simulated flow action update' # # The dpdk offload test cases use VF interfaces, which are Ethernet devices # with a minimum frame size of 64 bytes. As a result, received packets appear # larger than expected. Virtual interfaces, however, report the actual size. # To avoid adding complex counter adjustments in tests that rely on these # values, we skip them. # - 'conntrack - ICMP related' # - 'conntrack - resubmit to ct multiple times' # - 'conntrack - ICMP related with NAT' # - 'conntrack - Multiple ICMP traverse' # # There is a kernel problem where it will not accept fragmented ICMPv6 packets # for the same session if they are batched together. Due to DUT-specific # circumstances, this can occasionally happen for the test below. To avoid # adding complex detection for this, we simply skip the test. # - 'conntrack - IPv6 fragmentation + vlan' # m4_define([CHECK_NO_DPDK_OFFLOAD], [ AT_SKIP_IF([:]) ]) # OVS_DPDK_OFFLOAD_PRE_CHECK() # # Check prerequisites for DPDK rte_flow tests. Following settings are checked: # - Existence of at least six VFs and their representors. # m4_define([OVS_DPDK_OFFLOAD_PRE_CHECK], [ OVS_DPDK_PRE_CHECK() AT_SKIP_IF( [test "$(printf '%s' "$OVS_DPDK_VF_PCI_ADDRS" | wc -w)" -ne 6]) AT_SKIP_IF([! ovs_dpdk_verify_vf_cfg "$OVS_DPDK_VF_PCI_ADDRS"]) ]) # OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args]) # # This is a copy from system-dpdk-macro.at with two small changes: # 1) Use OVS_DPDK_OFFLOAD_PRE_CHECK() instead of OVS_DPDK_PRE_CHECK(). # 2) Start with pci enabled for PF devices. # m4_define([OVS_TRAFFIC_VSWITCHD_START], [ OVS_DPDK_OFFLOAD_PRE_CHECK() OVS_WAIT_WHILE([ip link show ovs-netdev]) ALLOW_LIST=$(echo "$OVS_DPDK_VF_PCI_ADDRS" | awk '{ for (i=1; i<=NF; i++) { split($i, parts, ","); addr = parts[1]; if (addr != "" && !seen[addr]) { seen[addr] = 1; if (result == "") result = "--allow " addr; else result = result " --allow " addr; } } print result; }') OVS_DPDK_START([$ALLOW_LIST], [--disable-system], [-- set Open_vSwitch . other_config:hw-offload=true $3]) dnl Add bridges, ports, etc. OVS_WAIT_WHILE([ip link show br0]) AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) ]) # OVS_TRAFFIC_VSWITCHD_STOP([ALLOWLIST], [extra_cmds]) # # This is a copy from system-dpdk-macro.at adding some dpdk offload specific # error message exclusion. # m4_define([OVS_TRAFFIC_VSWITCHD_STOP], [OVS_DPDK_STOP_VSWITCHD([dnl $1";/mlx5_net: Failed to update link status: /d"]) AT_CHECK([:; $2]) ]) # ADD_{VF|VETH}([port], [namespace], [ovs-br], [ip_addr] [mac_addr], [gateway], # [ip_addr_flags]) # # Simulate the ADD_VETH() macro defined in system-common-macros.at, allowing # us to run the existing system-traffic unit tests without any test-specific # changes. This is consistent with how DPDK and AF_XDP run the system tests, # except that we are not using actual veth devices, but VF representer ports. # m4_define([ADD_VF], [ USER_PORT=$1 case "$USER_PORT" in client) PORT_NO=0 ;; server) PORT_NO=1 ;; *) PORT_NO=${USER_PORT##*[!0-9]} ;; esac AT_CHECK([[[ "$PORT_NO" -ge 0 ]] && [[ "$PORT_NO" -le 6 ]] || return 66]) PORT_CFG=$(echo $OVS_DPDK_VF_PCI_ADDRS | cut -d' ' -f$((PORT_NO+1))) PF_PCI=$(ovs_dpdk_get_pci_id "$PORT_CFG") VF_IDX=$(ovs_dpdk_get_vf_idx "$PORT_CFG") REP=$(ovs_dpdk_get_representor_netdev $PF_PCI $VF_IDX) AT_CHECK([test $? -eq 0]) AT_CHECK([ip link set $REP name $1]) AT_CHECK([ip link set $1 netns $2]) AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \ set interface ovs-$1 external-ids:iface-id="$1" -- \ set interface ovs-$1 type=dpdk -- \ set interface ovs-$1 \ options:dpdk-devargs=$PF_PCI,representor=vf$VF_IDX]) NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7]) NS_CHECK_EXEC([$2], [ip link set dev $1 up]) if test -n "$5"; then NS_CHECK_EXEC([$2], [ip link set dev $1 address $5]) else NS_CHECK_EXEC([$2], [ip link set dev $1 address 02:00:00:00:EC:0$PORT_NO]) fi if test -n "$6"; then NS_CHECK_EXEC([$2], [ip route add default via $6]) fi on_exit "ip netns exec $2 ip link set $1 netns 1; \ ip link property del dev $1 altname $REP; \ ip link set $1 name $REP" ] ) m4_define([ADD_VETH], [ADD_VF($@)]) # DUMP_DP_IP_CLEAN_SORTED() # # Clean up and sort the ovs-dpctl dump-flow output for comparing. # m4_define([DUMP_DP_IP_CLEAN_SORTED], [dnl grep 'eth_type(0x0800)' \ | sed -e 's/eth(src=[[a-z0-9:]]*,dst=[[a-z0-9:]]*)/eth(macs)/;s/recirc_id(0),//' \ | strip_used | strip_ptype | sort]) OVS_START_SHELL_HELPERS # ovs_dpdk_is_valid_pci_vf_addr() # # Check if the given PF PCI address and the VF number are valid. # ovs_dpdk_is_valid_pci_vf_addr() { PCI_ID='[[0-9a-fA-F]]{4}:[[0-9a-fA-F]]{2}:[[0-9a-fA-F]]{2}\.[[0-7]]' echo "$1" | grep -E -q "^$PCI_ID,[[0-9]]+$" && return 0 || return 1 } # ovs_dpdk_get_pci_id() # ovs_dpdk_get_pci_id() { printf '%s\n' "${1%%,*}" } # ovs_dpdk_get_vf_idx() # ovs_dpdk_get_vf_idx() { printf '%s\n' "${1##*,}" } # ovs_dpdk_get_representor_netdev(, ) # # This function tries to find the representor netdev for the given PF's VF. # ovs_dpdk_get_representor_netdev() { PF_PCI=$1 VF_IDX=$2 VF_NET_DIR="/sys/bus/pci/devices/$PF_PCI/virtfn$VF_IDX/net" if [[ ! -d "$VF_NET_DIR" ]]; then echo "ERROR: VF $VF_IDX for PF $PF_PCI does not exist" >&2 return 1 fi for iface in "$VF_NET_DIR"/*; do if [[ -e "$iface" ]]; then basename "$iface" return 0 fi done echo "ERROR: No representor netdev found for VF $VF_IDX on PF $PF_PCI" >&2 return 1 } # ovs_dpdk_verify_vf_cfg() # # Verify that the given PF PCI addresses and corresponding VF IDs in # OVS_DPDK_VF_PCI_ADDRS are valid, exist, and have corresponding # representor ports. # ovs_dpdk_verify_vf_cfg() { i=0 for addr in $1; do ovs_dpdk_is_valid_pci_vf_addr "$addr" || return 1 PCI_ID=$(ovs_dpdk_get_pci_id "$addr") VF_IDX=$(ovs_dpdk_get_vf_idx "$addr") REP=$(ovs_dpdk_get_representor_netdev $PCI_ID $VF_IDX) || return 1 echo "ovs-p$i: PF PCI $PCI_ID with VF $VF_IDX has representor $REP" i=$((i + 1)) done return 0 } OVS_END_SHELL_HELPERS openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-offloads-testsuite.at000066400000000000000000000017561514270232600265470ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-userspace-macros.at]) m4_include([tests/system-dpdk-macros.at]) m4_include([tests/system-dpdk-offloads-macros.at]) m4_include([tests/system-dpdk-offloads.at]) m4_include([tests/system-traffic.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-offloads.at000066400000000000000000000252631514270232600245170ustar00rootroot00000000000000AT_COPYRIGHT([Copyright (c) 2025 Red Hat, Inc. 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.]) AT_BANNER([dpdk's rte_flow offload unit tests]) AT_SETUP([dpdk offload - ping between two hardware offloaded ports]) OVS_DPDK_OFFLOAD_PRE_CHECK() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VF(p0, at_ns0, br0, "10.1.1.1/24") ADD_VF(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 \ | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_WAIT_UNTIL_EQUAL( [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \ | DUMP_DP_IP_CLEAN_SORTED], [dnl in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP m4_define([DPDK_OFFLOAD_SIX_PORT_PING_TEST], [dnl AT_SETUP($1) OVS_DPDK_OFFLOAD_PRE_CHECK() OVS_TRAFFIC_VSWITCHD_START($2) m4_ifval([$2], [CHECK_CPU_DISCOVERED([14])]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:max-idle=20000]) ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3, at_ns4, at_ns5) ADD_VF(p0, at_ns0, br0, "10.1.1.1/24") ADD_VF(p1, at_ns1, br0, "10.1.1.2/24") ADD_VF(p2, at_ns2, br0, "10.1.1.3/24") ADD_VF(p3, at_ns3, br0, "10.1.1.4/24") ADD_VF(p4, at_ns4, br0, "10.1.1.5/24") ADD_VF(p5, at_ns5, br0, "10.1.1.6/24") for NS in $(seq 0 5); do START=$((NS + 2)) for IP in $(seq "$START" 6); do NS_CHECK_EXEC([at_ns$NS], [ping -q -c 3 -i 0.3 -W 2 10.1.1.$IP \ | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) done done OVS_WAIT_UNTIL_EQUAL( [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \ | DUMP_DP_IP_CLEAN_SORTED], [dnl in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p2 in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p3 in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p4 in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p5 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p2 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p3 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p4 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p5 in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0 in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p3 in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p4 in_port(ovs-p2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p5 in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0 in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p2 in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p4 in_port(ovs-p3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p5 in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0 in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p2 in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p3 in_port(ovs-p4),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p5 in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p0 in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p1 in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p2 in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p3 in_port(ovs-p5),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0s, actions:ovs-p4]) AT_CHECK([ovs-appctl dpctl/offload-stats-show], [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpif-netdev/pmd-rxq-show], [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP ]) DPDK_OFFLOAD_SIX_PORT_PING_TEST( [dpdk offload - ping between six hardware offloaded ports]) DPDK_OFFLOAD_SIX_PORT_PING_TEST( [dpdk offload - ping with multiple offload threads], [-- set Open_vSwitch . other_config:pmd-cpu-mask=0x3ffc \ -- set Open_vSwitch . other_config:n-offload-threads=10]) AT_SETUP([dpdk offload - partial offload]) OVS_DPDK_OFFLOAD_PRE_CHECK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns1, at_ns2, at_ns3, at_ns4) ADD_VETH(p1, at_ns1, br0, "10.1.1.1/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.2/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.3/24") ADD_VETH(p4, at_ns4, br0, "10.1.1.4/24") AT_DATA([flows.txt], [dnl table=0,ipv6, actions=drop table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=output:3 table=4,in_port=1,reg0=0x0 actions=output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 \ | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) AT_CHECK([ovs-appctl dpctl/dump-flows type=dpdk,partially-offloaded \ | grep "check_pkt_len" | DUMP_DP_IP_CLEAN_SORTED], [0], [dnl in_port(2),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:954, used:0.0s, actions:check_pkt_len(size=200,gt(4),le(5)),3 ]) AT_CHECK([test $(ovs-appctl dpif-netdev/pmd-stats-show | \ awk '/phwol hits:/ {sum += $3} END {print sum}') -ge 8]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([dpdk offload - flow modification non-offload -> offload]) OVS_DPDK_OFFLOAD_PRE_CHECK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2) ADD_VF(p0, at_ns0, br0, "10.1.1.1/24") ADD_VF(p1, at_ns1, br0, "10.1.1.2/24") ADD_VF(p2, at_ns2, br0, "10.1.1.3/24") NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns2], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=f0:00:00:01:01:01,eth_dst=f0:00:00:01:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) m4_define([ICMP_PKT_REPLY], [m4_join([,], [eth_src=f0:00:00:01:01:02,eth_dst=f0:00:00:01:01:01,eth_type=0x0800], [nw_src=10.1.1.2,nw_dst=10.1.1.1], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) # Send a packet in one direction; this should create a non-offloadable # flow between br0 and ovs-p1. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT' '')], [0], [ignore]) OVS_WAIT_UNTIL_EQUAL( [ovs-appctl dpctl/dump-flows --names type=partially-offloaded \ | grep "f0:00:00:01:01:01" | DUMP_DP_IP_CLEAN_SORTED], [dnl in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:br0,ovs-p1,ovs-p2]) # Send the reply packet. A new flow should be inserted in hardware for the # reverse direction (since the FDB entry for the destination is known). # However, the revalidator should update the originating flow, causing it to # be fully offloaded. NS_CHECK_EXEC([at_ns1], [$PYTHON3 $srcdir/sendpkt.py p1 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT_REPLY' '')], [0], [ignore]) OVS_WAIT_UNTIL_EQUAL( [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \ | grep "f0:00:00:01:01:01" | DUMP_DP_IP_CLEAN_SORTED], [dnl in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:ovs-p1 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:ovs-p0]) # Send 50 packets in each direction, and verify the packets are handled by # the hardware rather than by the PMDs. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py -c 50 p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT' '')], [0], [ignore]) NS_CHECK_EXEC([at_ns1], [$PYTHON3 $srcdir/sendpkt.py -c 50 p1 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT_REPLY' '')], [0], [ignore]) OVS_WAIT_UNTIL_EQUAL( [ovs-appctl dpctl/dump-flows --names type=dpdk,offloaded \ | grep "f0:00:00:01:01:01" | DUMP_DP_IP_CLEAN_SORTED], [dnl in_port(ovs-p0),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:50, bytes:3000, used:0.0s, actions:ovs-p1 in_port(ovs-p1),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:50, bytes:3000, used:0.0s, actions:ovs-p0]) AT_CHECK([test $(ovs-appctl dpif-netdev/pmd-stats-show | \ awk '/packets received:/ {sum += $3} END {print sum}') -lt 10]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk-testsuite.at000066400000000000000000000016661514270232600247540ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2017 Intel Corporation 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-userspace-macros.at]) m4_include([tests/system-dpdk-macros.at]) m4_include([tests/system-dpdk.at]) m4_include([tests/system-traffic.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-dpdk.at000066400000000000000000001160441514270232600227220ustar00rootroot00000000000000AT_BANNER([OVS-DPDK unit tests]) dnl CHECK_MEMPOOL_PARAM([mtu], [numa], [+line]) dnl dnl Waits for logs to indicate that the user has configured a mempool dnl for 'mtu' on 'numa'. Checking starts from line number 'line' in dnl ovs-vswitchd.log. m4_define([CHECK_MEMPOOL_PARAM], [ line_st=$3 if [[ -z "$line_st" ]] then line_st="+0" fi OVS_WAIT_UNTIL([tail -n $line_st ovs-vswitchd.log dnl | grep "User configured shared mempool set for: MTU $1, NUMA $2."]) ]) dnl ADD_VHOST_USER_CLIENT_PORT([bridge], [port], [socket]) dnl Add a dpdk vhost-user client port to a bridge and check this port is ready dnl to be used by looking at the logs. m4_define([ADD_VHOST_USER_CLIENT_PORT], [ AT_CHECK([ovs-vsctl add-port $1 $2 -- \ set Interface $2 type=dpdkvhostuserclient options:vhost-server-path=$3], [], [stdout], [stderr]) OVS_WAIT_UNTIL([grep "VHOST_CONFIG: ($3) vhost-user client: socket created" ovs-vswitchd.log]) OVS_WAIT_UNTIL([grep "vHost User device '$2' created in 'client' mode, using client socket" ovs-vswitchd.log]) OVS_WAIT_UNTIL([grep "VHOST_CONFIG: ($3) reconnecting..." ovs-vswitchd.log]) ]) dnl ADD_VHOST_USER_PORT([bridge], [port], [socket]) dnl Add a dpdk vhost-user port to a bridge and check this port is ready dnl to be used by looking at the logs. m4_define([ADD_VHOST_USER_PORT], [ AT_CHECK([ovs-vsctl add-port $1 $2 -- set Interface $2 type=dpdkvhostuser], [], [stdout], [stderr]) OVS_WAIT_UNTIL([grep "VHOST_CONFIG: ($3) vhost-user server: socket created" ovs-vswitchd.log]) OVS_WAIT_UNTIL([grep "Socket $3 created for vhost-user port $2" ovs-vswitchd.log]) OVS_WAIT_UNTIL([grep "VHOST_CONFIG: ($3) binding succeeded" ovs-vswitchd.log]) ]) dnl -------------------------------------------------------------------------- dnl Check if EAL init is successful AT_SETUP([OVS-DPDK - EAL init]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) AT_CHECK([grep "DPDK Enabled - initializing..." ovs-vswitchd.log], [], [stdout]) AT_CHECK([grep "EAL" ovs-vswitchd.log], [], [stdout]) AT_CHECK([grep "DPDK Enabled - initialized" ovs-vswitchd.log], [], [stdout]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Check dpdk-lcore-mask conversion for only first bit AT_SETUP([OVS-DPDK - dpdk-lcore-mask conversion - single]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START_OVSDB() OVS_DPDK_START_VSWITCHD([--no-pci]) CHECK_CPU_DISCOVERED() AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0x1]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) expected_lcores="lcores 0@0" OVS_WAIT_UNTIL([grep "$expected_lcores" ovs-vswitchd.log]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Check dpdk-lcore-mask conversion for only multiple bits AT_SETUP([OVS-DPDK - dpdk-lcore-mask conversion - multi]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START_OVSDB() OVS_DPDK_START_VSWITCHD([--no-pci]) CHECK_CPU_DISCOVERED(4) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0xf]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) expected_lcores="lcores 0@0,1@1,2@2,3@3" OVS_WAIT_UNTIL([grep "$expected_lcores" ovs-vswitchd.log]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Check dpdk-lcore-mask conversion for max length string AT_SETUP([OVS-DPDK - dpdk-lcore-mask conversion - non-contig]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START_OVSDB() OVS_DPDK_START_VSWITCHD([--no-pci]) CHECK_CPU_DISCOVERED(8) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0xca]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) expected_lcores="lcores 0@1,1@3,2@6,3@7" OVS_WAIT_UNTIL([grep "$expected_lcores" ovs-vswitchd.log]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Check dpdk-lcore-mask conversion for zero AT_SETUP([OVS-DPDK - dpdk-lcore-mask conversion - zeromask]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START_OVSDB() OVS_DPDK_START_VSWITCHD([--no-pci]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0x0]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) OVS_WAIT_UNTIL([grep "Ignoring database defined option 'dpdk-lcore-mask' due to invalid value '0x0'" ovs-vswitchd.log]) expected_lcores="lcores 0@0" OVS_WAIT_UNTIL([grep "$expected_lcores" ovs-vswitchd.log]) OVS_DPDK_STOP_VSWITCHD(["/Ignoring database defined option/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Add standard DPDK PHY port AT_SETUP([OVS-DPDK - add standard DPDK port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl Add userspace bridge and attach it to OVS AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Add vhost-user-client port AT_SETUP([OVS-DPDK - add vhost-user-client port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Check that no mempool was allocated. AT_CHECK([ovs-appctl netdev-dpdk/get-mempool-info dpdkvhostuserclient0], [2], [], [dnl Not allocated ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ping vhost-user port AT_SETUP([OVS-DPDK - ping vhost-user ports]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_PORT([br10], [dpdkvhostuser0], [$OVS_RUNDIR/dpdkvhostuser0]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Set up namespaces ADD_NAMESPACES(ns1, ns2) dnl Add veth device ADD_VETH(tap1, ns2, br10, "172.31.110.12/24") OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostuser0" \ --vdev="net_tap0,iface=tap0"]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ip link show dev tap0 | grep -qw LOWER_UP]) dnl Move the tap devices to the namespaces AT_CHECK([ps aux | grep testpmd], [], [stdout], [stderr]) AT_CHECK([ip link show], [], [stdout], [stderr]) AT_CHECK([ip link set tap0 netns ns1], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show | grep tap0], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link set tap0 up], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip addr add 172.31.110.11/24 dev tap0], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns2 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ping -c 4 -I tap0 172.31.110.12], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() dnl Wait for vhost-user handling the socket disconnect. OVS_WAIT_UNTIL([grep "vHost Device '$OVS_RUNDIR/dpdkvhostuser0' has been removed" ovs-vswitchd.log]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuser0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostuser0) recvmsg failed/d /VHOST_CONFIG: (.*dpdkvhostuser0) failed to connect: No such file or directory/d /dpdkvhostuser ports are considered deprecated; please migrate to dpdkvhostuserclient ports./d /failed to enumerate system datapaths: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ping vhost-user-client port AT_SETUP([OVS-DPDK - ping vhost-user-client ports]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Set up namespaces ADD_NAMESPACES(ns1, ns2) dnl Add veth device ADD_VETH(tap1, ns2, br10, "172.31.110.12/24") OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostclient0,queues=2,server=1" \ --vdev="net_tap0,iface=tap0" -- --nb-cores 2 --rxq 2 --txq 2]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ip link show dev tap0 | grep -qw LOWER_UP]) dnl Move the tap devices to the namespaces AT_CHECK([ps aux | grep testpmd], [], [stdout], [stderr]) AT_CHECK([ip link show], [], [stdout], [stderr]) AT_CHECK([ip link set tap0 netns ns1], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show | grep tap0], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link set tap0 up], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip addr add 172.31.110.11/24 dev tap0], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns2 ip link show], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ping -i 0.1 -c 10 -I tap0 172.31.110.12], [], [stdout], [stderr]) AT_CHECK([ip netns exec ns1 ip link set tap0 down], [], [stdout], [stderr]) # Wait for stats to be queried ("stats-update-interval") sleep 5 AT_CHECK([ovs-vsctl get interface dpdkvhostuserclient0 statistics], [], [stdout], [stderr]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_packets` -gt 0 -a dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_bytes` -gt 0]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_packets` -eq dnl $((`ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_q0_good_packets` + dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_q1_good_packets`))]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_bytes` -eq dnl $((`ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_q0_good_bytes` + dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:rx_q1_good_bytes`))]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_packets` -gt 0 -a dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_bytes` -gt 0]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_packets` -eq dnl $((`ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_q0_good_packets` + dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_q1_good_packets`))]) AT_CHECK([test `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_bytes` -eq dnl $((`ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_q0_good_bytes` + dnl `ovs-vsctl get interface dpdkvhostuserclient0 statistics:tx_q1_good_bytes`))]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) recvmsg failed/d /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d /failed to enumerate system datapaths: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ingress policing create delete phy port AT_SETUP([OVS-DPDK - Ingress policing create delete phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl Add userspace bridge and attach it to OVS and add policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl set interface phy0 ingress_policing_rate=10000 ingress_policing_burst=1000]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Fail if policer could not be created AT_FAIL_IF([grep "Could not create rte meter for ingress policer" ovs-vswitchd.log], [], [stdout]) dnl Remove policer AT_CHECK([ovs-vsctl set interface phy0 ingress_policing_rate=0 ingress_policing_burst=0]) dnl Check policer was removed correctly AT_CHECK([ovs-vsctl list interface phy0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_burst: 0' stdout], [], [stdout]) AT_CHECK([ovs-vsctl list interface phy0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_rate: 0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ingress policing create delete vport port AT_SETUP([OVS-DPDK - Ingress policing create delete vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add ingress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set interface dpdkvhostuserclient0 ingress_policing_rate=10000 ingress_policing_burst=1000]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Fail if ingress policer could not be created AT_FAIL_IF([grep "Could not create rte meter for ingress policer" ovs-vswitchd.log], [], [stdout]) dnl Remove ingress policer AT_CHECK([ovs-vsctl set interface dpdkvhostuserclient0 ingress_policing_rate=0 ingress_policing_burst=0]) dnl Check ingress policer was removed correctly AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_burst: 0' stdout], [], [stdout]) AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_rate: 0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ingress policing no policing rate AT_SETUP([OVS-DPDK - Ingress policing no policing rate]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add ingress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set interface dpdkvhostuserclient0 ingress_policing_burst=1000]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Fail if ingress policer could not be created AT_FAIL_IF([grep "Could not create rte meter for ingress policer" ovs-vswitchd.log], [], [stdout]) dnl Check ingress policer was created correctly AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_burst: 1000' stdout], [], [stdout]) AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_rate: 0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Ingress policing no policing burst AT_SETUP([OVS-DPDK - Ingress policing no policing burst]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add ingress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set interface dpdkvhostuserclient0 ingress_policing_rate=10000]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Fail if ingress policer could not be created AT_FAIL_IF([grep "Could not create rte meter for ingress policer" ovs-vswitchd.log], [], [stdout]) dnl Check ingress policer was created correctly AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_burst: 0' stdout], [], [stdout]) AT_CHECK([ovs-vsctl list interface dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'ingress_policing_rate: 10000' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl QoS create delete phy port AT_SETUP([OVS-DPDK - QoS create delete phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl Add userspace bridge and attach it to OVS and add egress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) OVS_WAIT_UNTIL([ovs-vsctl set port phy0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:cir=1250000 other-config:cbs=2048]) AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show phy0], [], [stdout]) sleep 2 dnl Fail if egress policer could not be created AT_FAIL_IF([grep "Could not create rte meter for egress policer" ovs-vswitchd.log], [], [stdout]) dnl Remove egress policer AT_CHECK([ovs-vsctl destroy QoS phy0 -- clear Port phy0 qos]) dnl Check egress policer was removed correctly AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show phy0], [], [stdout]) AT_CHECK([grep -E 'QoS not configured on phy0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl QoS create delete vport port AT_SETUP([OVS-DPDK - QoS create delete vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add egress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:cir=1250000 \ other-config:cbs=2048]) AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout]) sleep 2 dnl Fail if egress policer could not be created AT_FAIL_IF([grep "Could not create rte meter for egress policer" ovs-vswitchd.log], [], [stdout]) dnl Remove egress policer AT_CHECK([ovs-vsctl destroy QoS dpdkvhostuserclient0 -- clear Port dpdkvhostuserclient0 qos]) dnl Check egress policer was removed correctly AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'QoS not configured on dpdkvhostuserclient0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl QoS no cir AT_SETUP([OVS-DPDK - QoS no cir]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add egress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:cbs=2048]) sleep 2 dnl Check egress policer was not created AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'QoS not configured on dpdkvhostuserclient0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d /Could not create rte meter for egress policer/d /Failed to set QoS type egress-policer on port dpdkvhostuserclient0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl QoS no cbs AT_SETUP([OVS-DPDK - QoS no cbs]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and add egress policer AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) OVS_WAIT_UNTIL([ovs-vsctl set port dpdkvhostuserclient0 qos=@newqos -- --id=@newqos create qos type=egress-policer other-config:cir=1250000]) sleep 2 dnl Check egress policer was not created AT_CHECK([ovs-appctl -t ovs-vswitchd qos/show dpdkvhostuserclient0], [], [stdout]) AT_CHECK([grep -E 'QoS not configured on dpdkvhostuserclient0' stdout], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d /Could not create rte meter for egress policer/d /Failed to set QoS type egress-policer on port dpdkvhostuserclient0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU increase phy port AT_SETUP([OVS-DPDK - MTU increase phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl First set MTU to its default value and confirm that value, then increase the MTU value and confirm the new value dnl Add userspace bridge and attach it to OVS with default MTU value AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Check default MTU value in the datapath OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [1500]) dnl Increase MTU value and check in the datapath AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=9000]) OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [9000]) dnl Fail if MTU is not supported AT_FAIL_IF([grep "Interface phy0 does not support MTU configuration" ovs-vswitchd.log], [], [stdout]) dnl Fail if error is encountered during MTU setup AT_FAIL_IF([grep "Interface phy0 MTU (9000) setup error" ovs-vswitchd.log], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU decrease phy port AT_SETUP([OVS-DPDK - MTU decrease phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl First set an increased MTU value and confirm that value, then decrease the MTU value and confirm the new value dnl Add userspace bridge and attach it to OVS and modify MTU value AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=9000]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Check MTU value in the datapath OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [9000]) dnl Fail if MTU is not supported AT_FAIL_IF([grep "Interface phy0 does not support MTU configuration" ovs-vswitchd.log], [], [stdout]) dnl Fail if error is encountered during MTU setup AT_FAIL_IF([grep "Interface phy0 MTU (9000) setup error" ovs-vswitchd.log], [], [stdout]) dnl Decrease MTU value and check in the datapath AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=2000]) OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [2000]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU increase vport port AT_SETUP([OVS-DPDK - MTU increase vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS with default MTU value AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostclient0,server=1"]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ovs-vsctl get Interface dpdkvhostuserclient0 link_state | grep -w up]) dnl Check default MTU value in the datapath AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 1500 ]) dnl Increase MTU value and check in the datapath AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=9000]) AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 9000 ]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU decrease vport port AT_SETUP([OVS-DPDK - MTU decrease vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and modify MTU value AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=9000]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostclient0,server=1"]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ovs-vsctl get Interface dpdkvhostuserclient0 link_state | grep -w up]) dnl Check MTU value in the datapath AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 9000 ]) dnl Decrease MTU value and check in the datapath AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=2000]) AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 2000 ]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU upper bound phy port AT_SETUP([OVS-DPDK - MTU upper bound phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl Add userspace bridge and attach it to OVS and set MTU value to max upper bound AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=9702]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Check MTU value in the datapath OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [9702]) dnl Fail if MTU is not supported AT_FAIL_IF([grep "Interface phy0 does not support MTU configuration" ovs-vswitchd.log], [], [stdout]) dnl Fail if error is encountered during MTU setup AT_FAIL_IF([grep "Interface phy0 MTU (9702) setup error" ovs-vswitchd.log], [], [stdout]) dnl Set MTU value above upper bound and check for error AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=9711]) AT_CHECK([grep "phy0: unsupported MTU 9711" ovs-vswitchd.log], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /phy0: unsupported MTU 9711/d /failed to set MTU for network device phy0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU lower bound phy port AT_SETUP([OVS-DPDK - MTU lower bound phy port]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_PHY_SKIP() OVS_DPDK_START() dnl Add userspace bridge and attach it to OVS and set MTU value to min lower bound AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 phy0 -- set Interface phy0 type=dpdk options:dpdk-devargs=$(cat DPDK_PCI_ADDR)], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=68]) AT_CHECK([ovs-vsctl show], [], [stdout]) dnl Check MTU value in the datapath OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get Interface phy0 mtu], [68]) dnl Fail if MTU is not supported AT_FAIL_IF([grep "Interface phy0 does not support MTU configuration" ovs-vswitchd.log], [], [stdout]) dnl Fail if error is encountered during MTU setup AT_FAIL_IF([grep "Interface phy0 MTU (68) setup error" ovs-vswitchd.log], [], [stdout]) dnl Set MTU value below lower bound and check for error AT_CHECK([ovs-vsctl set Interface phy0 mtu_request=67]) AT_CHECK([grep "phy0: unsupported MTU 67" ovs-vswitchd.log], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 phy0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl /phy0: unsupported MTU 67/d /failed to set MTU for network device phy0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU upper bound vport port AT_SETUP([OVS-DPDK - MTU upper bound vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and set MTU value to max upper bound AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=9702]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostclient0,server=1"]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ovs-vsctl get Interface dpdkvhostuserclient0 link_state | grep -w up]) dnl Check MTU value in the datapath AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 9702 ]) dnl Set MTU value above upper bound and check for error AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=9711]) AT_CHECK([grep "dpdkvhostuserclient0: unsupported MTU 9711" ovs-vswitchd.log], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d /dpdkvhostuserclient0: unsupported MTU 9711/d /failed to set MTU for network device dpdkvhostuserclient0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl MTU lower bound vport port AT_SETUP([OVS-DPDK - MTU lower bound vport port]) AT_KEYWORDS([dpdk]) OVS_DPDK_CHECK_TESTPMD() OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) dnl Add userspace bridge and attach it to OVS and set MTU value to min lower bound AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) ADD_VHOST_USER_CLIENT_PORT([br10], [dpdkvhostuserclient0], [$OVS_RUNDIR/dpdkvhostclient0]) AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=68]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 OVS_DPDK_START_TESTPMD([--vdev="net_virtio_user,path=$OVS_RUNDIR/dpdkvhostclient0,server=1"]) OVS_WAIT_UNTIL([grep "virtio is now ready for processing" ovs-vswitchd.log]) OVS_WAIT_UNTIL([ovs-vsctl get Interface dpdkvhostuserclient0 link_state | grep -w up]) dnl Check MTU value in the datapath AT_CHECK([ovs-vsctl get Interface dpdkvhostuserclient0 mtu], [0], [dnl 68 ]) dnl Set MTU value below lower bound and check for error AT_CHECK([ovs-vsctl set Interface dpdkvhostuserclient0 mtu_request=67]) AT_CHECK([grep "dpdkvhostuserclient0: unsupported MTU 67" ovs-vswitchd.log], [], [stdout]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_TESTPMD() OVS_DPDK_STOP_VSWITCHD(["dnl /VHOST_CONFIG: (.*dpdkvhostclient0) failed to connect: No such file or directory/d /dpdkvhostuserclient0: unsupported MTU 67/d /failed to set MTU for network device dpdkvhostuserclient0: Invalid argument/d"]) AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- dnl Setup user configured mempools AT_SETUP([OVS-DPDK - user configured mempool]) AT_KEYWORDS([dpdk]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START_OVSDB() OVS_DPDK_START_VSWITCHD([--no-pci]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:shared-mempool-config=8000,6000,1500]) AT_CHECK([ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true]) CHECK_MEMPOOL_PARAM([8000], [ALL], []) CHECK_MEMPOOL_PARAM([6000], [ALL], []) CHECK_MEMPOOL_PARAM([1500], [ALL], []) AT_CHECK(ovs-appctl vlog/set netdev_dpdk:dbg) dnl Add userspace bridge and a dpdk port AT_CHECK([ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br10 p1 -- set Interface p1 type=dpdk options:dpdk-devargs=net_null0,no-rx=1], [], [stdout], [stderr]) AT_CHECK([ovs-vsctl show], [], [stdout]) sleep 2 dnl Check if the right user configured mempool is found for default MTU (1500) AT_CHECK([grep "Found user configured shared mempool .* suitable for port with MTU 1500" ovs-vswitchd.log], [], [stdout]) AT_CHECK([grep "Port p1: Requesting a mempool" ovs-vswitchd.log], [], [stdout]) dnl Change the MTU value to 7000 to trigger mempool change TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK(ovs-vsctl set Interface p1 mtu_request=7000) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Found user configured shared mempool .* suitable for port with MTU 7000"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Port p1: Requesting a mempool"]) dnl Change back the MTU value to 1500 to trigger mempool change (re-use) TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK(ovs-vsctl set Interface p1 mtu_request=1500) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Found user configured shared mempool .* suitable for port with MTU 1500"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Reusing mempool"]) dnl Change the MTU value beyond the max value in shared-mempool-config list TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1)) AT_CHECK(ovs-vsctl set Interface p1 mtu_request=9000) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "No user configured shared mempool mbuf sizes found suitable for port with MTU 9000"]) OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Port p1: Requesting a mempool"]) dnl Clean up AT_CHECK([ovs-vsctl del-port br10 p1], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- dnl -------------------------------------------------------------------------- AT_SETUP([OVS-DPDK - ovs-appctl dpif/offload/show]) AT_KEYWORDS([dpdk dpif-offload]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) AT_CHECK([ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev]) AT_CHECK([ovs-vsctl add-port br0 p1 \ -- set Interface p1 type=dpdk options:dpdk-devargs=net_null0,no-rx=1], [], [stdout], [stderr]) AT_CHECK([ovs-appctl dpif/offload/show], [0], [dnl Globally enabled: false Datapaths: netdev@ovs-netdev: dpdk - p1: port_no: 2 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "enabled": false, "netdev@ovs-netdev": { "priority": [[ "dpdk"]], "providers": { "dpdk": { "ports": { "p1": { "port_no": 2}}}}}} ]) OVS_DPDK_STOP_VSWITCHD AT_CLEANUP dnl -------------------------------------------------------------------------- openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-interface.at000066400000000000000000000146721514270232600237440ustar00rootroot00000000000000AT_BANNER([system-inteface]) dnl add a veth interface to br0, then delete and re-create dnl the veth interface with the same name in the system AT_SETUP([interface - add delete add same interface]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1]) on_exit 'ip link del ovs-veth0' AT_CHECK([ovs-vsctl add-port br0 ovs-veth0]) AT_CHECK([ip link del ovs-veth0]) AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1]) AT_CHECK([ovs-vsctl del-port br0 ovs-veth0]) OVS_TRAFFIC_VSWITCHD_STOP(["dnl /could not open network device ovs-veth0/d /cannot get .*STP status on nonexistent port/d /ethtool command .*on network device ovs-veth0 failed/d /error receiving .*ovs-veth0/d /ovs-veth0: removing policing failed/d"]) AT_CLEANUP dnl add a p1-0 interface to br-p1, then add a route to br-p1 and stop the OvS dnl instance. Confirm br-p1 interface has been deleted from the system. AT_SETUP([interface - add route to br and verify clean-up]) OVS_TRAFFIC_VSWITCHD_START() HWADDR_BRP1=aa:55:00:00:00:01 dnl Create tap port to later add to br-p1 AT_CHECK([ip tuntap add name p1-0 mode tap]) AT_CHECK([ip link set p1-0 up]) on_exit 'ip link del p1-0' AT_CHECK([ ovs-vsctl add-br br-p1 -- \ set bridge br-p1 datapath_type=netdev fail-mode=standalone other-config:hwaddr=$HWADDR_BRP1 ovs-vsctl add-port br-p1 p1-0 ovs-ofctl del-flows br-p1 ], [0]) AT_CHECK([ ip addr add 10.0.0.1/24 dev br-p1 ip link set br-p1 up ], [0], [stdout]) AT_CHECK([ ovs-appctl ovs/route/add 10.0.0.0/24 br-p1 ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 ], [0], [stdout]) OVS_TRAFFIC_VSWITCHD_STOP AT_CHECK([ ip link show br-p1], [1], [stdout], [Device "br-p1" does not exist.] ) AT_CLEANUP AT_SETUP([interface - datapath ports garbage collection]) OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() dnl Not relevant for userspace datapath. AT_SKIP_IF([! ovs-appctl dpctl/show | grep -q ovs-system]) AT_CHECK([ovs-vsctl add-port br0 tunnel_port dnl -- set Interface tunnel_port dnl type=geneve options:remote_ip=flow options:key=123]) AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1]) on_exit 'ip link del ovs-veth0' AT_CHECK([ovs-vsctl add-port br0 ovs-veth0]) OVS_WAIT_UNTIL([ip link show | grep -q " genev_sys_[[0-9]]*: .* ovs-system "]) dnl Store the output of ip link for geneve port to compare ifindex later. AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " > geneve.0]) AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl port 0: ovs-system (internal) port 1: br0 (internal) port 2: genev_sys_6081 (geneve: packet_type=ptap) port 3: ovs-veth0 ]) OVS_APP_EXIT_AND_WAIT_BY_TARGET([ovs-vswitchd], [ovs-vswitchd.pid]) dnl Check that geneve backing interface is still in the datapath. AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " | diff -u - geneve.0]) dnl Remove the veth port from the database while ovs-vswitchd is down. AT_CHECK([ovs-vsctl --no-wait del-port ovs-veth0]) dnl Check that it is still tied to the OVS datapath. AT_CHECK([ip link show ovs-veth0 | grep -q ovs-system]) dnl Bring ovs-vswitchd back up. AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --log-file -vdpif:dbg], [0], [], [stderr]) dnl Wait for the veth port to be removed from the datapath. OVS_WAIT_WHILE([ip link show ovs-veth0 | grep -q ovs-system]) AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl port 0: ovs-system (internal) port 1: br0 (internal) port 2: genev_sys_6081 (geneve: packet_type=ptap) ]) dnl Check that geneve backing interface is still in the datapath and it wasn't dnl re-created, i.e. the ifindex is the same. AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " | diff -u - geneve.0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([interface - datapath port rename]) OVS_TRAFFIC_VSWITCHD_START() dnl Not relevant for userspace datapath. AT_SKIP_IF([! ovs-appctl dpctl/show | grep -q ovs-system]) AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1]) dnl We will rename ovs-veth0, so removing the peer on exit. on_exit 'ip link del ovs-veth1' AT_CHECK([ovs-vsctl add-port br0 ovs-veth0]) OVS_WAIT_UNTIL([ip link show | grep -q "ovs-veth0.* ovs-system "]) AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl port 0: ovs-system (internal) port 1: br0 (internal) port 2: ovs-veth0 ]) dnl Rename the interface while attached to OVS. AT_CHECK([ip l set ovs-veth0 name ovs-new-port]) dnl Wait for the port to be detached from the OVS datapath. OVS_WAIT_UNTIL([ip link show | grep "ovs-new-port" | grep -v "ovs-system"]) dnl Check that database indicates the error. AT_CHECK([ovs-vsctl get interface ovs-veth0 error], [0], [dnl "could not open network device ovs-veth0 (No such device)" ]) dnl Check that the port is no longer in the datapath. AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl port 0: ovs-system (internal) port 1: br0 (internal) ]) dnl Rename the interface back and check that it is in use again. AT_CHECK([ip l set ovs-new-port name ovs-veth0]) OVS_WAIT_UNTIL([ip link show | grep -q "ovs-veth0.* ovs-system "]) AT_CHECK([ovs-vsctl get interface ovs-veth0 error], [0], [dnl [[]] ]) AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl port 0: ovs-system (internal) port 1: br0 (internal) port 2: ovs-veth0 ]) OVS_TRAFFIC_VSWITCHD_STOP([" /could not open network device ovs-veth0 (No such device)/d "]) AT_CLEANUP AT_SETUP([interface - current speed]) AT_SKIP_IF([test $HAVE_ETHTOOL = "no"]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ip tuntap add tap0 mode tap]) on_exit 'ip tuntap del tap0 mode tap' AT_CHECK([ip link set dev tap0 address aa:55:aa:55:00:01]) AT_CHECK([ethtool -s tap0 speed 50000 duplex full]) AT_CHECK([ip link set dev tap0 up]) AT_CHECK([ovs-vsctl add-port br0 tap0 -- set int tap0 type=tap]) AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn dump-ports-desc br0 tap0], [0], [stdout]) AT_CHECK([strip_xids < stdout], [0], [dnl OFPST_PORT_DESC reply (OF1.5): 1(tap0): addr:aa:55:aa:55:00:01 config: 0 state: LIVE current: OTHER COPPER speed: 50000 Mbps now, 0 Mbps max ]) AT_CHECK([ovs-vsctl get interface tap0 link_speed], [0], [dnl 50000000000 ]) AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl full ]) AT_CHECK([ip link set dev tap0 down]) AT_CHECK([ethtool -s tap0 duplex half]) AT_CHECK([ip link set dev tap0 up]) AT_CHECK([ovs-vsctl get interface tap0 duplex], [0], [dnl half ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-ipsec.at000066400000000000000000001166761514270232600231160ustar00rootroot00000000000000AT_BANNER(IPsec) dnl IPSEC_SETUP_UNDERLAY() dnl dnl Configure anything required in the underlay network m4_define([IPSEC_SETUP_UNDERLAY], [AT_CHECK([cp ${abs_top_srcdir}/vswitchd/vswitch.ovsschema vswitch.ovsschema]) dnl Set up the underlay switch AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])]) m4_define([START_PLUTO], [ rm -f $ovs_base/$1/pluto.pid mkdir -p $ovs_base/$1/ipsec.d touch $ovs_base/$1/ipsec.conf touch $ovs_base/$1/secrets ipsec initnss --nssdir $ovs_base/$1/ipsec.d NS_CHECK_EXEC([$1], [ipsec pluto --config $ovs_base/$1/ipsec.conf \ --ipsecdir $ovs_base/$1 --nssdir $ovs_base/$1/ipsec.d \ --logfile $ovs_base/$1/pluto.log --secretsfile $ovs_base/$1/secrets \ --rundir $ovs_base/$1], [0], [], [stderr]) ]) dnl IPSEC_ADD_NODE([namespace], [device], [address], [peer address], dnl [custom-ipsec-conf], [extra]) dnl dnl Creates a dummy host that acts as an IPsec endpoint. Creates host in dnl 'namespace' and attaches a veth 'device' to 'namespace' to act as the host dnl NIC. Assigns 'address' to 'device' and adds the other end of veth 'device' to dnl 'br0' which is an OVS bridge in the default namespace acting as an underlay dnl switch. Sets the default gateway of 'namespace' to 'peer address'. dnl dnl Starts all daemons in 'namespace' that are required for IPsec. dnl dnl If 'custom-ipsec-conf' is provided, then it will be used as --ipsec-conf dnl and the ipsec.conf will be used as --root-ipsec-conf. dnl dnl If 'extra' is provided, passes it as an additional argument list for dnl ovs-monitor-ipsec. m4_define([IPSEC_ADD_NODE], [ADD_NAMESPACES($1) dnl Disable DAD. We know we wont get duplicates on this underlay network. NS_EXEC([$1], [sysctl -w net.ipv6.conf.all.accept_dad=0]) NS_EXEC([$1], [sysctl -w net.ipv6.conf.default.accept_dad=0]) ADD_VETH($2, $1, br0, $3/24) NS_EXEC([$1], [ip route add default via $4 dev $2]) mkdir -p $ovs_base/$1 touch $ovs_base/$1/.conf.db.~lock~ NS_EXEC([$1], [ovsdb-tool create $ovs_base/$1/conf.db \ $abs_top_srcdir/vswitchd/vswitch.ovsschema], [0], [], [stderr]) dnl Start ovsdb-server. NS_EXEC([$1],[ovsdb-server $ovs_base/$1/conf.db --detach --no-chdir \ --log-file=$ovs_base/$1/ovsdb.log --pidfile=$ovs_base/$1/ovsdb.pid \ --remote=punix:$OVS_RUNDIR/$1/db.sock], [0], [], [stderr]) on_exit "kill `cat $ovs_base/$1/ovsdb.pid`" NS_EXEC([$1], [ovs-vsctl --no-wait init]) dnl Start ovs-vswitchd. NS_EXEC([$1], [ovs-vswitchd unix:${OVS_RUNDIR}/$1/db.sock --detach \ --no-chdir --pidfile=$ovs_base/$1/vswitchd.pid \ --unixctl=$ovs_base/$1/vswitchd.ctl \ --log-file=$ovs_base/$1/vswitchd.log -vvconn -vofproto_dpif -vunixctl],\ [0], [], [stderr]) on_exit "kill_ovs_vswitchd `cat $ovs_base/$1/vswitchd.pid`" m4_if([$5], [], [], [ AT_CHECK([echo "## A read-only root config. ##" > $ovs_base/$1/ipsec.conf]) AT_CHECK([echo "include $ovs_base/$1/$5" >> $ovs_base/$1/ipsec.conf]) ]) AT_CHECK dnl Start pluto START_PLUTO([$1]) on_exit 'kill $(cat $ovs_base/$1/pluto.pid)' dnl Start ovs-monitor-ipsec NS_CHECK_EXEC([$1], [ovs-monitor-ipsec unix:${OVS_RUNDIR}/$1/db.sock\ --pidfile=${OVS_RUNDIR}/$1/ovs-monitor-ipsec.pid --ike-daemon=libreswan\ --ipsec-conf=$ovs_base/$1/m4_if([$5], [], [ipsec.conf], [$5]) \ m4_if([$5], [], [], [--root-ipsec-conf=$ovs_base/$1/ipsec.conf]) \ --ipsec-d=$ovs_base/$1/ipsec.d \ --ipsec-secrets=$ovs_base/$1/secrets \ --log-file=$ovs_base/$1/ovs-monitor-ipsec.log \ --ipsec-ctl=$ovs_base/$1/pluto.ctl \ m4_if([$6], [], [], [$6]) \ --no-restart-ike-daemon --detach ], [0], [], [stderr]) on_exit 'kill $(cat $ovs_base/$1/ovs-monitor-ipsec.pid)' dnl Set up OVS bridge NS_CHECK_EXEC([$1], [ovs-vsctl --db unix:$ovs_base/$1/db.sock add-br br-ipsec \ -- set-controller br-ipsec punix:$ovs_base/br-ipsec.$1.mgmt])] ) m4_define([IPSEC_ADD_NODE_LEFT], [IPSEC_ADD_NODE(left, p0, $1, $2, [$3], [$4])]) m4_define([IPSEC_ADD_NODE_RIGHT], [IPSEC_ADD_NODE(right, p1, $1, $2, [$3], [$4])]) dnl OVS_VSCTL([namespace], [sub-command]) dnl dnl Runs `ovs-vsctl 'sub-command'` in 'namespace' m4_define([OVS_VSCTL], [[ip netns exec $1 ovs-vsctl --db unix:$ovs_base/$1/db.sock $2 ]]) m4_define([OVS_VSCTL_LEFT], [OVS_VSCTL(left, $1)]) m4_define([OVS_VSCTL_RIGHT], [OVS_VSCTL(right, $1)]) dnl IPSEC_ADD_TUNNEL([namespace], [type], [options]]) dnl dnl Creates a tunnel of type 'type' in namespace 'namespace' using 'options' m4_define([IPSEC_ADD_TUNNEL], [OVS_VSCTL([$1], [add-port br-ipsec tun -- set Interface tun type=$2 $3]) dnl Wait for all expected connections to be loaded into Libreswan. dnl GRE creates 1 connection, all others create 2. m4_if($2, [gre], [OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N($1, loaded)) -eq 1])], [OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N($1, loaded)) -eq 2])]) ]) m4_define([IPSEC_ADD_TUNNEL_LEFT], [IPSEC_ADD_TUNNEL(left, $1, $2)]) m4_define([IPSEC_ADD_TUNNEL_RIGHT], [IPSEC_ADD_TUNNEL(right, $1, $2)]) dnl CHECK_LIBRESWAN() dnl dnl Check if necessary Libreswan dependencies are available on the test machine m4_define([CHECK_LIBRESWAN], [dnl Skip tests if system has not been set up for Libreswan AT_SKIP_IF([!(ipsec --version | grep Libreswan)]) AT_SKIP_IF([test ! -x $(which certutil)]) AT_SKIP_IF([test ! -x $(which pk12util)]) AT_SKIP_IF([test ! -x $(which openssl)]) dnl If '$ovs_base' is too long, the following Libreswan issue will trigger dnl so we check that it is not too long and skip test if it is. dnl https://github.com/libreswan/libreswan/issues/428 AT_SKIP_IF([test "${#ovs_base}" -gt "90" ])]) dnl IPSEC_SA_LIST([namespace], [output-file]) dnl dnl Get numbers of all the currently active SAs. m4_define([IPSEC_SA_LIST], [ AT_CHECK([ipsec --rundir $ovs_base/$1 status > $2.status]) AT_CHECK([grep -oE '#[0-9]+:' $2.status | sort -uV], [0], [stdout]) AT_CHECK([mv stdout $2]) ]) dnl IPSEC_STATUS_N([namespace], [type]) dnl dnl Get number of 'loaded' or 'active' connections from ipsec status. m4_define([IPSEC_STATUS_N], [ ipsec --rundir $ovs_base/$1 status | \ grep "Total IPsec connections" | \ sed 's/[[0-9]]* *Total IPsec connections:.*$2 \([[0-9]]*\).*/\1/m']) dnl CHECK_ESP_TRAFFIC() dnl dnl Checks for connectivity between nodes and that the underlay traffic is ESP. m4_define([CHECK_ESP_TRAFFIC], [dnl Add test interfaces for pinging NS_EXEC([left], [ip addr add 192.0.0.1/24 dev br-ipsec]) NS_EXEC([left], [ip link set dev br-ipsec up]) NS_EXEC([right], [ip addr add 192.0.0.2/24 dev br-ipsec]) NS_EXEC([right], [ip link set dev br-ipsec up]) dnl Capture any underlay esp packets OVS_DAEMONIZE([tcpdump -l -nn -i ovs-p0 esp > $ovs_base/left/tcpdump.log], [tcpdump0.pid]) OVS_DAEMONIZE([tcpdump -l -nn -i ovs-p1 esp > $ovs_base/right/tcpdump.log], [tcpdump1.pid]) dnl Wait for all loaded connections to be active OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(left, loaded)) \ -eq $(IPSEC_STATUS_N(left, active))]) OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(right, loaded)) \ -eq $(IPSEC_STATUS_N(right, active))]) dnl Ping over IPsec tunnel NS_CHECK_EXEC([left], [ping -q -c 3 -i 0.3 -W 2 192.0.0.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([right], [ping -q -c 3 -i 0.3 -W 2 192.0.0.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check for esp traffic dnl Note: Geneve tests may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 AT_CHECK([cat $ovs_base/left/tcpdump.log | grep ESP], [0], [stdout], [stderr]) AT_CHECK([cat $ovs_base/right/tcpdump.log | grep ESP], [0], [stdout], [stderr])]) AT_SETUP([IPsec -- Libreswan (ipv4, geneve, defaultroute, psk)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, localip, psk)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 \ options:local_ip=10.1.1.1 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 \ options:local_ip=10.1.1.2 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, defaultroute, self-signed)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve self-signed]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Create and set self-signed certs ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log req -u left ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log req -u right ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log self-sign left ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log self-sign right OVS_VSCTL_LEFT(set Open_vSwitch . \ other_config:certificate=${ovs_base}/left-cert.pem \ other_config:private_key=${ovs_base}/left-privkey.pem) OVS_VSCTL_RIGHT(set Open_vSwitch . \ other_config:certificate=${ovs_base}/right-cert.pem \ other_config:private_key=${ovs_base}/right-privkey.pem) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 \ options:remote_cert=${ovs_base}/right-cert.pem]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 \ options:remote_cert=${ovs_base}/left-cert.pem]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, defaultroute, ca-signed)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve ca-signed]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Create and set ca-signed certs ovs-pki --force -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log init ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log req+sign -u left ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log req+sign -u right OVS_VSCTL_LEFT(set Open_vSwitch . \ other_config:ca_cert=${ovs_base}/switchca/cacert.pem \ other_config:certificate=${ovs_base}/left-cert.pem \ other_config:private_key=${ovs_base}/left-privkey.pem) OVS_VSCTL_RIGHT(set Open_vSwitch . \ other_config:ca_cert=${ovs_base}/switchca/cacert.pem \ other_config:certificate=${ovs_base}/right-cert.pem \ other_config:private_key=${ovs_base}/right-privkey.pem) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:remote_name=right]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:remote_name=left]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, gre, defaultroute, psk)]) AT_KEYWORDS([ipsec libreswan ipv4 gre psk]) CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([gre], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([gre], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, vxlan, defaultroute, psk)]) AT_KEYWORDS([ipsec libreswan ipv4, vxlan psk]) CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([vxlan], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([vxlan], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv6, vxlan, defaultroute, psk)]) AT_KEYWORDS([ipsec libreswan ipv6 vxlan psk]) CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(fd01::101, fd01::102) IPSEC_ADD_NODE_RIGHT(fd01::102, fd01::101) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([vxlan], [options:remote_ip=fd01::102 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([vxlan], [options:remote_ip=fd01::101 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv6, vxlan, localip, psk)]) AT_KEYWORDS([ipsec libreswan ipv6 vxlan psk]) CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(fd01::101, fd01::102) IPSEC_ADD_NODE_RIGHT(fd01::102, fd01::101) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([vxlan], [options:remote_ip=fd01::102 \ options:local_ip=fd01::101 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([vxlan], [options:remote_ip=fd01::101 \ options:local_ip=fd01::102 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv6, geneve, defaultroute, psk)]) AT_KEYWORDS([ipsec libreswan ipv6 geneve psk]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts IPSEC_ADD_NODE_LEFT(fd01::101, fd01::102) IPSEC_ADD_NODE_RIGHT(fd01::102, fd01::101) dnl Set up IPsec tunnel on 'left' host IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=fd01::102 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=fd01::101 options:psk=swordfish]) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, custom conf)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk custom conf]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up hosts. IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2, [custom.conf]) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1, [custom.conf]) dnl Set up IPsec tunnel on 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC dnl Check that custom.conf doesn't include default section, but has dnl ike and esp configuration per connection. AT_CHECK([grep -q "conn %default" $ovs_base/left/custom.conf], [1]) AT_CHECK([grep -c -E "(ike|ikev2|esp)=" $ovs_base/left/custom.conf], [0], [6 ]) AT_CHECK([grep -q "conn %default" $ovs_base/right/custom.conf], [1]) AT_CHECK([grep -c -E "(ike|ikev2|esp)=" $ovs_base/right/custom.conf], [0], [6 ]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, default crypto)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk default crypto]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up hosts. IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2, [], [--use-default-crypto]) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1, [], [--use-default-crypto]) dnl Set up IPsec tunnel on 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC dnl Check that ipsec.conf doesn't include ike or esp configuration. AT_CHECK([grep -q "conn %default" $ovs_base/left/ipsec.conf]) AT_CHECK([grep -q -E "(ike|ikev2|esp)=" $ovs_base/left/ipsec.conf], [1]) AT_CHECK([grep -q "conn %default" $ovs_base/right/ipsec.conf]) AT_CHECK([grep -q -E "(ike|ikev2|esp)=" $ovs_base/right/ipsec.conf], [1]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan (ipv4, geneve, custom conf, default crypto)]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk custom conf default crypto]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up hosts. IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2, [custom.conf], [--use-default-crypto]) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1, [custom.conf], [--use-default-crypto]) dnl Set up IPsec tunnel on 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:psk=swordfish]) dnl Set up IPsec tunnel on 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:psk=swordfish]) CHECK_ESP_TRAFFIC dnl Check that custom.conf doesn't include default section, and also doesn't dnl have ike or esp configuration. AT_CHECK([grep -q "conn %default" $ovs_base/left/custom.conf], [1]) AT_CHECK([grep -q -E "(ike|ikev2|esp)=" $ovs_base/left/custom.conf], [1]) AT_CHECK([grep -q "conn %default" $ovs_base/right/custom.conf], [1]) AT_CHECK([grep -q -E "(ike|ikev2|esp)=" $ovs_base/right/custom.conf], [1]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan - reconciliation interval is respected - ipv4]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk reconciliation]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() m4_define([PSK_OPTIONS], [options:remote_ip=$1 options:psk=swordfish]) dnl Set up only the left host. IPSEC_ADD_NODE_LEFT([10.1.1.1], [10.1.1.254]) IPSEC_ADD_TUNNEL_LEFT([geneve], PSK_OPTIONS([10.1.1.2])) dnl Wait for the monitor to find the new connection. OVS_WAIT_UNTIL([grep -q 'tun appeared' left/ovs-monitor-ipsec.log]) dnl Add a few more tunels waiting for the monitor to find them too. m4_for([id], [1], 5, [1], [ OVS_VSCTL([left], add-port br-ipsec tun-id \ -- set Interface tun-id type=geneve PSK_OPTIONS(10.1.2.id)) OVS_WAIT_UNTIL(grep -q 'tun-id appeared' left/ovs-monitor-ipsec.log) ]) dnl And now remove all the extra tunnels. m4_for([id], [1], 5, [1], [ OVS_VSCTL([left], del-port tun-id) OVS_WAIT_UNTIL(grep -q 'tun-id disappeared' left/ovs-monitor-ipsec.log) ]) dnl Check that none of the connections were marked for reconciliation yet. dnl It should take at least 15 seconds for this to happen. AT_CHECK([grep 'defunct' left/ovs-monitor-ipsec.log], [1]) AT_CHECK([grep 'Bringing up' left/ovs-monitor-ipsec.log], [1]) dnl But we should eventually attempt to reconcile the original 'tun' tunnel. OVS_WAIT_UNTIL([grep -qE 'Bringing up ipsec connection tun-[[io]]' \ left/ovs-monitor-ipsec.log]) dnl Now add the right host and check that connection works properly. IPSEC_ADD_NODE_RIGHT([10.1.1.2], [10.1.1.254]) IPSEC_ADD_TUNNEL_RIGHT([geneve], PSK_OPTIONS([10.1.1.1])) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan - reconciliation interval is respected - ipv6]) AT_KEYWORDS([ipsec libreswan ipv6 geneve psk reconciliation]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() m4_define([PSK_OPTIONS], [options:remote_ip=$1 options:psk=swordfish]) dnl Set up only the left host. IPSEC_ADD_NODE_LEFT([fd01::101], [fd01::254]) IPSEC_ADD_TUNNEL_LEFT([geneve], PSK_OPTIONS([fd01::102])) dnl Wait for the monitor to find the new connection. OVS_WAIT_UNTIL([grep -q 'tun appeared' left/ovs-monitor-ipsec.log]) dnl Add a few more tunels waiting for the monitor to find them too. m4_for([id], [1], 5, [1], [ OVS_VSCTL([left], add-port br-ipsec tun-id \ -- set Interface tun-id type=geneve PSK_OPTIONS(fd02::id)) OVS_WAIT_UNTIL(grep -q 'tun-id appeared' left/ovs-monitor-ipsec.log) ]) dnl And now remove all the extra tunnels. m4_for([id], [1], 5, [1], [ OVS_VSCTL([left], del-port tun-id) OVS_WAIT_UNTIL(grep -q 'tun-id disappeared' left/ovs-monitor-ipsec.log) ]) dnl Check that none of the connections were marked for reconciliation yet. dnl It should take at least 15 seconds for this to happen. AT_CHECK([grep 'defunct' left/ovs-monitor-ipsec.log], [1]) AT_CHECK([grep 'Bringing up' left/ovs-monitor-ipsec.log], [1]) dnl But we should eventually attempt to reconcile the original 'tun' tunnel. OVS_WAIT_UNTIL([grep -qE 'Bringing up ipsec connection tun-[[io]]' \ left/ovs-monitor-ipsec.log]) dnl Now add the right host and check that connection works properly. IPSEC_ADD_NODE_RIGHT([fd01::102], [fd01::254]) IPSEC_ADD_TUNNEL_RIGHT([geneve], PSK_OPTIONS([fd01::101])) CHECK_ESP_TRAFFIC OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan - established conns survive new additions - ipv4]) AT_KEYWORDS([ipsec libreswan ipv4 geneve psk persistence]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up hosts. IPSEC_ADD_NODE_LEFT([10.1.1.1], [10.1.1.254]) IPSEC_ADD_NODE_RIGHT([10.1.1.2], [10.1.1.254]) m4_define([PSK_OPTIONS], [options:remote_ip=$1 options:psk=swordfish]) dnl Set up IPsec tunnel on a 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], PSK_OPTIONS([10.1.1.2])) dnl Set up IPsec tunnel on a 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], PSK_OPTIONS([10.1.1.1])) CHECK_ESP_TRAFFIC dnl Get the numbers of all the current SAs. IPSEC_SA_LIST([left], [left/sa.before]) IPSEC_SA_LIST([right], [right/sa.before]) dnl Add a third host and wire it up only to the left to avoid creating a loop. IPSEC_ADD_NODE([third], [p3], [10.1.1.3], [10.1.1.254]) IPSEC_ADD_TUNNEL([third], [geneve], PSK_OPTIONS([10.1.1.1])) OVS_VSCTL([left], add-port br-ipsec tun3 \ -- set Interface tun3 type=geneve PSK_OPTIONS([10.1.1.3])) dnl Wait for all the expected connections to be loaded into Libreswan. dnl 2 tunnels == 4 connections. OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(left, loaded)) -eq 4]) dnl Wait for tunnels to become active. OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(left, loaded)) \ -eq $(IPSEC_STATUS_N(left, active))]) OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(third, loaded)) \ -eq $(IPSEC_STATUS_N(third, active))]) dnl Check that the original left-right tunnel still works. NS_CHECK_EXEC([left], [ping -q -c 3 -i 0.1 -W 2 192.0.0.2 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([right], [ping -q -c 3 -i 0.1 -W 2 192.0.0.1 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check that ovs-monitor-ipsec didn't touch the original tunnel. IPSEC_SA_LIST([left], [left/sa.after]) IPSEC_SA_LIST([right], [right/sa.after]) dnl Right SAs should not change at all. AT_CHECK([diff -u right/sa.before right/sa.after]) dnl Left SAs should be the same, but there will be new ones for the third conn. AT_CHECK([test $(grep -c ':' left/sa.after) -gt $(grep -c ':' left/sa.before)]) AT_CHECK([head -n $(grep -c ':' left/sa.before) left/sa.after \ | diff -u - left/sa.before]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan - established conns survive new additions - ipv6]) AT_KEYWORDS([ipsec libreswan ipv6 geneve psk persistence]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up hosts. IPSEC_ADD_NODE_LEFT([fd01::101], [fd01::254]) IPSEC_ADD_NODE_RIGHT([fd01::102], [fd01::254]) m4_define([PSK_OPTIONS], [options:remote_ip=$1 options:psk=swordfish]) dnl Set up IPsec tunnel on a 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], PSK_OPTIONS([fd01::102])) dnl Set up IPsec tunnel on a 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], PSK_OPTIONS([fd01::101])) CHECK_ESP_TRAFFIC dnl Get the numbers of all the current SAs. IPSEC_SA_LIST([left], [left/sa.before]) IPSEC_SA_LIST([right], [right/sa.before]) dnl Add a third host and wire it up only to the left to avoid creating a loop. IPSEC_ADD_NODE([third], [p3], [fd01::103], [fd01::254]) IPSEC_ADD_TUNNEL([third], [geneve], PSK_OPTIONS([fd01::101])) OVS_VSCTL([left], add-port br-ipsec tun3 \ -- set Interface tun3 type=geneve PSK_OPTIONS([fd01::103])) dnl Wait for all the expected connections to be loaded into Libreswan. dnl 2 tunnels == 4 connections. OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(left, loaded)) -eq 4]) dnl Wait for tunnels to become active. OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(left, loaded)) \ -eq $(IPSEC_STATUS_N(left, active))]) OVS_WAIT_UNTIL([test $(IPSEC_STATUS_N(third, loaded)) \ -eq $(IPSEC_STATUS_N(third, active))]) dnl Check that the original left-right tunnel still works. NS_CHECK_EXEC([left], [ping -q -c 3 -i 0.1 -W 2 192.0.0.2 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([right], [ping -q -c 3 -i 0.1 -W 2 192.0.0.1 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check that ovs-monitor-ipsec didn't touch the original tunnel. IPSEC_SA_LIST([left], [left/sa.after]) IPSEC_SA_LIST([right], [right/sa.after]) dnl Right SAs should not change at all. AT_CHECK([diff -u right/sa.before right/sa.after]) dnl Left SAs should be the same, but there will be new ones for the third conn. AT_CHECK([test $(grep -c ':' left/sa.after) -gt $(grep -c ':' left/sa.before)]) AT_CHECK([head -n $(grep -c ':' left/sa.before) left/sa.after \ | diff -u - left/sa.before]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan - certificate update while the daemon is down]) AT_KEYWORDS([ipsec libreswan geneve ca-cert]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() dnl Set up dummy hosts. IPSEC_ADD_NODE_LEFT(10.1.1.1, 10.1.1.2) IPSEC_ADD_NODE_RIGHT(10.1.1.2, 10.1.1.1) dnl Create and set ca-signed certs. AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ --force init], [0], [ignore], [ignore]) AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ req+sign -u left], [0], [ignore], [ignore]) AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ req+sign -u right], [0], [ignore], [ignore]) OVS_VSCTL_LEFT(set Open_vSwitch . \ other_config:ca_cert=${ovs_base}/switchca/cacert.pem \ other_config:certificate=${ovs_base}/left-cert.pem \ other_config:private_key=${ovs_base}/left-privkey.pem) OVS_VSCTL_RIGHT(set Open_vSwitch . \ other_config:ca_cert=${ovs_base}/switchca/cacert.pem \ other_config:certificate=${ovs_base}/right-cert.pem \ other_config:private_key=${ovs_base}/right-privkey.pem) dnl Set up IPsec tunnel on 'left' host. IPSEC_ADD_TUNNEL_LEFT([geneve], [options:remote_ip=10.1.1.2 options:remote_name=right]) dnl Set up IPsec tunnel on 'right' host. IPSEC_ADD_TUNNEL_RIGHT([geneve], [options:remote_ip=10.1.1.1 options:remote_name=left]) CHECK_ESP_TRAFFIC dnl Get the numbers of all the current SAs. IPSEC_SA_LIST([left], [left/sa.before]) IPSEC_SA_LIST([right], [right/sa.before]) dnl Kill the ovs-monitor-ipsec on the left host. AT_CHECK([kill $(cat ${ovs_base}/left/ovs-monitor-ipsec.pid)]) OVS_WAIT_WHILE([kill -0 $(cat ${ovs_base}/left/ovs-monitor-ipsec.pid)]) dnl Re-generate the certificates. AT_CHECK([rm -rf ${ovs_base}/switchca *.pem]) AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ --force init], [0], [ignore], [ignore]) AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ req+sign -u left], [0], [ignore], [ignore]) AT_CHECK([ovs-pki -b --dir=${ovs_base} -l ${ovs_base}/ovs-pki.log \ req+sign -u right], [0], [ignore], [ignore]) dnl Re-start ovs-monitor-ipsec with --no-restart-ike-daemon. NS_CHECK_EXEC([left], [ovs-monitor-ipsec unix:${OVS_RUNDIR}/left/db.sock \ --pidfile=${OVS_RUNDIR}/left/ovs-monitor-ipsec.pid \ --ike-daemon=libreswan \ --ipsec-conf=$ovs_base/left/ipsec.conf \ --ipsec-d=$ovs_base/left/ipsec.d \ --ipsec-secrets=$ovs_base/left/secrets \ --log-file=$ovs_base/left/ovs-monitor-ipsec-2.log \ --ipsec-ctl=$ovs_base/left/pluto.ctl \ --no-restart-ike-daemon --detach ], [0], [], [stderr]) OVS_WAIT_UNTIL([grep -q 'Connections for all(1) configured tunnels are Up.' \ $ovs_base/left/ovs-monitor-ipsec-2.log]) dnl Check that the original left-right tunnel still works. NS_CHECK_EXEC([left], [ping -q -c 3 -i 0.1 -W 2 192.0.0.2 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([right], [ping -q -c 3 -i 0.1 -W 2 192.0.0.1 | FORMAT_PING], [0], [3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check that ovs-monitor-ipsec didn't touch the original tunnel. IPSEC_SA_LIST([left], [left/sa.after]) IPSEC_SA_LIST([right], [right/sa.after]) AT_CHECK([diff -u left/sa.before left/sa.after]) AT_CHECK([diff -u right/sa.before right/sa.after]) dnl Check that there are no extra certs in the NSS database. AT_CHECK([certutil -L -d $ovs_base/left/ipsec.d | grep -c ovs_cert], [0], [2 ]) dnl Check that loaded certs are the new ones. Using openssl for comparison dnl to ensure the same format (the file on disk and the dump from the NSS dnl database have slightly different formats). AT_CHECK([certutil -L -d $ovs_base/left/ipsec.d \ -a -n ovs_cert_cacert], [0], [stdout]) AT_CHECK_UNQUOTED([openssl x509 -in stdout -text -noout], [0], [$(openssl x509 -in switchca/cacert.pem -text -noout) ]) AT_CHECK([certutil -L -d $ovs_base/left/ipsec.d \ -a -n ovs_certkey_left], [0], [stdout]) AT_CHECK_UNQUOTED([openssl x509 -in stdout -text -noout], [0], [$(openssl x509 -in left-cert.pem -text -noout) ]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP AT_SETUP([IPsec -- Libreswan NxN geneve tunnels + reconciliation]) AT_KEYWORDS([ipsec libreswan scale reconciliation]) dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 CHECK_LIBRESWAN() OVS_TRAFFIC_VSWITCHD_START() IPSEC_SETUP_UNDERLAY() m4_define([NODES], [20]) dnl Set up fake hosts. m4_for([id], [1], NODES, [1], [ IPSEC_ADD_NODE([node-id], [p-id], 10.1.1.id, 10.1.1.254) AT_CHECK([ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log \ req -u node-id], [0], [stdout]) AT_CHECK([ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log \ self-sign node-id], [0], [stdout]) AT_CHECK(OVS_VSCTL([node-id], set Open_vSwitch . \ other_config:certificate=${ovs_base}/node-id-cert.pem \ other_config:private_key=${ovs_base}/node-id-privkey.pem \ -- set bridge br-ipsec other-config:hwaddr=f2:ff:00:00:00:id), [0], [ignore], [ignore]) on_exit "ipsec --rundir $ovs_base/node-id status > $ovs_base/node-id/status" ]) dnl Create a full mesh of tunnels. m4_for([LEFT], [1], NODES, [1], [ m4_for([RIGHT], [1], NODES, [1], [ if test LEFT -ne RIGHT; then AT_CHECK(OVS_VSCTL(node-LEFT, add-port br-ipsec tun-RIGHT \ -- set Interface tun-RIGHT type=geneve options:remote_ip=10.1.1.RIGHT \ options:remote_cert=${ovs_base}/node-RIGHT-cert.pem), [0], [ignore], [ignore]) fi ])]) dnl These are not necessary, but nice to have in the test log in dnl order to spot pluto failures during the test. on_exit "grep -E 'Timed out|outdated|defunct|went down' \ $ovs_base/node-*/ovs-monitor-ipsec.log" on_exit "grep -E 'ABORT|ERROR' $ovs_base/node-*/pluto.log" m4_define([WAIT_FOR_LOADED_CONNS], [ m4_for([id], [1], NODES, [1], [ echo "================== node-id =========================" iterations=0 loaded=0 active=0 dnl Using a custom loop instead of OVS_WAIT_UNTIL, because it may take dnl much longer than a default timeout. The default retransmit timeout dnl for pluto is 60 seconds. Also, we need to make sure pluto didn't dnl crash in the process and revive it if it did, unfortunately. while true; do date AT_CHECK([ipsec --rundir $ovs_base/node-id status 2>&1 \ | grep -E "whack|Total"], [ignore], [stdout]) if grep -E 'is Pluto running?|refused' stdout; then echo "node-id: Pluto died, restarting..." START_PLUTO([node-id]) else loaded=$(IPSEC_STATUS_N(node-id, loaded)) m4_if([$1], [active], [active=$(IPSEC_STATUS_N(node-id, active))], [active=$loaded]) fi if test "$loaded" -ne "$(( (NODES - 1) * 2 ))" -o \ "$loaded" -ne "$active"; then sleep 3 else break fi let iterations=$iterations+1 AT_CHECK([test $iterations -lt 100]) done ]) ]) dnl Wait for all the connections to be loaded to pluto. Not waiting for dnl them to become active, because if pluto is down on one of the nodes, dnl some connections may not become active until we revive it. Some dnl connections may also never become active due to bugs in libreswan 4.x. WAIT_FOR_LOADED_CONNS() dnl Next section will check connectivity between all the nodes. dnl Different versions of Libreswan 4.x have issues where connections dnl are not being correctly established or never become active in a dnl way that can not be mitigated from ovs-monitor-ipsec or the test. dnl So, only checking connectivity for Libreswan 3- or 5+. dnl Skipping in the middle of the test, so test can still fail while dnl testing with Libreswan 4, if the first half fails. AT_SKIP_IF([ipsec --version 2>&1 | grep -q 'Libreswan 4\.']) dnl Wait for the monitor and pluto to be done with all the connections. OVS_WAIT_UNTIL([grep -q 'all(19) configured tunnels are Up' \ $ovs_base/node-1/ovs-monitor-ipsec.log]) AT_CHECK([ipsec auto --help], [ignore], [ignore], [stderr]) auto=auto if test -s stderr; then auto= fi dnl Remove connections for two tunnels. One fully and one partially. AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ --config $ovs_base/node-1/ipsec.conf \ --delete tun-5-out-1], [0], [stdout]) AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ --config $ovs_base/node-1/ipsec.conf \ --delete tun-2-in-1], [0], [stdout]) AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ --config $ovs_base/node-1/ipsec.conf \ --delete tun-2-out-1], [0], [stdout]) dnl Wait for the monitor to notice the missing connections. OVS_WAIT_UNTIL([grep -qE 'tun-[[25]] .*need to reconcile' \ $ovs_base/node-1/ovs-monitor-ipsec.log]) dnl Wait for all the connections to be loaded back. WAIT_FOR_LOADED_CONNS() dnl Turn off IPv6 and add static ARP entries for all namespaces to avoid dnl any broadcast / multicast traffic that would otherwise be multiplied dnl by each node creating a traffic storm. Add specific OpenFlow rules dnl to forward traffic to exact destinations without any MAC learning. m4_for([LEFT], [1], NODES, [1], [ NS_CHECK_EXEC([node-LEFT], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) AT_CHECK([ovs-ofctl del-flows unix:$ovs_base/br-ipsec.node-LEFT.mgmt]) AT_CHECK([ovs-ofctl add-flow unix:$ovs_base/br-ipsec.node-LEFT.mgmt \ "dl_dst=f2:ff:00:00:00:LEFT actions=LOCAL"]) m4_for([RIGHT], [1], NODES, [1], [ if test LEFT -ne RIGHT; then NS_CHECK_EXEC([node-LEFT], [ip neigh add 192.0.0.RIGHT lladdr f2:ff:00:00:00:RIGHT dev br-ipsec]) AT_CHECK([ovs-ofctl add-flow unix:$ovs_base/br-ipsec.node-LEFT.mgmt \ "dl_dst=f2:ff:00:00:00:RIGHT actions=tun-RIGHT"]) fi ]) ]) dnl Bring up and add IP addresses for br-ipsec interface. m4_for([id], [1], NODES, [1], [ echo "================== node-id =========================" NS_CHECK_EXEC([node-id], [ip addr add 192.0.0.id/24 dev br-ipsec]) NS_CHECK_EXEC([node-id], [ip link set dev br-ipsec up]) ]) dnl Wait for all the connections to be loaded and active. In case one of dnl the pluto processes crashed some of the connections may never become dnl active. But we did run this loop with a pluto reviving logic twice dnl already, so the chances for pluto to be down here are much lower. WAIT_FOR_LOADED_CONNS([active]) dnl Check the full mesh ping. m4_for([LEFT], [1], NODES, [1], [ m4_for([RIGHT], [1], NODES, [1], [ if test LEFT -ne RIGHT; then echo "====== ping: node-LEFT --> node-RIGHT ==========" dnl Ping without checking in case connection will recover after the dnl first packet. NS_CHECK_EXEC([node-LEFT], [ping -q -c 1 -W 2 192.0.0.RIGHT | FORMAT_PING], [ignore], [stdout]) dnl Now check. If this one fails, there is no actual connectivity. NS_CHECK_EXEC([node-LEFT], [ping -q -c 3 -i 0.1 -W 2 192.0.0.RIGHT | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) fi ])]) OVS_TRAFFIC_VSWITCHD_STOP() AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-kmod-macros.at000066400000000000000000000253141514270232600242130ustar00rootroot00000000000000# _ADD_BR([name]) # # Expands into the proper ovs-vsctl commands to create a bridge with the # appropriate type and properties m4_define([_ADD_BR], [[add-br $1 -- set Bridge $1 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15 fail-mode=secure ]]) # OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args]]) # # Creates a database and starts ovsdb-server, starts ovs-vswitchd # connected to that database, calls ovs-vsctl to create a bridge named # br0 with predictable settings, passing 'vsctl-args' as additional # commands to ovs-vsctl. If 'vsctl-args' causes ovs-vsctl to provide # output (e.g. because it includes "create" commands) then 'vsctl-output' # specifies the expected output after filtering through uuidfilt. # 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init' # before starting ovs-vswitchd. # # Best-effort loading of all available vport modules is performed. # m4_define([OVS_TRAFFIC_VSWITCHD_START], [AT_CHECK([modprobe openvswitch]) on_exit 'modprobe -r openvswitch' m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_vxlan]], [modprobe -q mod || echo "Module mod not loaded." on_exit 'modprobe -q -r mod' ]) on_exit 'ovs-dpctl del-dp ovs-system' on_exit 'ovs-appctl dpctl/flush-conntrack' _OVS_VSWITCHD_START([], [$3]) dnl Add bridges, ports, etc. AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) dnl Start retis capture if requested. RETIS_CHECK_AND_RUN() ]) # OVS_TRAFFIC_VSWITCHD_STOP([ALLOWLIST], [extra_cmds]) # # Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files # for messages with severity WARN or higher and signaling an error if any # is present. The optional ALLOWLIST may contain shell-quoted "sed" # commands to delete any warnings that are actually expected, e.g.: # # OVS_TRAFFIC_VSWITCHD_STOP(["/expected error/d"]) # # 'extra_cmds' are shell commands to be executed after OVS_VSWITCHD_STOP() is # invoked. They can be used to perform additional cleanups such as name space # removal. m4_define([OVS_TRAFFIC_VSWITCHD_STOP], [OVS_VSWITCHD_STOP([$1]) AT_CHECK([:; $2]) ]) # CONFIGURE_VETH_OFFLOADS([VETH]) # # The kernel datapath has no problem with offloads and veths. Nothing # to do here. m4_define([CONFIGURE_VETH_OFFLOADS], ) # CHECK_CONNTRACK() # # Perform requirements checks for running conntrack tests, and flush the # kernel conntrack tables when the test is finished. # m4_define([CHECK_CONNTRACK], [m4_foreach([mod], [[nf_conntrack_ipv4], [nf_conntrack_ipv6], [nf_nat_ftp], [nf_nat_tftp]], [modprobe mod || echo "Module mod not loaded." on_exit 'modprobe -r mod' ]) sysctl -w net.netfilter.nf_conntrack_helper=0 on_exit 'ovstest test-netlink-conntrack flush' ] ) # CHECK_CONNTRACK_ALG() # # Perform requirements checks for running conntrack ALG tests. The kernel # supports ALG, so no check is needed. # m4_define([CHECK_CONNTRACK_ALG]) # CHECK_CONNTRACK_LOCAL_STACK() # # Perform requirements checks for running conntrack tests with local stack. # The kernel always supports reading the connection state of an skb coming # from an internal port, without an explicit ct() action, so no check is # needed. m4_define([CHECK_CONNTRACK_LOCAL_STACK]) # CHECK_CONNTRACK_FRAG_OVERLAP() # # The kernel does not support overlapping fragments checking. m4_define([CHECK_CONNTRACK_FRAG_OVERLAP], [ AT_SKIP_IF([:]) ]) # CHECK_CONNTRACK_NAT() # # Perform requirements checks for running conntrack NAT tests. The kernel # always supports NAT, so no check is needed. # m4_define([CHECK_CONNTRACK_NAT]) # CHECK_CONNTRACK_ZEROIP_SNAT() # # Perform requirements checks for running conntrack all-zero IP SNAT tests. # The kernel always supports all-zero IP SNAT, so no check is needed. # However, the Windows datapath using the same netlink interface does not. # m4_define([CHECK_CONNTRACK_ZEROIP_SNAT], [ AT_SKIP_IF([test "$IS_WIN32" = "yes"]) ]) # CHECK_CONNTRACK_SCTP() # # Perform requirements checks for running conntrack SCTP. The kernel # optionally support nf proto sctp. # m4_define([CHECK_CONNTRACK_SCTP], [ AT_SKIP_IF([test "$IS_WIN32" = "yes"]) AT_SKIP_IF([! test -e /proc/sys/net/netfilter/nf_conntrack_sctp_timeout_closed]) ]) # CHECK_CONNTRACK_TIMEOUT() # # Perform requirements checks for running conntrack customized timeout tests. # m4_define([CHECK_CONNTRACK_TIMEOUT], [ AT_SKIP_IF([! cat /boot/config-$(uname -r) | grep NF_CONNTRACK_TIMEOUT | grep '=y' > /dev/null]) modprobe nfnetlink_cttimeout on_exit 'modprobe -r nfnetlink_cttimeout' ]) # CHECK_CONNTRACK_DUMP_EXPECTATIONS() # # Perform requirements checks for dumping conntrack expectations. # m4_define([CHECK_CONNTRACK_DUMP_EXPECTATIONS], [ AT_SKIP_IF([:]) ]) # CHECK_CT_DPIF_SET_GET_MAXCONNS() # # Perform requirements checks for running ovs-dpctl ct-set-maxconns or # ovs-dpctl ct-get-maxconns. The kernel datapath does not support this # feature. m4_define([CHECK_CT_DPIF_SET_GET_MAXCONNS], [ AT_SKIP_IF([:]) ]) # CHECK_CT_DPIF_GET_NCONNS() # # Perform requirements checks for running ovs-dpctl ct-get-nconns. The # kernel datapath does not support this feature. m4_define([CHECK_CT_DPIF_GET_NCONNS], [ AT_SKIP_IF([:]) ]) # DPCTL_SET_MIN_FRAG_SIZE() # # The kernel does not support this command. m4_define([DPCTL_SET_MIN_FRAG_SIZE], [ ]) # DPCTL_MODIFY_FRAGMENTATION() # # The kernel does not support this command. m4_define([DPCTL_MODIFY_FRAGMENTATION], [ ]) # DPCTL_CHECK_FRAGMENTATION_PASS() # # The kernel does not support this command. m4_define([DPCTL_CHECK_FRAGMENTATION_PASS], [ ]) # DPCTL_CHECK_V6_FRAGMENTATION_PASS() # # The kernel does not support this command. m4_define([DPCTL_CHECK_V6_FRAGMENTATION_PASS], [ ]) # DPCTL_CHECK_FRAGMENTATION_FAIL() # # The kernel does not support this command. m4_define([DPCTL_CHECK_FRAGMENTATION_FAIL], [ ]) # OVS_CHECK_FRAG_LARGE # # This check isn't valid for kernel m4_define([OVS_CHECK_FRAG_LARGE], [ ]) # OVS_CHECK_MIN_KERNEL([minversion], [minsublevel]) # # Skip test if kernel version falls below minversion.minsublevel m4_define([OVS_CHECK_MIN_KERNEL], [ version=$(uname -r | sed -e 's/\./ /g' | awk '{print $ 1}') sublevel=$(uname -r | sed -e 's/\./ /g' | awk '{print $ 2}') AT_SKIP_IF([test $version -lt $1 || ( test $version -eq $1 && test $sublevel -lt $2 )]) ]) # OVS_CHECK_KERNEL_EXCL([minversion], [minsublevel], [maxversion], [maxsublevel]) # # Skip test if kernel version falls between minversion.minsublevel and maxversion.maxsublevel m4_define([OVS_CHECK_KERNEL_EXCL], [ version=$(uname -r | sed -e 's/\./ /g' | awk '{print $ 1}') sublevel=$(uname -r | sed -e 's/\./ /g' | awk '{print $ 2}') AT_SKIP_IF([ ! ( test $version -lt $1 || ( test $version -eq $1 && test $sublevel -lt $2 ) || test $version -gt $3 || ( test $version -eq $3 && test $sublevel -gt $4 ) ) ]) ]) # OVS_CHECK_SRV6() # # The kernel datapath does not support this feature. m4_define([OVS_CHECK_SRV6], [ AT_SKIP_IF([:]) ]) # CHECK_LATER_IPV6_FRAGMENTS() # # Upstream kernels beetween 4.20 and 5.19 are not parsing IPv6 fragments # correctly. The issue was also backported in some older distribution # kernels, so kernels below 4.20 are not reliable. m4_define([CHECK_LATER_IPV6_FRAGMENTS], [OVS_CHECK_MIN_KERNEL(5, 19)]) # VSCTL_ADD_DATAPATH_TABLE() # # Create system datapath table "system" for kernel tests in ovsdb m4_define([VSCTL_ADD_DATAPATH_TABLE], [ AT_CHECK([ovs-vsctl -- --id=@m create Datapath datapath_version=0 -- set Open_vSwitch . datapaths:"system"=@m], [0], [stdout]) DP_TYPE=$(echo "system") ]) # CHECK_L3L4_CONNTRACK_REASM() # # Only allow this test to run on the kernel datapath - it is not useful # or necessary for the userspace datapath as it is checking for a kernel # specific regression. m4_define([CHECK_L3L4_CONNTRACK_REASM]) # CHECK_NO_DPDK_OFFLOAD # # The kernel module tests do not use dpdk's rte_flow offload. m4_define([CHECK_NO_DPDK_OFFLOAD]) # CHECK_NO_TC_OFFLOAD # # The kernel module tests do not use TC offload. m4_define([CHECK_NO_TC_OFFLOAD]) # OVS_CHECK_BAREUDP() # # The feature needs to be enabled in the kernel configuration (CONFIG_BAREUDP) # to work. m4_define([OVS_CHECK_BAREUDP], [ AT_SKIP_IF([! ip link add dev ovs_bareudp0 type bareudp dstport 6635 ethertype mpls_uc 2>&1 >/dev/null]) AT_CHECK([ip link del dev ovs_bareudp0]) ]) # IPTABLES_CHECK_EXTERNAL_CT() # # Checks if packets can be tracked outside OvS. # iptables variant of this macro m4_define([IPTABLES_CHECK_EXTERNAL_CT], [ dnl Kernel config (CONFIG_NETFILTER_XT_TARGET_CT) dnl and user space extensions need to be present. AT_SKIP_IF([! iptables -t raw -I OUTPUT 1 -j CT]) AT_CHECK([iptables -t raw -D OUTPUT 1]) ]) # NFT_CHECK_EXTERNAL_CT() # # Checks if packets can be tracked outside OvS. # nft variant of this macro m4_define([NFT_CHECK_EXTERNAL_CT], [ dnl Kernel config (CONFIG_NETFILTER_XT_TARGET_CT) dnl and user space extensions need to be present. AT_SKIP_IF([! nft -c -f - << EOF table ip raw { chain output-ovs-testsuite { type filter hook output priority raw; ct state new } } EOF ]) ]) # CHECK_EXTERNAL_CT() # # Checks if packets can be tracked outside OvS. m4_define([CHECK_EXTERNAL_CT], [ dnl Kernel config (CONFIG_NETFILTER_XT_TARGET_CT) dnl and user space extensions need to be present. if test $HAVE_NFT = yes; then NFT_CHECK_EXTERNAL_CT() elif test $HAVE_IPTABLES = yes; then IPTABLES_CHECK_EXTERNAL_CT() else AT_SKIP_IF([true]) fi ]) # IPTABLES_ADD_EXTERNAL_CT() # # Let conntrack start tracking the packets outside OvS. # iptables variant of this macro m4_define([IPTABLES_ADD_EXTERNAL_CT], [ AT_CHECK([iptables -t raw -I OUTPUT 1 -o $1 -j CT]) on_exit 'iptables -t raw -D OUTPUT 1' ]) # NFT_ADD_EXTERNAL_CT() # # Let conntrack start tracking the packets outside OvS. # nft variant of this macro m4_define([NFT_ADD_EXTERNAL_CT], [ if ! nft list table ip raw > /dev/null 2>1; then on_exit 'nft "delete table ip raw"' fi AT_CHECK([nft -f - << EOF table ip raw { chain output-ovs-testsuite { type filter hook output priority raw; oifname "$1" ct state new } } EOF ]) on_exit 'nft "delete chain ip raw output-ovs-testsuite"' ]) # ADD_EXTERNAL_CT() # # Checks if packets can be tracked outside OvS. m4_define([ADD_EXTERNAL_CT], [ if test $HAVE_NFT = yes; then NFT_ADD_EXTERNAL_CT([$1]) else IPTABLES_ADD_EXTERNAL_CT([$1]) fi ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-kmod-testsuite.at000066400000000000000000000017301514270232600247540ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2015 Nicira, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-kmod-macros.at]) m4_include([tests/system-traffic.at]) m4_include([tests/system-layer3-tunnels.at]) m4_include([tests/system-interface.at]) m4_include([tests/system-ipsec.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-layer3-tunnels.at000066400000000000000000000200761514270232600246640ustar00rootroot00000000000000AT_BANNER([layer3-tunnels]) AT_SETUP([layer3 - ping over VXLAN-GPE]) OVS_TRAFFIC_VSWITCHD_START([set Bridge br0 other-config:hwaddr="00:12:34:56:78:bb"]) OVS_CHECK_VXLAN_GPE() OVS_CHECK_IPROUTE_ENCAP() ADD_BR([br-underlay]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.2/24], [options:packet_type=legacy_l3 options:exts=gpe]) AT_CHECK([ip neigh add 10.1.1.1 lladdr 00:12:34:56:78:aa dev br0]) NS_CHECK_EXEC([at_ns0], [ip link add dev at_vxlan1 type vxlan dstport 4789 external gpe]) NS_CHECK_EXEC([at_ns0], [ip addr add dev at_vxlan1 10.1.1.1/24]) NS_CHECK_EXEC([at_ns0], [ip link set dev at_vxlan1 mtu 1450 up]) NS_CHECK_EXEC([at_ns0], [ip route add 10.1.1.2/32 encap ip id 0 dst 172.31.1.100 dev at_vxlan1]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) dnl Now add rules for OVS to forward to the tunnel and local port AT_CHECK([ovs-ofctl add-flow br0 "priority=1 action=drop"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 ip,nw_dst=10.1.1.1 action=output:at_vxlan0"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 ip,nw_dst=10.1.1.2 action=mod_dl_src:00:12:34:56:78:aa,mod_dl_dst:00:12:34:56:78:bb,local"]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([layer3 - ping over GRE]) OVS_TRAFFIC_VSWITCHD_START([set Bridge br0 other-config:hwaddr="00:12:34:56:78:bb"]) OVS_CHECK_GRE_L3() OVS_CHECK_IPROUTE_ENCAP() ADD_BR([br-underlay]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.2/24], [options:packet_type=legacy_l3]) AT_CHECK([ip neigh add 10.1.1.1 lladdr 00:12:34:56:78:aa dev br0]) NS_CHECK_EXEC([at_ns0], [ip link add dev at_gre1 type gre remote 172.31.1.100]) NS_CHECK_EXEC([at_ns0], [ip addr add dev at_gre1 10.1.1.1/24]) NS_CHECK_EXEC([at_ns0], [ip link set dev at_gre1 mtu 1450 up]) NS_CHECK_EXEC([at_ns0], [ip route add 10.1.1.2/32 encap ip id 0 dst 172.31.1.100 dev at_gre1]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) dnl Now add rules for OVS to forward to the tunnel and local port AT_CHECK([ovs-ofctl add-flow br0 "priority=1 action=drop"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 ip,nw_dst=10.1.1.1 action=output:at_gre0"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 ip,nw_dst=10.1.1.2 action=mod_dl_src:00:12:34:56:78:aa,mod_dl_dst:00:12:34:56:78:bb,local"]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([layer3 - ping over MPLS Bareudp]) OVS_CHECK_BAREUDP() OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "36:b1:ee:7c:01:01") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", "36:b1:ee:7c:01:02") ADD_OVS_TUNNEL([bareudp], [br0], [at_bareudp0], [8.1.1.3], [8.1.1.2/24], [ options:local_ip=8.1.1.2 options:packet_type="legacy_l3" options:payload_type=mpls options:dst_port=6635]) ADD_OVS_TUNNEL([bareudp], [br1], [at_bareudp1], [8.1.1.2], [8.1.1.3/24], [options:local_ip=8.1.1.3 options:packet_type="legacy_l3" options:payload_type=mpls options:dst_port=6635]) AT_DATA([flows0.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_mpls_label:3,output:at_bareudp0 table=0,priority=100,dl_type=0x8847 in_port=at_bareudp0 actions=pop_mpls:0x0800,set_field:36:b1:ee:7c:01:01->dl_dst,set_field:36:b1:ee:7c:01:02->dl_src,output:ovs-p0 table=0,priority=10 actions=normal ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_mpls_label:3,output:at_bareudp1 table=0,priority=100,dl_type=0x8847 in_port=at_bareudp1 actions=pop_mpls:0x0800,set_field:36:b1:ee:7c:01:02->dl_dst,set_field:36:b1:ee:7c:01:01->dl_src,output:ovs-p1 table=0,priority=10 actions=normal ]) AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0]) AT_CHECK([ovs-vsctl add-port br1 patch1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows0.txt]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([layer3 - ping over Bareudp]) OVS_CHECK_BAREUDP() OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "36:b1:ee:7c:01:01") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", "36:b1:ee:7c:01:02") ADD_OVS_TUNNEL([bareudp], [br0], [at_bareudp0], [8.1.1.3], [8.1.1.2/24], [ options:local_ip=8.1.1.2 options:packet_type="legacy_l3" options:payload_type=ip options:dst_port=6636]) ADD_OVS_TUNNEL([bareudp], [br1], [at_bareudp1], [8.1.1.2], [8.1.1.3/24], [options:local_ip=8.1.1.3 options:packet_type="legacy_l3" options:payload_type=ip options:dst_port=6636]) AT_DATA([flows0.txt], [dnl table=0,priority=100,dl_type=0x0800 in_port=ovs-p0, actions=output:at_bareudp0 table=0,priority=100,dl_type=0x0800 in_port=at_bareudp0 actions=set_field:36:b1:ee:7c:01:01->dl_dst,set_field:36:b1:ee:7c:01:02->dl_src,output:ovs-p0 table=0,priority=10 actions=normal ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,dl_type=0x0800 in_port=ovs-p1 actions=output:at_bareudp1 table=0,priority=100,dl_type=0x0800 in_port=at_bareudp1 actions=set_field:36:b1:ee:7c:01:02->dl_dst,set_field:36:b1:ee:7c:01:01->dl_src,output:ovs-p1 table=0,priority=10 actions=normal ]) AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0]) AT_CHECK([ovs-vsctl add-port br1 patch1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows0.txt]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-offloads-testsuite-macros.at000066400000000000000000000054751514270232600271130ustar00rootroot00000000000000AT_COPYRIGHT([Copyright (c) 2022 Red Hat, Inc. 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.]) # The goal is to run as many as possible of the system-traffic tests with # OVS tc offload enabled. We do this by overriding the # OVS_TRAFFIC_VSWITCHD_START() with offloading enabled. m4_define([OVS_TRAFFIC_VSWITCHD_START], [AT_CHECK([modprobe openvswitch]) on_exit 'modprobe -r openvswitch' m4_foreach([mod], [[vport_geneve], [vport_gre], [vport_vxlan]], [modprobe -q mod || echo "Module mod not loaded." on_exit 'modprobe -q -r mod' ]) on_exit 'ovs-dpctl del-dp ovs-system' on_exit 'ovs-appctl dpctl/flush-conntrack' _OVS_VSWITCHD_START([], [-- set Open_vSwitch . other_config:hw-offload=true $3]) dnl Add bridges, ports, etc. AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) dnl Start retis capture if requested. RETIS_CHECK_AND_RUN() ]) # CHECK_NO_DPDK_OFFLOAD # # The kernel module tests do not use dpdk's rte_flow offload. m4_define([CHECK_NO_DPDK_OFFLOAD]) # Macro to exclude tests that will fail with TC offload enabled. # We currently have the below tests disabled in system-traffic.at # for the following reasons: # # TC does not support moving ports to a different namespace than vswitchd's # namespace, so we need to disable this test. # - 'conntrack - multiple namespaces, internal ports' # # The kernel's tcf_ct_act() function does not seem to take care of any (QinQ) # VLAN headers causing commits to fail. However, if this is solved, we have to # make sure conntrack does not break the VLAN boundary, i.e., putting together # two packets with different CVLAN+SVLAN values. # - 'conntrack - IPv4 fragmentation + cvlan' # # Fragmentation handling in ct zone 9 does not seem to work correctly. # When moving this test over to the default zone all works fine. # - 'conntrack - Fragmentation over vxlan' # # Occasionally we fail with invalid byte counts. # - 'datapath - truncate and output to gre tunnel by simulated packets' # - 'datapath - truncate and output to gre tunnel' # m4_define([CHECK_NO_TC_OFFLOAD], [ AT_SKIP_IF([:]) ]) # Conntrack ALGs are not supported for tc. m4_define([CHECK_CONNTRACK_ALG], [ AT_SKIP_IF([:]) ]) # Conntrack timeout not supported for tc. m4_define([CHECK_CONNTRACK_TIMEOUT], [ AT_SKIP_IF([:]) ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-offloads-testsuite.at000066400000000000000000000017261514270232600256240ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2016 Mellanox Technologies, Ltd. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-kmod-macros.at]) m4_include([tests/system-offloads-traffic.at]) m4_include([tests/system-offloads-testsuite-macros.at]) m4_include([tests/system-traffic.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-offloads-traffic.at000066400000000000000000001471221514270232600252120ustar00rootroot00000000000000AT_BANNER([datapath offloads]) # DUMP_CLEAN_SORTED([]) # # Normilizes output ports, recirc_id, packets and macs. # m4_define([DUMP_CLEAN_SORTED], [sed -e 's/used:\([[0-9.]]*s\|never\)/used:0.001s/;s/eth(src=[[a-z0-9:]]*,dst=[[a-z0-9:]]*)/eth(macs)/;s/actions:[[0-9,]]\+/actions:output/;s/recirc_id(0),//' | sort]) # OVS_CHECK_ACTIONS([ACTIONS]) # # This extracts and matches the action for IPv4 rules for ingress port p0 # m4_define([OVS_CHECK_ACTIONS], [ AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | sed -n 's/^.*in_port(2),eth(.*),eth_type(0x0800).*actions:\(.*\)/\1/p' | tr -d '\n'], [0], [$1]) ]) m4_define([CHECK_TC_INGRESS_PPS], [ OVS_CHECK_TC_QDISC() AT_CHECK([ip link add ovs_tc_pps0 type veth peer name ovs_tc_pps1 dnl || exit 77]) on_exit 'ip link del ovs_tc_pps0' AT_CHECK([tc qdisc add dev ovs_tc_pps0 handle ffff: ingress || exit 77]) AT_CHECK([tc filter add dev ovs_tc_pps0 parent ffff: u32 match dnl u32 0 0 police pkts_rate 100 pkts_burst 10 || exit 77]) ]) AT_SETUP([offloads - ping between two ports - offloads disabled]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=offloaded], [0], []) AT_CHECK([test $(ovs-appctl upcall/show | grep -c "offloaded flows") -eq 0], [0], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - ping between two ports - offloads enabled]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [ignore]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], []) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - tc-policy configuration]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true \ -- set Open_vSwitch . other_config:tc-policy=skip_hw]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [ignore]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], []) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore]) AT_CHECK([tc -d filter show dev ovs-p0 ingress | grep -q "skip_hw"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - set ingress_policing_rate and ingress_policing_burst - offloads disabled]) AT_KEYWORDS([ingress_policing]) OVS_CHECK_TC_QDISC() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=false]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_rate=100]) AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_burst=10]) AT_CHECK([ovs-vsctl --columns=other_config list open], [0], [dnl other_config : {hw-offload="false"} ]) m4_define([POLICE_CONF], [rate 100Kbit burst 1...b]) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(rate [[0-9]]*[[a-zA-Z]]* burst [[0-9]]*[[a-zA-Z]]*\).*/\1/; T; p; q' | grep -q 'POLICE_CONF']) AT_CHECK([tc -s -d filter show dev ovs-p0 ingress | grep -E "basic|matchall" > /dev/null], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - set ingress_policing_rate and ingress_policing_burst - offloads enabled]) AT_KEYWORDS([ingress_policing]) OVS_CHECK_TC_QDISC() OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_rate=100]) AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_burst=10]) AT_CHECK([ovs-vsctl --columns=other_config list open], [0], [dnl other_config : {hw-offload="true"} ]) m4_define([POLICE_CONF], [rate 100Kbit burst 1...b]) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(rate [[0-9]]*[[a-zA-Z]]* burst [[0-9]]*[[a-zA-Z]]*\).*/\1/; T; p; q' | grep -q 'POLICE_CONF']) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | grep matchall | sed -n 's/.*\(matchall\).*/\1/; T; p; q'], [0], [dnl matchall ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - set ingress_policing_kpkts_rate and ingress_policing_kpkts_burst - offloads disabled]) AT_KEYWORDS([ingress_policing_kpkts]) CHECK_TC_INGRESS_PPS() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=false]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_kpkts_rate=100]) AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_kpkts_burst=10]) AT_CHECK([ovs-vsctl --columns=other_config list open], [0], [dnl other_config : {hw-offload="false"} ]) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(pkts_rate [[0-9]]*[[a-zA-Z]]* pkts_burst [[0-9]]*[[a-zA-Z]]*\).*/\1/; T; p; q' | sed -e 's/10240/10000/'], [0],[dnl pkts_rate 100000 pkts_burst 10000 ]) AT_CHECK([tc -s -d filter show dev ovs-p0 ingress | grep -E "basic|matchall" > /dev/null], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - set ingress_policing_kpkts_rate and ingress_policing_kpkts_burst - offloads enabled]) AT_KEYWORDS([ingress_policing_kpkts]) CHECK_TC_INGRESS_PPS() OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_kpkts_rate=100]) AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_kpkts_burst=10]) AT_CHECK([ovs-vsctl --columns=other_config list open], [0], [dnl other_config : {hw-offload="true"} ]) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(pkts_rate [[0-9]]*[[a-zA-Z]]* pkts_burst [[0-9]]*[[a-zA-Z]]*\).*/\1/; T; p; q' | sed -e 's/10240/10000/'], [0],[dnl pkts_rate 100000 pkts_burst 10000 ]) AT_CHECK([tc -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(matchall\).*/\1/; T; p; q'], [0], [dnl matchall ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - check interface meter offloading - offloads disabled]) AT_KEYWORDS([dp-meter]) AT_SKIP_IF([test $HAVE_NC = "no"]) OVS_CHECK_GITHUB_ACTION() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1']) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.2 lladdr f0:00:00:01:01:02 dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.1 lladdr f0:00:00:01:01:01 dev p1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "actions=normal"]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) NETNS_DAEMONIZE([at_ns1], [nc -u -l 5678 > /dev/null ], [nc0.pid]) AT_CHECK([ovs-ofctl -O OpenFlow13 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "priority=10,in_port=ovs-p0,udp actions=meter:1,normal"]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "priority=1 actions=normal"]) NS_CHECK_EXEC([at_ns0], [echo "mark" | nc $NC_EOF_OPT -u 10.1.1.2 5678 -p 6789]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "meter" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:0, bytes:0, used:0.001s, actions:meter(0),3 ]) sleep 1 for i in `seq 10`; do NS_CHECK_EXEC([at_ns0], [echo "mark" | nc $NC_EOF_OPT -u 10.1.1.2 5678 -p 6789]) done AT_CHECK([ovs-appctl dpctl/dump-flows | grep "meter" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:10, bytes:470, used:0.001s, actions:meter(0),3 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | sed -e 's/duration:[[0-9]].[[0-9]]*s/duration:0.001s/'], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:11 byte_in_count:517 duration:0.001s bands: 0: packet_count:9 byte_count:423 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - check interface meter offloading - offloads enabled]) AT_KEYWORDS([offload-meter]) OVS_CHECK_GITHUB_ACTION() CHECK_TC_INGRESS_PPS() AT_SKIP_IF([test $HAVE_NC = "no"]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1']) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.2 lladdr f0:00:00:01:01:02 dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.1 lladdr f0:00:00:01:01:01 dev p1]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "actions=normal"]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) NETNS_DAEMONIZE([at_ns1], [nc -u -l 5678 > /dev/null ], [nc0.pid]) AT_CHECK([ovs-ofctl -O OpenFlow13 del-flows br0]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "priority=10,in_port=ovs-p0,udp actions=meter:1,normal"]) AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br0 "priority=1 actions=normal"]) NS_CHECK_EXEC([at_ns0], [echo "mark" | nc $NC_EOF_OPT -u 10.1.1.2 5678 -p 6789]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "meter" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:0, bytes:0, used:0.001s, actions:meter(0),3 ]) sleep 1 for i in `seq 10`; do NS_CHECK_EXEC([at_ns0], [echo "mark" | nc $NC_EOF_OPT -u 10.1.1.2 5678 -p 6789]) done AT_CHECK([ovs-appctl dpctl/dump-flows | grep "meter" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x0800),ipv4(proto=17,frag=no), packets:10, bytes:330, used:0.001s, actions:meter(0),3 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | sed -e 's/duration:[[0-9]].[[0-9]]*s/duration:0.001s/'], [0], [dnl OFPST_METER reply (OF1.3) (xid=0x2): meter:1 flow_count:1 packet_in_count:11 byte_in_count:377 duration:0.001s bands: 0: packet_count:9 byte_count:0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - check_pkt_len action - offloads disabled]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns1, at_ns2, at_ns3, at_ns4) ADD_VETH(p1, at_ns1, br0, "10.1.1.1/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.2/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.3/24") ADD_VETH(p4, at_ns4, br0, "10.1.1.4/24") AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=output:3 table=4,in_port=1,reg0=0x0 actions=output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) NETNS_DAEMONIZE([at_ns3], [tcpdump -l -n -U -i p3 dst 10.1.1.2 and icmp > p3.pcap 2>/dev/null], [tcpdump3.pid]) NETNS_DAEMONIZE([at_ns4], [tcpdump -l -n -U -i p4 dst 10.1.1.2 and icmp > p4.pcap 2>/dev/null], [tcpdump4.pid]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(4),le(5)),3 in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(4),le(5)),3 in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded], [0], []) AT_CHECK([test $(ovs-appctl upcall/show | grep -c "offloaded flows") -eq 0], [0], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CHECK([cat p3.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 1032 ]) AT_CHECK([cat p4.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 72 ]) AT_CLEANUP AT_SETUP([offloads - check_pkt_len action - offloads enabled]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ADD_NAMESPACES(at_ns1, at_ns2, at_ns3, at_ns4) ADD_VETH(p1, at_ns1, br0, "10.1.1.1/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.2/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.3/24") ADD_VETH(p4, at_ns4, br0, "10.1.1.4/24") AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=output:3 table=4,in_port=1,reg0=0x0 actions=output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) NETNS_DAEMONIZE([at_ns3], [tcpdump -l -n -U -i p3 dst 10.1.1.2 and icmp > p3.pcap 2>/dev/null], [tcpdump3.pid]) NETNS_DAEMONIZE([at_ns4], [tcpdump -l -n -U -i p4 dst 10.1.1.2 and icmp > p4.pcap 2>/dev/null], [tcpdump4.pid]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | sed 's/bytes:11348/bytes:11614/'], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(4),le(5)),3 in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], []) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | sed 's/bytes:11348/bytes:11614/'], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(4),le(5)),3 in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore]) sleep 1 kill $(cat tcpdump3.pid) kill $(cat tcpdump4.pid) AT_CHECK([cat p3.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 1032 ]) AT_CHECK([cat p4.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 72 ]) # This test verifies the total packet counters work when individual branches # are taken. AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=4,in_port=1,reg0=0x1 actions=output:2 table=4,in_port=1,reg0=0x0 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | sed 's/bytes:11348/bytes:11614/'], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(3),le(3)) in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) # The remaining tests are just to make sure the datapath flow actions are # encoded/decoded the right way. AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=4,in_port=1,reg0=0x1 actions=output:4 table=4,in_port=1,reg0=0x0 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(5),le(3))]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=4,in_port=1,reg0=0x0 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(drop),le(3))]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=4,in_port=1,reg0=0x1 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(3),le(drop))]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=4,in_port=1,reg0=0x0 actions=output:2,3 table=4,in_port=1,reg0=0x1 actions=output:2,4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(3,5),le(3,4))]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=mod_nw_tos:4,output:3 table=4,in_port=1,reg0=0x0 actions=mod_nw_tos:8,output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) NETNS_DAEMONIZE([at_ns3], [tcpdump -l -n -U -i p3 dst 10.1.1.2 and icmp > p3_2.pcap 2>/dev/null], [tcpdump3_2.pid]) NETNS_DAEMONIZE([at_ns4], [tcpdump -l -n -U -i p4 dst 10.1.1.2 and icmp > p4_2.pcap 2>/dev/null], [tcpdump4_2.pid]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | sed -e 's/bytes:11348/bytes:11614/'], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(proto=1,tos=0/0xfc,frag=no), packets:19, bytes:11614, used:0.001s, actions:check_pkt_len(size=200,gt(set(ipv4(tos=0x4/0xfc)),4),le(set(ipv4(tos=0x8/0xfc)),5)),3 in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:11614, used:0.001s, actions:output ]) sleep 1 kill $(cat tcpdump3_2.pid) kill $(cat tcpdump4_2.pid) AT_CHECK([cat p3_2.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 1032 ]) AT_CHECK([cat p4_2.pcap | awk 'NF{print $NF}' | uniq -c | awk '{$1=$1;print}'], [0], [dnl 10 72 ]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=mod_nw_tos:4,output:3 table=4,in_port=1,reg0=0x0 actions=mod_dl_src:00:11:11:11:11:11,output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(set(ipv4(tos=0x4/0xfc)),4),le(set(eth(src=00:11:11:11:11:11)),5)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=mod_dl_src:00:11:11:11:11:11,output:3 table=4,in_port=1,reg0=0x0 actions=mod_nw_tos:8,output:4 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(set(eth(src=00:11:11:11:11:11)),4),le(set(ipv4(tos=0x8/0xfc)),5)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=output:br0 table=4,in_port=1,reg0=0x0 actions=output:br0 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(1),le(1)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=output:br0 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(1),le(drop)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x0 actions=output:br0 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 1024 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(drop),le(1)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(drop),le(drop)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x1 actions=check_pkt_larger(400)->NXM_NX_REG0[[0]],resubmit(,5) table=4,in_port=1,reg0=0x0 actions=output:4 table=5,in_port=1,reg0=0x1 actions=output:4 table=5,in_port=1,reg0=0x0 actions=output:3 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(check_pkt_len(size=400,gt(5),le(4))),le(5)),3]) AT_CHECK([ovs-appctl revalidator/wait], [0]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl table=0,in_port=2 actions=output:1 table=0,in_port=1 actions=load:0x1->NXM_NX_REG1[[]],resubmit(,1),load:0x2->NXM_NX_REG1[[]],resubmit(,1) table=1,in_port=1,reg1=0x1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,4) table=1,in_port=1,reg1=0x2 actions=output:2 table=4,in_port=1,reg0=0x0 actions=check_pkt_larger(100)->NXM_NX_REG0[[0]],resubmit(,5) table=4,in_port=1,reg0=0x1 actions=output:4 table=5,in_port=1,reg0=0x1 actions=output:4 table=5,in_port=1,reg0=0x0 actions=output:3 ]) AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) sleep 1 NS_CHECK_EXEC([at_ns1], [ping -q -c 10 -i 0.1 -W 2 -s 64 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ], [], [ovs-appctl dpctl/dump-flows; ovs-ofctl dump-flows br0]) OVS_CHECK_ACTIONS([check_pkt_len(size=200,gt(5),le(check_pkt_len(size=100,gt(5),le(4)))),3]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - offload flow to none-offload]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl add in_port=ovs-p0,actions=ovs-p1 add in_port=ovs-p1,actions=ovs-p0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:3 recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:2 ]) dnl Here we use an output action with truncate, which will force a kernel flow. AT_DATA([flows2.txt], [dnl modify in_port=ovs-p0,actions=output(port=ovs-p1, max_len=128) modify in_port=ovs-p1,actions=output(port=ovs-p0, max_len=128) ]) AT_CHECK([ovs-ofctl add-flows br0 flows2.txt]) AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),3 recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:3 recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:2 ]) AT_CHECK([ovs-appctl coverage/read-counter ukey_invalid_stat_reset], [0], [dnl 0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - delete ufid mapping if device not exist - offloads enabled]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1, at_ns2) dnl Disable IPv6 to skip unexpected flow AT_CHECK([sysctl -w net.ipv6.conf.br0.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns2], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "aa:1a:54:e9:c5:56") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl 2 packets transmitted, 2 received, 0% packet loss, time 0ms ]) dnl Delete and add interface ovs-p0/p0 AT_CHECK([ip link del dev ovs-p0]) AT_CHECK([ip link add p0 type veth peer name ovs-p0 || return 77]) AT_CHECK([ip link set p0 netns at_ns0]) AT_CHECK([ip link set dev ovs-p0 up]) NS_CHECK_EXEC([at_ns0], [ip addr add dev p0 "10.1.1.1/24"]) NS_CHECK_EXEC([at_ns0], [ip link set dev p0 up]) NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address "aa:1a:54:e9:c5:56"]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Generate flows to trigger the hmap expand once ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl 2 packets transmitted, 2 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.3 | FORMAT_PING], [0], [dnl 2 packets transmitted, 2 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Fix purge fail occasionally AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([test $(ovs-appctl dpctl/dump-flows | grep -c "eth_type(0x0800)") -eq 0], [0], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP(["/could not open network device ovs-p0/d /on nonexistent port/d /No such device/d /failed to offload flow/d "]) AT_CLEANUP AT_SETUP([offloads - ping over vxlan tunnel with gbp - offloads enabled]) OVS_CHECK_TUNNEL_TSO() OVS_CHECK_VXLAN() OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_SKIP_IF([! grep -q "probe tc: vxlan gbp is supported." ovs-vswitchd.log]) AT_SKIP_IF([test $HAVE_NFT = no -a $HAVE_IPTABLES = no]) ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24], [options:exts=gbp]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=br0 actions=load:0x200->NXM_NX_TUN_GBP_ID[], output:at_vxlan0]") AT_CHECK([ovs-ofctl add-flow br0 "in_port=at_vxlan0, tun_gbp_id=512 actions=output:br0"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.1.1.1/24], [id 0 dstport 4789 gbp]) if test $HAVE_NFT = yes; then NS_CHECK_EXEC([at_ns0], [nft -f - << EOF table ip filter { chain OUTPUT { type filter hook output priority filter; policy accept; counter meta mark set 512 } } table ip filter { chain INPUT { type filter hook input priority filter; policy accept; mark 512 counter accept; } } EOF ]) else NS_CHECK_EXEC([at_ns0], [iptables -I OUTPUT -p ip -j MARK --set-mark 512 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns0], [iptables -I INPUT -m mark --mark 512 -j ACCEPT 2>/dev/null], [0], [ignore]) fi dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 1000 -i 0.01 10.1.1.100 | FORMAT_PING], [0], [dnl 1000 packets transmitted, 1000 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | grep "tp_dst=4789,vxlan(gbp(id=512))" | wc -l], [0], [dnl 1 ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | grep "tp_dst=4789,vxlan(gbp(id=512,flags=0))" | wc -l], [0], [dnl 1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - IGMP with ip rewrite - offloads enabled]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Set up the ip field modify flow. AT_CHECK([ovs-ofctl add-flow br0 "priority=100 in_port=ovs-p0,ip actions=mod_nw_tos:12,output:ovs-p1"]) dnl Add and del multicast address to send IGMP packet. NS_CHECK_EXEC([at_ns0], [ip addr add dev p0 224.10.10.10/24 autojoin 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns0], [ip addr del dev p0 224.10.10.10/24 2>/dev/null], [0]) OVS_WAIT_UNTIL([test `ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | wc -l` -ge 1]) dnl Check the offloaded flow. AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | strip_stats], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(proto=2,tos=0xc0/0xfc,frag=no), packets:0, bytes:0, used:0.001s, actions:set(ipv4(tos=0xc/0xfc)),3 ]) dnl Check the tc rule. AT_CHECK([tc -d filter show dev ovs-p0 ingress | grep -q "csum (iph)"], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - IPIP wth ip rewrite - offloads enabled]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) AT_CHECK([ovs-ofctl add-flow br0 "priority=0 actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Set up the ip field modify flow. AT_CHECK([ovs-ofctl add-flow br0 "priority=100 in_port=ovs-p0,ip,nw_dst=10.1.1.2 actions=dec_ttl,output:ovs-p1"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 in_port=ovs-p1,ip,nw_dst=10.1.1.1 actions=dec_ttl,output:ovs-p0"]) dnl Set up ipip tunnel in NS. NS_CHECK_EXEC([at_ns0], [ip tunnel add ipip0 remote 10.1.1.2 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns0], [ip link set dev ipip0 up 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns0], [ip addr add dev ipip0 192.168.1.1/30 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns1], [ip tunnel add ipip0 remote 10.1.1.1 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns1], [ip link set dev ipip0 up 2>/dev/null], [0]) NS_CHECK_EXEC([at_ns1], [ip addr add dev ipip0 192.168.1.2/30 2>/dev/null], [0]) dnl Check the tunnel. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 192.168.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check the offloaded flow. AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED | strip_stats], [0], [dnl in_port(2),eth(),eth_type(0x0800),ipv4(dst=10.1.1.2,proto=4,ttl=64,frag=no), packets:0, bytes:0, used:0.001s, actions:set(ipv4(ttl=63)),3 in_port(3),eth(),eth_type(0x0800),ipv4(dst=10.1.1.1,proto=4,ttl=64,frag=no), packets:0, bytes:0, used:0.001s, actions:set(ipv4(ttl=63)),2 ]) dnl Check the tc rule. AT_CHECK([tc -d filter show dev ovs-p0 ingress | grep -q "csum (iph)"], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - re-probe drop action]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_DROP_ACTION() AT_KEYWORDS(drop_action) dnl Trigger a re-probe of the explicit drop action. AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true]) OVS_WAIT_UNTIL([grep -q "Datapath does not support explicit drop action" ovs-vswitchd.log]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offload - Tunnel offloading with flags]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_VXLAN() OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . \ other_config:hw-offload=true]) AT_SKIP_IF([! grep -q "probe tc: enc flags are supported." ovs-vswitchd.log]) ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl Linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.1.1.1/24], [id 0 dstport 4789]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check the overlay, with some icmp traffic. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) # Check for a set tunnel action with df flag set. AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded], [], [stdout]) AT_CHECK( [grep -q -F "set(tunnel(dst=172.31.1.1,ttl=64,tp_dst=4789,flags(df)))" \ stdout]) AT_CHECK( [grep -q -F "tunnel(tun_id=0x0,src=172.31.1.1,dst=172.31.1.100,tp_dst=4789,flags(+key))" \ stdout]) # Now verify the none df, and forced csum case. AT_CHECK([ovs-vsctl set int at_vxlan0 options:df_default=false \ options:csum=true]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded ], [], [stdout]) AT_CHECK( [grep -q -F "set(tunnel(dst=172.31.1.1,ttl=64,tp_dst=4789,flags(csum)))" \ stdout]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - 802.1ad should be offloaded]) OVS_TRAFFIC_VSWITCHD_START( [], [], [-- set Open_vSwitch . other_config:hw-offload=true]) OVS_CHECK_8021AD() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_SVLAN(p0, at_ns0, 4094, "10.255.2.1/24") ADD_SVLAN(p1, at_ns1, 4094, "10.255.2.2/24") ADD_CVLAN(p0.4094, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p1.4094, at_ns1, 100, "10.2.2.2/24") AT_CHECK([ovs-ofctl add-flow br0 "priority=1 action=normal"]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) AT_CHECK([ovs-appctl dpctl/dump-flows type=tc,offloaded | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], [dnl in_port(2),eth(macs),eth_type(0x88a8),vlan(vid=4094,pcp=0),encap(eth_type(0x0800)), packets:0, bytes:0, used:0.001s, actions:output in_port(3),eth(macs),eth_type(0x88a8),vlan(vid=4094,pcp=0),encap(eth_type(0x0800)), packets:0, bytes:0, used:0.001s, actions:output ]) AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | DUMP_CLEAN_SORTED], [0], []) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - split kernel vs offload datapath rules]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p1, at_ns0, br0, "10.0.0.1/24") ADD_VETH(p2, at_ns1, br0, "10.0.0.2/24") AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=1 \ -- set interface ovs-p2 ofport_request=2]) AT_DATA([groups.txt], [dnl group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2) ]) AT_DATA([flows.txt], [dnl table=0,arp,actions=NORMAL table=0,priority=100,cookie=0x12345678,in_port=ovs-p1,ip,nw_dst=10.0.0.2,actions=resubmit(,1) table=0,priority=100,cookie=0xabcedf,in_port=ovs-p2,ip,nw_dst=10.0.0.1,actions=ct(table=3) table=1,priority=200,ip,actions=group:1 table=2,ip,actions=ovs-p2 table=3,ip,actions=ovs-p1 ]) AT_CHECK([ovs-ofctl add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.1 -W 2 10.0.0.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl revalidator/wait]) dnl In this test we should not have the first recirculation(s) in the kernel dnl datapath, and the final flow in TC. They should all be in the kernel dnl datapath, as the dp_hash() action is currently not supported by TC. dnl The command below ensures they are all handled in the kernel datapath. AT_CHECK([ovs-appctl dpctl/dump-flows --names type=ovs filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no), packets:2, bytes:196, used:0.001s, actions:hash(l4(0)),recirc() recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.001s, actions:ct(commit),recirc() recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.001s, actions:ovs-p2 ]) AT_CHECK([ovs-appctl dpctl/dump-flows --names type=tc filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl ]) dnl The next test will install two DP flows that are both accepted in the dnl TC datapath. But then the first one is moved to the kernel datapath, dnl we have to make sure both flows are moved to kernel (and no dual rules dnl exist). AT_DATA([flows.txt], [dnl table=0,arp,actions=NORMAL table=0,priority=100,in_port=ovs-p1,ip,nw_dst=10.0.0.2,actions=ct(table=1) table=0,priority=100,in_port=ovs-p2,ip,nw_dst=10.0.0.1,actions=ovs-p1 table=1,ip,actions=ovs-p2 ]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:max-idle=2000]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl revalidator/purge]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.1 -W 2 10.0.0.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl dpctl/dump-flows --names type=tc filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no), packets:2, bytes:168, used:0.001s, actions:ct,recirc() recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:168, used:0.001s, actions:ovs-p2 ]) AT_CHECK([ovs-appctl dpctl/dump-flows --names type=ovs filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl ]) AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=ovs-p1,ip,nw_dst=10.0.0.2,actions=controller(),ct(table=1) ]) AT_CHECK([ovs-ofctl del-flows br0 table=0,in_port=ovs-p1,ip,nw_dst=10.0.0.2]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl revalidator/wait]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.1 -W 2 10.0.0.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Unfortunately, until the unreachable TC rule times out, packets will be dnl handled by upcalls. So we first check for this case and then verify that dnl the rule finally moved. AT_CHECK([ovs-appctl dpctl/dump-flows --names type=tc filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:168, used:0.001s, actions:ovs-p2 ]) AT_CHECK([ovs-appctl dpctl/dump-flows --names type=ovs filter='in_port(ovs-p1),ipv4' | \ strip_recirc | strip_dp_hash | DUMP_CLEAN_SORTED], [0], [dnl recirc_id(),in_port(ovs-p1),eth(),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no), packets:3, bytes:294, used:0.001s, actions:userspace(pid=4294967295,controller(reason=1,dont_send=0,continuation=0,recirc_id=,rule_cookie=0,controller_id=0,max_len=65535)),ct,recirc() ]) check_rules_and_ping() { NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -i 0.1 -W 2 10.0.0.2], [0], [ignore]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p1),ipv4'], [0], [stdout]) [[ "$(wc -l < stdout)" -ne 2 ]] && return 0 grep -q "dp:tc" stdout } OVS_WAIT_WHILE( [check_rules_and_ping], [ovs-appctl dpctl/dump-flows -m --names filter='in_port(ovs-p1),ipv4']) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([offloads - ovs-appctl dpif/offload/show - offloads enabled]) AT_KEYWORDS([dpif-offload]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) sort_dpif_offload_show () { awk ' /^ -/ { dashlines[[++n]] = $0; next } { print } END { asort(dashlines) for (i=1; i<=n; i++) print dashlines[[i]] } ' | sed -E 's/ifindex: [[0-9]]+/ifindex: 0/g' } AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: true Datapaths: system@ovs-system: tc - br0: port_no: 1, ifindex: 0 - ovs-system: port_no: 0, ifindex: 0 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show \ | sed -E 's/"ifindex": [[0-9]]+/"ifindex": 0/g'], [0], [dnl { "enabled": true, "system@ovs-system": { "priority": [[ "tc"]], "providers": { "tc": { "ports": { "br0": { "ifindex": 0, "port_no": 1}, "ovs-system": { "ifindex": 0, "port_no": 0}}}}}} ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-route.at000066400000000000000000000470101514270232600231320ustar00rootroot00000000000000AT_BANNER([system-route]) dnl Add an interface, add/del ip address, check that OVS catches route updates. AT_SETUP([ovs-route - add/remove system route]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap port. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' dnl Add ip address. AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout]) dnl Check that OVS catches route updates. OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local]) dnl Delete ip address. AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout]) dnl Check that routes was removed from OVS. OVS_WAIT_UNTIL([test `ovs-appctl ovs/route/show | grep -c 'p1-route'` -eq 0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route with src - ipv4]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ip link set br0 up]) AT_CHECK([ip addr add 192.168.9.2/24 dev br0], [0], [stdout]) AT_CHECK([ip addr add 192.168.9.3/24 dev br0], [0], [stdout]) AT_CHECK([ip route add 192.168.10.12/32 dev br0 via 192.168.9.1 src 192.168.9.2], [0], [stdout]) AT_CHECK([ip route add 192.168.10.13/32 dev br0 via 192.168.9.1 src 192.168.9.3], [0], [stdout]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '192.168.10.1[[23]]/32' | sort], [dnl Cached: 192.168.10.12/32 dev br0 GW 192.168.9.1 SRC 192.168.9.2 Cached: 192.168.10.13/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route with src - ipv6]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ip link set br0 up]) AT_CHECK([ip -6 addr add fc00:db8:cafe::2/64 dev br0], [0], [stdout]) AT_CHECK([ip -6 addr add fc00:db8:cafe::3/64 dev br0], [0], [stdout]) dnl If we try to add a route immediately after assigning ipv6 addresses, dnl iproute2 would give us "Invalid source address" error, dnl so wait a while to succeed. OVS_WAIT_UNTIL([ip -6 route add fc00:db8:beef::12/128 via fc00:db8:cafe::1 dev br0 src fc00:db8:cafe::3]) OVS_WAIT_UNTIL([ip -6 route add fc00:db8:beef::13/128 via fc00:db8:cafe::1 dev br0 src fc00:db8:cafe::2]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E 'fc00:db8:beef::1[[23]]/128' | sort], [dnl Cached: fc00:db8:beef::12/128 dev br0 GW fc00:db8:cafe::1 SRC fc00:db8:cafe::3 Cached: fc00:db8:beef::13/128 dev br0 GW fc00:db8:cafe::1 SRC fc00:db8:cafe::2]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route - ipv4 via ipv6 nexthop]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl set bridge br0 other-config:hwaddr=00:53:00:00:00:42]) AT_CHECK([ip link set br0 up]) AT_CHECK([ip addr add 192.168.9.2/24 dev br0], [0], [stdout]) AT_CHECK([ip route add 192.168.10.12/32 \ via inet6 fe80::253:ff:fe00:51 dev br0], [0], [stdout]) AT_CHECK([ovs-appctl revalidator/wait]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | \ grep -E '192.168.10.12/32' | sort], [dnl Cached: 192.168.10.12/32 dev br0 GW fe80::253:ff:fe00:51 SRC fe80::253:ff:fe00:42]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Checks that OVS doesn't use routes from non-standard tables. AT_SETUP([ovs-route - route tables]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap port. on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) dnl Add ip address. AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout]) dnl Check that OVS catches route updates. OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local]) dnl Add a route to the main routing table and check that OVS caches dnl this new route. AT_CHECK([ip route add 10.0.0.18/32 dev p1-route]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17]) dnl Negative check for custom routing table using route-table library. AT_CHECK([ovstest test-lib-route-table-dump | grep rta_table_id:\ 42], [1]) AT_CHECK([ovstest test-lib-route-table-dump | grep rta_table_id:\ 1042], [1]) dnl Add a route to a custom routing table and check that OVS doesn't cache it. AT_CHECK([ip route add 10.0.0.19/32 dev p1-route table 42]) AT_CHECK([ip route add 10.0.0.20/32 dev p1-route table 1042]) AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.19']) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that OVS didn't learn this route. AT_CHECK([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [0], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17 ]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/rta_table_id:.*42/{print$1" "$15" "$16}' | sort], [0], [dnl 10.0.0.19/32 rta_table_id: 42 10.0.0.20/32 rta_table_id: 1042 ]) dnl Delete a route from the main table and check that OVS removes the route dnl from the cache. AT_CHECK([ip route del 10.0.0.18/32 dev p1-route]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local]) dnl Delete a route from a custom routing table and check that the cache dnl dosn't change. AT_CHECK([ip route del 10.0.0.19/32 dev p1-route table 42]) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that the cache is still the same. AT_CHECK([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [0], [dnl Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local ]) dnl Delete ip address. AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout]) dnl Check that routes were removed from OVS. OVS_WAIT_UNTIL([test $(ovs-appctl ovs/route/show | grep -c 'p1-route') -eq 0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route with multiple nexthop - ipv4]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap ports. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p2-route mode tap]) AT_CHECK([ip link set p2-route up]) on_exit 'ip link del p2-route' AT_CHECK([ip addr add 192.168.42.10/24 dev p1-route], [0], [stdout]) AT_CHECK([ip addr add 192.168.51.10/24 dev p2-route], [0], [stdout]) AT_CHECK([ip route add 172.16.42.0/24 nexthop via 192.168.42.1 \ dev p1-route nexthop via 192.168.51.1 dev p2-route], [0], [stdout]) dnl NOTE: At the time of this writing, it is expected that only the first route dnl will be stored in ovs-router. OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '172.16.42.0/24' | \ sort], [dnl Cached: 172.16.42.0/24 dev p1-route GW 192.168.42.1 SRC 192.168.42.10]) dnl Confirm that both nexthops are available when using the route-table library dnl directly. AT_CHECK([ovstest test-lib-route-table-dump | grep 172.16.42.0.*nexthop | sort], [0], [dnl 172.16.42.0/24 nexthop family: AF_INET addr: 192.168.42.1 ifname: p1-route 172.16.42.0/24 nexthop family: AF_INET addr: 192.168.51.1 ifname: p2-route ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route - ipv4 via multiple ipv6 nexthop]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap ports. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p2-route mode tap]) AT_CHECK([ip link set p2-route up]) on_exit 'ip link del p2-route' AT_CHECK([ip -6 addr add fc00:db8:dead::10/64 dev p1-route], [0], [stdout]) AT_CHECK([ip -6 addr add fc00:db8:beef::10/64 dev p2-route], [0], [stdout]) AT_CHECK([ip route add 172.16.42.0/24 nexthop via inet6 fc00:db8:dead::1 \ dev p1-route nexthop via inet6 fc00:db8:beef::1 dev p2-route], [0], [stdout]) dnl NOTE: At the time of this writing, it is expected that only the first route dnl will be stored in ovs-router. OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep -E '172.16.42.0/24' | \ sort], [dnl Cached: 172.16.42.0/24 dev p1-route GW fc00:db8:dead::1 SRC fc00:db8:dead::10]) dnl Confirm that both nexthops are available when using the route-table library dnl directly. AT_CHECK([ovstest test-lib-route-table-dump | grep 172.16.42.0.*nexthop | sort], [0], [dnl 172.16.42.0/24 nexthop family: AF_INET6 addr: fc00:db8:beef::1 ifname: p2-route 172.16.42.0/24 nexthop family: AF_INET6 addr: fc00:db8:dead::1 ifname: p1-route ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([ovs-route - add system route with multiple nexthop - ipv6]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap ports. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p2-route mode tap]) AT_CHECK([ip link set p2-route up]) on_exit 'ip link del p2-route' AT_CHECK([ip -6 addr add fc00:db8:dead::10/64 dev p1-route], [0], [stdout]) AT_CHECK([ip -6 addr add fc00:db8:beef::10/64 dev p2-route], [0], [stdout]) AT_CHECK([ip -6 route add fc00:db8:cafe::/64 nexthop via fc00:db8:dead::1 \ dev p1-route nexthop via fc00:db8:beef::1 dev p2-route], [0], [stdout]) dnl NOTE: At the time of this writing, it is expected that only the first route dnl will be stored in ovs-router. OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | \ grep -E 'fc00:db8:cafe::/64' | sort], [dnl Cached: fc00:db8:cafe::/64 dev p1-route GW fc00:db8:dead::1 SRC fc00:db8:dead::10]) dnl Confirm that both nexthops are available when using the route-table library dnl directly. AT_CHECK([ovstest test-lib-route-table-dump | grep fc00:db8:cafe::.*nexthop | \ sort], [0], [dnl fc00:db8:cafe::/64 nexthop family: AF_INET6 addr: fc00:db8:beef::1 ifname: p2-route fc00:db8:cafe::/64 nexthop family: AF_INET6 addr: fc00:db8:dead::1 ifname: p1-route ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([route-table - exported functions work for netlink-notifier]) AT_KEYWORDS([route]) dnl Create tap ports. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p2-route mode tap]) AT_CHECK([ip link set p2-route up]) on_exit 'ip link del p2-route' AT_CHECK([ip -6 addr add fc00:db8:dead::10/64 dev p1-route], [0], [stdout]) AT_CHECK([ip -6 addr add fc00:db8:beef::10/64 dev p2-route], [0], [stdout]) AT_CHECK([ovstest test-lib-route-table-monitor 'ip route add 172.16.42.0/24 \ nexthop via inet6 fc00:db8:dead::1 dev p1-route \ nexthop via inet6 fc00:db8:beef::1 dev p2-route' | \ grep 172.16.42.0.*nexthop | sort], [0], [dnl 172.16.42.0/24 nexthop family: AF_INET6 addr: fc00:db8:beef::1 ifname: p2-route 172.16.42.0/24 nexthop family: AF_INET6 addr: fc00:db8:dead::1 ifname: p1-route ]) AT_CLEANUP AT_SETUP([route-table - route attributes]) AT_KEYWORDS([route]) dnl Create tap ports. AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) on_exit 'ip link del p1-route' dnl Add ip address. AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/^10.0.0.17/{print$1" "$6" "$7}'], [0], [dnl 10.0.0.17/32 rtm_protocol: RTPROT_KERNEL ]) dnl Add route. AT_CHECK([ip route add 192.168.10.12/32 dev p1-route via 10.0.0.18], [0], [stdout]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/^192.168.10.12/{print$1" "$17" "$18}'], [0], [dnl 192.168.10.12/32 rta_priority: 0 ]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/^192.168.10.12/{print$1" "$6" "$7}'], [0], [dnl 192.168.10.12/32 rtm_protocol: RTPROT_BOOT ]) dnl Delete route. AT_CHECK([ip route del 192.168.10.12/32 dev p1-route via 10.0.0.18], [0], [stdout]) dnl Add route with priority. AT_CHECK([ip route add 192.168.10.12/32 dev p1-route via 10.0.0.18 metric 42], [0], [stdout]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/^192.168.10.12/{print$1" "$17" "$18}'], [0], [dnl 192.168.10.12/32 rta_priority: 42 ]) AT_CHECK([ovstest test-lib-route-table-dump | \ awk '/^192.168.10.12/{print$1" "$6" "$7}'], [0], [dnl 192.168.10.12/32 rtm_protocol: RTPROT_BOOT ]) AT_CLEANUP dnl Checks that OVS ignores unsupported routing rules. AT_SETUP([ovs-route - unsupported rules]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap port. on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) dnl Check there are no non-standard rules cached in OVS. AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/show -6], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main ]) dnl Add unsupported rules to kernel. on_exit 'ip rule del priority 100 fwmark 0x16 lookup 42' AT_CHECK([ip rule add priority 100 fwmark 0x16 lookup 42]) on_exit 'ip rule del priority 101 from 10.0.0.1 dport 22 lookup 42' AT_CHECK([ip rule add priority 101 from 10.0.0.1 dport 22 lookup 42]) on_exit 'ip rule del priority 102 from 10.0.0.1 sport 22 lookup 42' AT_CHECK([ip rule add priority 102 from 10.0.0.1 sport 22 lookup 42]) on_exit 'ip rule del priority 103 from 10.0.0.1 tun_id 22 lookup 42' AT_CHECK([ip rule add priority 103 from 10.0.0.1 tun_id 22 lookup 42]) on_exit 'ip rule del priority 104 iif p1-route lookup 42' AT_CHECK([ip rule add priority 104 iif p1-route lookup 42]) on_exit 'ip rule del priority 105 ipproto udp lookup 42' AT_CHECK([ip rule add priority 105 ipproto udp lookup 42]) on_exit 'ip rule del priority 106 from all tun_id 22 lookup 42' AT_CHECK([ip rule add priority 106 from all tun_id 22 lookup 42]) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check OVS rules cache hasn't changed. AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ovs/route/rule/show -6], [0], [dnl Cached: 0: from all lookup local Cached: 32766: from all lookup main ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Checks that OVS uses routes from non-standard tables when there is a rule dnl referencing the table. AT_SETUP([ovs-route - route tables + rules]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap port. on_exit 'ip link del p1-route' AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip link set p1-route up]) dnl Add ip address. AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout]) dnl Add routes to a custom routing table with a source match rule and check dnl that OVS caches them. on_exit 'ip rule del from 10.0.0.1 lookup 42' AT_CHECK([ip rule add from 10.0.0.1 lookup 42]) AT_CHECK([ip rule show | grep -q 'from 10.0.0.1 lookup 42']) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that the rule is cached in OVS. AT_CHECK([ovs-appctl ovs/route/rule/show | grep -q 'lookup 42']) dnl Check that the route cache is unchanged (because the table has not been dnl created yet). AT_CHECK([ovs-appctl ovs/route/show table=42], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Table 'table=42' not found ovs-appctl: ovs-vswitchd: server returned an error ]) on_exit 'ip route flush table 42' AT_CHECK([ip route add 10.0.0.18/32 dev p1-route table 42]) AT_CHECK([ip route add 10.0.0.19/32 dev p1-route table 42]) AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.18']) AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.19']) AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that OVS learn those routes. AT_CHECK([ovs-appctl ovs/route/show table=42 | sort], [0], [dnl Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17 Cached: 10.0.0.19/32 dev p1-route SRC 10.0.0.17 Route Table #42: ]) dnl Delete a route from the custom table and check that OVS removes the route dnl from the cache. AT_CHECK([ip route del 10.0.0.18/32 dev p1-route table 42]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show table=42], [dnl Route Table #42: Cached: 10.0.0.19/32 dev p1-route SRC 10.0.0.17]) dnl Delete the rule and check that the table no longer exists in the cache. AT_CHECK([ip rule del from 10.0.0.1 lookup 42]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl ovs/route/show table=42], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Table 'table=42' not found ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Add a custom table and check that OVS ignores it because no rule is dnl referencing it. on_exit 'ip route flush table 43' AT_CHECK([ip route add 10.0.0.18/32 dev p1-route table 43]) AT_CHECK([ip route show table 43 | grep 'p1-route' | grep -q '10.0.0.18']) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl ovs/route/show table=43], [2], [], [stderr]) AT_CHECK([tail -2 stderr], [0], [dnl Table 'table=43' not found ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Checks that OVS performs route lookup according to rules with src match. AT_SETUP([ovs-route - route lookup + rules]) AT_KEYWORDS([route]) OVS_TRAFFIC_VSWITCHD_START() dnl Create tap ports. on_exit 'ip link del p1-route' on_exit 'ip link del p2-route' AT_CHECK([ip tuntap add name p1-route mode tap]) AT_CHECK([ip tuntap add name p2-route mode tap]) AT_CHECK([ip link set p1-route up]) AT_CHECK([ip link set p2-route up]) dnl Add ip addresses, they need to be from same subnet so that the main router dnl table in OVS contains only one of the routes. AT_CHECK([ip addr add 10.0.0.11/24 dev p1-route], [0], [stdout]) AT_CHECK([ip addr add 10.0.0.12/24 dev p2-route], [0], [stdout]) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that OVS learn only one of the routes to the subnet. AT_CHECK([ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -q 'p1-route'], [1]) AT_CHECK([ovs-appctl ovs/route/show | grep -F '10.0.0.0/24' | grep -q 'p2-route']) dnl Check that OVS lookup returns p2-route even for srouce IP of p1-route dnl internface. AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11 gateway :: dev p2-route ]) dnl Add rule to use a custom routing table for source IP of p1-route. on_exit 'ip route flush table 42' AT_CHECK([ip route add 10.0.0.0/24 dev p1-route table 42]) AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -Fq '10.0.0.0/24']) on_exit 'ip rule del from 10.0.0.11 lookup 42' AT_CHECK([ip rule add from 10.0.0.11 lookup 42]) AT_CHECK([ip rule show | grep -q 'from 10.0.0.11 lookup 42']) dnl Give the main thread a chance to act. AT_CHECK([ovs-appctl revalidator/wait]) dnl Check that OVS uses custom table for lookup with source IP of p1-route dnl internface and returns the correct p1-route. AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.11], [0], [src 10.0.0.11 gateway :: dev p1-route ]) dnl Check that OVS uses main table for lookup with other source IP that doesn't dnl match the rule. AT_CHECK([ovs-appctl ovs/route/lookup 10.0.0.10 src=10.0.0.12], [0], [src 10.0.0.12 gateway :: dev p2-route ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-tap.at000066400000000000000000000022301514270232600225530ustar00rootroot00000000000000AT_SETUP([traffic between namespaces using tap]) AT_KEYWORDS([http_tap]) OVS_TRAFFIC_VSWITCHD_START() AT_SKIP_IF([test $HAVE_TUNCTL = no]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) AT_CHECK([ip tuntap add tap0 mode tap]) on_exit 'ip tuntap del tap0 mode tap' AT_CHECK([ip tuntap add tap1 mode tap]) on_exit 'ip tuntap del tap1 mode tap' AT_CHECK([ovs-vsctl add-port br0 tap0 -- set int tap0 type=tap]) AT_CHECK([ovs-vsctl add-port br0 tap1 -- set int tap1 type=tap]) AT_CHECK([ip link set tap0 netns at_ns0]) AT_CHECK([ip link set tap1 netns at_ns1]) AT_CHECK([ip netns exec at_ns0 ip link set dev tap0 up]) AT_CHECK([ip netns exec at_ns1 ip link set dev tap1 up]) AT_CHECK([ip netns exec at_ns0 ip addr add 10.1.1.1/24 dev tap0]) AT_CHECK([ip netns exec at_ns1 ip addr add 10.1.1.2/24 dev tap1]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP(["/.*ethtool command ETHTOOL_G.*/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-traffic.at000066400000000000000000016044561514270232600234300ustar00rootroot00000000000000AT_BANNER([datapath-sanity]) AT_SETUP([datapath - ping between two ports]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - http between two ports]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping between two ports on vlan]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VLAN(p0, at_ns0, 100, "10.2.2.1/24") ADD_VLAN(p1, at_ns1, 100, "10.2.2.2/24") NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping between two ports on cvlan]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_8021AD() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_SVLAN(p0, at_ns0, 4094, "10.255.2.1/24") ADD_SVLAN(p1, at_ns1, 4094, "10.255.2.2/24") ADD_CVLAN(p0.4094, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p1.4094, at_ns1, 100, "10.2.2.2/24") OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 between two ports]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 between two ports on vlan]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") ADD_VLAN(p0, at_ns0, 100, "fc00:1::1/96") ADD_VLAN(p1, at_ns1, 100, "fc00:1::2/96") dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00:1::2]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 between two ports on cvlan]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_8021AD() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") ADD_SVLAN(p0, at_ns0, 4094, "fc00:ffff::1/96") ADD_SVLAN(p1, at_ns1, 4094, "fc00:ffff::2/96") ADD_CVLAN(p0.4094, at_ns0, 100, "fc00:1::1/96") ADD_CVLAN(p1.4094, at_ns1, 100, "fc00:1::2/96") OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00:1::2]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00:1::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 between two ports IPv6 later fragments]) OVS_TRAFFIC_VSWITCHD_START() CHECK_LATER_IPV6_FRAGMENTS() AT_CHECK([ovs-ofctl add-flow br0 "priority=1,actions=normal"]) AT_CHECK([ovs-ofctl add-flow br0 "priority=2,tcp6,actions=drop"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ovs-appctl dpctl/dump-flows -m --names > stdout.txt]) NS_CHECK_EXEC([at_ns0], [grep ipv6 stdout.txt | grep frag=later | grep -q proto=44], [0], []) NS_CHECK_EXEC([at_ns0], [grep ipv6 stdout.txt | grep frag=later | grep -v -q proto=44], [1], []) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 between two ports with header modify]) OVS_TRAFFIC_VSWITCHD_START() CHECK_LATER_IPV6_FRAGMENTS() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", e4:11:22:33:44:55) ADD_VETH(p1, at_ns1, br0, "fc00::2/96", e4:11:22:33:44:54) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::3 lladdr e4:11:22:33:44:54 dev p0]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) OVS_WAIT_UNTIL([ip netns exec at_ns1 ping6 -c 1 fc00::1]) AT_DATA([flows.txt], [dnl priority=100,in_port=ovs-p0,ipv6,ipv6_src=fc00::1,ipv6_dst=fc00::3,actions=set_field:fc00::2->ipv6_dst,ovs-p1 priority=100,in_port=ovs-p1,ipv6,ipv6_src=fc00::2,ipv6_dst=fc00::1,actions=set_field:fc00::3->ipv6_src,ovs-p0 priority=0,actions=NORMAL ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl We need to wait until the new flows are actually in the datapath to avoid dnl intermittent loss of first ping packet. AT_CHECK([ovs-appctl revalidator/wait]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::3 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00::3 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00::3 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over bond]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH_BOND(p1 p2, at_ns1, br0, bond0, lacp=active bond_mode=balance-tcp, "10.1.1.2/24") OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over vxlan tunnel]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_VXLAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.1.1.1/24], [id 0 dstport 4789]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - tcp over vxlan tunnel with software fallback]) AT_SKIP_IF([test $HAVE_NC = no]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_CHECK_VXLAN() dnl This test is only valid with tso. If the kernel segments the packets, the dnl packet lengths in the final test will be different. m4_ifndef([CHECK_SYSTEM_TSO], [AT_SKIP_IF(:)]) OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Test the case where one side has all checksum and TSO offload disabled. AT_CHECK([ethtool -K ovs-p0 tso off], [0], [ignore], [ignore]) AT_CHECK([ethtool -K ovs-p0 sg off], [0], [ignore], [ignore]) dnl Reinitialize. AT_CHECK([ovs-vsctl del-port ovs-p0]) AT_CHECK([ovs-vsctl add-port br-underlay ovs-p0]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.1.1.1/24], [id 0 dstport 4789]) dnl Set MTU for tunnel to generate 1500 byte packets. AT_CHECK([ip link set dev br0 mtu 1400]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check that the tunnel is up. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start tcpdump to capture the encapsulated packets. OVS_DAEMONIZE([tcpdump -i ovs-p0 -w p0.pcap], [tcpdump.pid]) dnl Wait until the pcap is written, which happens after the interface dnl is opened by tcpdump. OVS_WAIT_UNTIL([test -e p0.pcap]) dnl Initialize the listener before it is needed. NETNS_DAEMONIZE([at_ns0], [nc -l 10.1.1.1 1234 > data2], [nc.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :1234])]) dnl Large TCP transfer aimed towards ovs-p0, which has TSO disabled. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT 10.1.1.1 1234 < payload.bin]) dnl Wait until transfer completes before checking. OVS_WAIT_WHILE([kill -0 $(cat nc.pid)]) AT_CHECK([diff -q payload.bin data2], [0]) OVS_WAIT_WHILE([test $(stat -c %s p0.pcap) -le 68000 ]) dnl Stop OVS and tcpdump and verify the results. AT_CHECK([kill -15 $(cat tcpdump.pid)]) OVS_WAIT_WHILE([kill -0 $(cat tcpdump.pid)]) dnl The exact number of packets sent will vary, but we check that the largest dnl segments have the correct lengths and certain other fields. AT_CHECK([test $(ovs-pcap p0.pcap | grep -Ec dnl "^.{24}0800"dnl Ethernet "450005aa....4000..11....ac1f0164ac1f0101"dnl IP(len=1450, DF, UDP, 172.31.1.100->172.31.1.1) "....12b505960000"dnl UDP(len=1430, dport=4789) "0800000000000000"dnl VXLAN(gpid=0, vni=0) ".{24}0800"dnl Ethernet "45000578....4000..06....0a0101640a010101"dnl IP(len=1400, DF, TCP, 10.1.1.100->10.1.1.1) "....04d2............................0000"dnl TCP(dport=1234 ) -ge 20]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping vlan over vxlan tunnel]) OVS_CHECK_TUNNEL_TSO() OVS_CHECK_VXLAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.2.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.2.1.1/24], [id 0 dstport 4789]) AT_CHECK([ovs-vsctl set port br0 tag=100]) AT_CHECK([ovs-vsctl set port br-underlay tag=42]) ADD_VLAN(at_vxlan1, at_ns0, 100, "10.1.1.1/24") ADD_VLAN(p0, at_ns0, 42, "172.31.1.1/24") dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over vxlan6 tunnel]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_VXLAN_UDP6ZEROCSUM() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00::1/64", [], [], "nodad") AT_CHECK([ip addr add dev br-underlay "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([vxlan], [br0], [at_vxlan0], [fc00::1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL6([vxlan], [at_vxlan1], [at_ns0], [fc00::100], [10.1.1.1/24], [id 0 dstport 4789 udp6zerocsumtx udp6zerocsumrx]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::100]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over gre tunnel]) OVS_CHECK_GRE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([gretap], [ns_gre0], [at_ns0], [172.31.1.100], [10.1.1.1/24]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - tcp over gre tunnel with software fallback]) AT_SKIP_IF([test $HAVE_NC = no]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_CHECK_GRE() dnl This test is only valid with tso. If the kernel segments the packets, the dnl packet lengths in the final test will be different. m4_ifndef([CHECK_SYSTEM_TSO], [AT_SKIP_IF(:)]) OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Test the case where one side has all checksum and TSO offload disabled. AT_CHECK([ethtool -K ovs-p0 tso off], [0], [ignore], [ignore]) AT_CHECK([ethtool -K ovs-p0 sg off], [0], [ignore], [ignore]) dnl Reinitialize. AT_CHECK([ovs-vsctl del-port ovs-p0]) AT_CHECK([ovs-vsctl add-port br-underlay ovs-p0]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([gretap], [at_gre1], [at_ns0], [172.31.1.100], [10.1.1.1/24]) dnl Set MTU for tunnel to generate 1500 byte packets. AT_CHECK([ip link set dev br0 mtu 1400]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check that the tunnel is up. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start tcpdump to capture the encapsulated packets. OVS_DAEMONIZE([tcpdump -i ovs-p0 -w p0.pcap], [tcpdump.pid]) dnl Wait until the pcap is written, which happens after the interface dnl is opened by tcpdump. OVS_WAIT_UNTIL([test -e p0.pcap]) dnl Initialize the listener before it is needed. NETNS_DAEMONIZE([at_ns0], [nc -l 10.1.1.1 1234 > data2], [nc.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :1234])]) dnl Large TCP transfer aimed towards ovs-p0, which has TSO disabled. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT 10.1.1.1 1234 < payload.bin]) dnl Wait until transfer completes before checking. OVS_WAIT_WHILE([kill -0 $(cat nc.pid)]) AT_CHECK([diff -q payload.bin data2], [0]) OVS_WAIT_WHILE([test $(stat -c %s p0.pcap) -le 68000 ]) dnl Stop OVS and tcpdump and verify the results. AT_CHECK([kill -15 $(cat tcpdump.pid)]) OVS_WAIT_WHILE([kill -0 $(cat tcpdump.pid)]) dnl The exact number of packets sent will vary, but we check that the largest dnl segments have the correct lengths and certain other fields. AT_CHECK([test $(ovs-pcap p0.pcap | grep -Ec dnl "^.{24}0800"dnl Ethernet "4500059e....4000..2f....ac1f0164ac1f0101"dnl IP(len=1450, DF, GRE, 172.31.1.100->172.31.1.1) "00006558"dnl GRE(flags=0, proto=0x6558) ".{24}0800"dnl Ethernet "45000578....4000..06....0a0101640a010101"dnl IP(len=1400, DF, TCP, 10.1.1.100->10.1.1.1) "....04d2............................0000"dnl TCP(dport=1234 ) -ge 20]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over ip6gre L2 tunnel]) OVS_CHECK_GRE() OVS_CHECK_ERSPAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00:100::1/96", [], [], nodad) AT_CHECK([ip addr add dev br-underlay "fc00:100::100/96" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([ip6gre], [br0], [at_gre0], [fc00:100::1], [10.1.1.100/24], [options:packet_type=legacy_l2]) ADD_NATIVE_TUNNEL6([ip6gretap], [ns_gretap0], [at_ns0], [fc00:100::100], [10.1.1.1/24], [local fc00:100::1]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 2 fc00:100::100]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over erspan v1 tunnel]) OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15) OVS_CHECK_GRE() OVS_CHECK_ERSPAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=1 options:erspan_idx=7]) ADD_NATIVE_TUNNEL([erspan], [ns_erspan0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [seq key 1 erspan_ver 1 erspan 7]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes dnl NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl NS_CHECK_EXEC([at_ns0], [ping -s 1200 -i 0.3 -c 3 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over erspan v2 tunnel]) OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15) OVS_CHECK_GRE() OVS_CHECK_ERSPAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=2 options:erspan_dir=1 options:erspan_hwid=0x7]) ADD_NATIVE_TUNNEL([erspan], [ns_erspan0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [seq key 1 erspan_ver 2 erspan_dir egress erspan_hwid 7]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes dnl NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl NS_CHECK_EXEC([at_ns0], [ping -s 1200 -i 0.3 -c 3 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over ip6erspan v1 tunnel]) OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15) OVS_CHECK_GRE() OVS_CHECK_ERSPAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00:100::1/96", [], [], nodad) AT_CHECK([ip addr add dev br-underlay "fc00:100::100/96" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([ip6erspan], [br0], [at_erspan0], [fc00:100::1], [10.1.1.100/24], [options:key=123 options:erspan_ver=1 options:erspan_idx=0x7]) ADD_NATIVE_TUNNEL6([ip6erspan], [ns_erspan0], [at_ns0], [fc00:100::100], [10.1.1.1/24], [local fc00:100::1 seq key 123 erspan_ver 1 erspan 7]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 2 fc00:100::100]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over ip6erspan v2 tunnel]) OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15) OVS_CHECK_GRE() OVS_CHECK_ERSPAN() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00:100::1/96", [], [], nodad) AT_CHECK([ip addr add dev br-underlay "fc00:100::100/96" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([ip6erspan], [br0], [at_erspan0], [fc00:100::1], [10.1.1.100/24], [options:key=121 options:erspan_ver=2 options:erspan_dir=0 options:erspan_hwid=0x7]) ADD_NATIVE_TUNNEL6([ip6erspan], [ns_erspan0], [at_ns0], [fc00:100::100], [10.1.1.1/24], [local fc00:100::1 seq key 121 erspan_ver 2 erspan_dir ingress erspan_hwid 0x7]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 2 fc00:100::100]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over geneve tunnel]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over geneve tunnel, delete flow regression]) OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_DATA([flows.txt], [dnl priority=100,icmp actions=resubmit(,10) priority=0 actions=NORMAL table=10, priority=100, ip, actions=ct(table=20,zone=65520) table=20, priority=200, ip, ct_state=-new+trk, actions=resubmit(,30) table=20, priority=100, ip, ct_state=+new, actions=resubmit(,30) table=20, priority=50, ip, actions=DROP table=30, priority=100, ip, actions=ct(commit,table=40,zone=65520) table=40, actions=normal ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl ping over tunnel should work NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl del-flows br0 "ct_state=+new"]) dnl ping should not go through after removal of the flow NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl 7 packets transmitted, 0 received, 100% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP(["/|ERR|/d /|WARN|/d"]) AT_CLEANUP AT_SETUP([datapath - flow resume with geneve tun_metadata]) OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl monitor br0 resume --detach --no-chdir --pidfile 2> /dev/null]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0]) dnl Set up flows AT_DATA([flows.txt], [dnl table=0, arp action=NORMAL table=0, in_port=LOCAL icmp action=output:at_gnv0 table=0, in_port=at_gnv0 icmp action=set_field:0xa->tun_metadata0,resubmit(,1) table=1, icmp action=controller(pause), resubmit(,2) table=2, tun_metadata0=0xa, icmp action=output:LOCAL ]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Test OVS handles TLV map modifictions properly when restores frozen state. NETNS_DAEMONIZE([at_ns0], [ping 10.1.1.100 > /dev/null], [ping0.pid]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0x88,len=4}->tun_metadata1"]) sleep 1 AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0x99,len=4}->tun_metadata2"]) sleep 1 AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0xaa,len=4}->tun_metadata3"]) sleep 1 OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over geneve6 tunnel]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_GENEVE_UDP6ZEROCSUM() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00::1/64", [], [], "nodad") AT_CHECK([ip addr add dev br-underlay "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([geneve], [br0], [at_gnv0], [fc00::1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL6([geneve], [ns_gnv0], [at_ns0], [fc00::100], [10.1.1.1/24], [vni 0 udp6zerocsumtx udp6zerocsumrx]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::100]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start ncat listeners. OVS_DAEMONIZE([nc -l 10.1.1.100 1234 > tcp_data], [nc.pid]) NETNS_DAEMONIZE([at_ns0], [nc -l -u 10.1.1.1 4321 > udp_data], [nc2.pid]) dnl Verify that ncat is ready. OVS_WAIT_UNTIL([netstat -ln | grep :1234]) OVS_WAIT_UNTIL([NS_EXEC([at_ns0], [netstat -ln | grep :4321])]) dnl Check large bidirectional TCP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.100 1234 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin tcp_data]) dnl Check UDP. AT_CHECK([dd if=/dev/urandom of=payload.bin bs=600 count=1 2> /dev/null]) AT_CHECK([nc $NC_EOF_OPT -u 10.1.1.1 4321 < payload.bin]) OVS_WAIT_UNTIL([diff -q payload.bin udp_data]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - slow_action on geneve6 tunnel]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_CHECK_GENEVE_UDP6ZEROCSUM() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00::1/64", [], [], "nodad") AT_CHECK([ip addr add dev br-underlay "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([geneve], [br0], [at_gnv0], [fc00::1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL6([geneve], [ns_gnv0], [at_ns0], [fc00::100], [10.1.1.1/24], [vni 0 udp6zerocsumtx udp6zerocsumrx]) AT_CHECK([ovs-ofctl add-flow br0 "table=37,actions=at_gnv0"]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::100]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Start tcpdump to capture the encapsulated packets. NETNS_DAEMONIZE([at_ns0], [tcpdump -n -U -i p0 -w p0.pcap], [tcpdump.pid]) sleep 1 dnl Generate a single packet trough the controler that needs an ARP modification AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=fffffffffffffa163e949d8008060001080006040001fa163e949d80c0a820300000000000000a0000fe actions=set_field:0xa0000f4->reg1,move:NXM_NX_XXREG0[[64..95]]->NXM_OF_ARP_SPA[[]],resubmit(,37)"]) sleep 1 dnl Stop OVS and tcpdump and verify the results. OVS_TRAFFIC_VSWITCHD_STOP ovs-pcap p0.pcap AT_CHECK([ovs-pcap p0.pcap | grep -Eq "^[[[:xdigit:]]]{24}86dd60000000003a1140fc000000000000000000000000000100fc000000000000000000000000000001[[[:xdigit:]]]{4}17c1003a[[[:xdigit:]]]{4}0000655800000000fffffffffffffa163e949d8008060001080006040001[[[:xdigit:]]]{12}0a0000f40000000000000a0000fe$"]) AT_CLEANUP AT_SETUP([datapath - bridging two geneve tunnels]) OVS_CHECK_TUNNEL_TSO() OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay-0]) ADD_BR([br-underlay-1]) ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) dnl Set up underlay link from host into the namespaces using veth pairs. ADD_VETH(p0, at_ns0, br-underlay-0, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay-0 "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay-0 up]) ADD_VETH(p1, at_ns1, br-underlay-1, "172.31.2.1/24") AT_CHECK([ip addr add dev br-underlay-1 "172.31.2.100/24"]) AT_CHECK([ip link set dev br-underlay-1 up]) dnl Set up two OVS tunnel endpoints in a root namespace and two native dnl linux devices inside the test namespaces. dnl dnl ns_gnv0 | ns_gnv1 dnl ip: 10.1.1.1/24 | ip: 10.1.1.2/24 dnl remote_ip: 172.31.1.100 | remote_ip: 172.31.2.100 dnl | | | dnl | | | dnl p0 | p1 dnl ip: 172.31.1.1/24 | ip: 172.31.2.1/24 dnl | NS0 | NS1 | dnl ---------|------------------------+------------------|-------------------- dnl | | dnl br-underlay-0: br-underlay-1: dnl ip: 172.31.1.100/24 ip: 172.31.2.100/24 dnl ovs-p0 ovs-p1 dnl | | dnl | br0 | dnl encap/decap --- ip: 10.1.1.100/24 --------- encap/decap dnl at_gnv0 dnl remote_ip: 172.31.1.1 dnl at_gnv1 dnl remote_ip: 172.31.2.1 dnl ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0]) ADD_OVS_TUNNEL([geneve], [br0], [at_gnv1], [172.31.2.1], [10.1.1.101/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv1], [at_ns1], [172.31.2.100], [10.1.1.2/24], [vni 0]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay-0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay-1 "actions=normal"]) dnl First, check both underlays. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 172.31.2.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Now, check the overlay with different packet sizes. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - bridging IPv4 and IPv6 flow based geneve tunnels]) OVS_CHECK_TUNNEL_TSO() OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay-0]) ADD_BR([br-underlay-1]) ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) dnl Set up underlay link from host into the namespaces using veth pairs. ADD_VETH(p0, at_ns0, br-underlay-0, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay-0 "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay-0 up]) ADD_VETH(p1, at_ns1, br-underlay-1, "fc00::1/64", [], [], "nodad") AT_CHECK([ip addr add dev br-underlay-1 "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay-1 up]) dnl Set up a single flow-based OVS tunnel endpoint in a root namespace and dnl two native linux devices inside the test namespaces. dnl dnl ns_gnv0 | ns_gnv1 dnl ip: 10.1.1.1/24 | ip: 10.1.1.2/24 dnl remote_ip: 172.31.1.100 | remote_ip: fc00::100 dnl vni: 4 | vni: 6 dnl | | | dnl | | | dnl p0 | p1 dnl ip: 172.31.1.1/24 | ip: fc00::1/64 dnl | NS0 | NS1 | dnl ---------|------------------------+------------------|-------------------- dnl | | dnl br-underlay-0: br-underlay-1: dnl ip: 172.31.1.100/24 ip: fc00::100/64 dnl ovs-p0 ovs-p1 dnl | | dnl | br0 | dnl encap/decap --- ip: 10.1.1.100/24 --------- encap/decap dnl at_gnv0 dnl remote_ip: flow dnl local_ip: flow dnl key: flow dnl dnl Using v6 macro for OVS tunnel to account for maximum overhead. ADD_OVS_TUNNEL6([geneve], [br0], [at_gnv0], [flow], [10.1.1.100/24], [options:local_ip=flow options:key=flow]) dnl Setup Linux native tunnels inside namespaces. Again, using v6 macro for dnl the proper MTU configuration. ADD_NATIVE_TUNNEL6([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 4]) ADD_NATIVE_TUNNEL6([geneve], [ns_gnv1], [at_ns1], [fc00::100], [10.1.1.2/24], [vni 6 udp6zerocsumtx udp6zerocsumrx]) AT_DATA([flows.txt], [dnl in_port=at_gnv0,tun_id=4,actions=load:0->tun_src,load:0->tun_dst,set_field:fc00::100->tun_ipv6_src,set_field:fc00::1->tun_ipv6_dst,set_field:6->tun_id,output:in_port in_port=at_gnv0,tun_id=6,actions=load:0->tun_ipv6_src,load:0->tun_ipv6_dst,set_field:172.31.1.100->tun_src,set_field:172.31.1.1->tun_dst,set_field:4->tun_id,output:in_port ]) AT_CHECK([ovs-ofctl -OOpenFlow15 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flow br-underlay-0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay-1 "actions=normal"]) dnl It many take longer for IPv6 address to become available. OVS_WAIT_UNTIL([ip netns exec at_ns1 ping6 -c 2 fc00::100]) dnl First, check both underlays. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Now, check the overlay with different packet sizes. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl There should be 6 tunnel flows in the datapath - 3 in v4 to v6 direction dnl for different fragment types (no, first, later) and 3 from v6 to v4. dnl Only count flows that are used in the datapath to make sure they are dnl actually matching the traffic. AT_CHECK([ovs-appctl dpctl/dump-flows | grep -v 'packets:0' | dnl grep "eth_type(0x0800)" | grep -c tun_id], [0], [dnl 6 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - handling of geneve corrupted metadata]) OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START( [_ADD_BR([br-underlay]) -- \ set bridge br0 other-config:hwaddr=f2:ff:00:00:00:01 -- \ set bridge br-underlay other-config:hwaddr=f2:ff:00:00:00:02]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", f2:ff:00:00:00:03) AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0], [address f2:ff:00:00:00:04]) NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 03 08 00 45 00 00 52 00 01 00 00 40 11 1f f7 ac 1f 01 01 ac 1f 01 64 de c1 17 c1 00 3e 59 e9 01 00 65 58 00 00 00 00 00 03 00 02 f2 ff 00 00 00 01 f2 ff 00 00 00 04 08 00 45 00 00 1c 00 01 00 00 40 01 64 7a 0a 01 01 01 0a 01 01 64 08 00 f7 ff 00 00 00 00 > /dev/null]) OVS_WAIT_UNTIL([grep -q 'Invalid Geneve tunnel metadata' ovs-vswitchd.log]) OVS_TRAFFIC_VSWITCHD_STOP(["/Invalid Geneve tunnel metadata on bridge br0 while processing icmp,in_port=1,vlan_tci=0x0000,dl_src=f2:ff:00:00:00:04,dl_dst=f2:ff:00:00:00:01,nw_src=10.1.1.1,nw_dst=10.1.1.100,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=8,icmp_code=0/d /Unable to parse geneve options/d"]) AT_CLEANUP AT_SETUP([datapath - ping over gre tunnel by simulated packets]) OVS_CHECK_XT() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl -- set bridge br0 other-config:hwaddr=\"f2:ff:00:00:00:01\"]) ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f2:ff:00:00:00:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", f2:ff:00:00:00:03) AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) XT_ACCEPT([br-underlay]) NETNS_DAEMONIZE([at_ns0], [tcpdump -n -i p0 dst host 172.31.1.1 -l > p0.pcap 2>/dev/null], [tcpdump.pid]) sleep 1 dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl We don't actually add gretap port as below, instead, we will dnl emulate one that sends packets. Suppose its mac address is f2:ff:00:00:00:04. dnl ADD_NATIVE_TUNNEL([gretap], [ns_gre0], [at_ns0], [172.31.1.100], [10.1.1.1/24]) dnl Now, check the overlay by sending out raw arp and icmp packets. ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff00000003080045000042ec2c4000402ff3bcac1f0101ac1f016400006558fffffffffffff2ff0000000408060001080006040001f2ff000000040a0101010000000000000a010164 actions=NORMAL" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0, length 46: ARP, Reply 10.1.1.100 is-at f2:ff:00:00:00:01.* length 28" 2>&1 1>/dev/null]) ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000308004500007aec8e4000402ff322ac1f0101ac1f016400006558f2ff00000001f2ff00000004080045000054548f40004001cfb30a0101010a0101640800e6e829270003e1a3435b00000000ff1a050000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 actions=NORMAL" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0, length 102: IP 10.1.1.100 > 10.1.1.1: ICMP echo reply,.* length 64$" 2>&1 1>/dev/null]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over erspan v1 tunnel by simulated packets]) OVS_CHECK_XT() OVS_CHECK_MIN_KERNEL(3, 10) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl -- set bridge br0 other-config:hwaddr=\"f2:ff:00:00:00:01\"]) ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f2:ff:00:00:00:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", f2:ff:00:00:00:03) AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and emulate a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=1 options:erspan_idx=7]) XT_ACCEPT([br-underlay]) NETNS_DAEMONIZE([at_ns0], [tcpdump -n -x -i p0 dst host 172.31.1.1 -l > p0.pcap 2>/dev/null], [tcpdump.pid]) sleep 1 dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now send out an arp request from 10.1.1.1 for 10.1.1.100 in erspan. ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000308004500004e151d4000402fcac0ac1f0101ac1f0164100088be000000061000000100000007fffffffffffff2ff0000000408060001080006040001f2ff000000040a0101010000000000000a010164 actions=normal" dnl 0002 is arp reply, followed by mac address of 10.1.1.100. OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0030: 0806 0001 0800 0604 0002 f2ff 0000 0001" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0040: 0a01 0164 f2ff 0000 0004 0a01 0101" 2>&1 1>/dev/null]) dnl Okay, now check the overlay with raw icmp packets. AT_FAIL_IF([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0,.* length 122" 2>&1 1>/dev/null]) ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000308004500008e70cb4000402f6ed2ac1f0101ac1f0164100088be000000051000000100000007f2ff00000001f2ff0000000408004500005c4a3340004001da070a0101010a010164080084f238fb0001f36a6b5b0000000021870e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0,.* length 122" 2>&1 1>/dev/null]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over erspan v2 tunnel by simulated packets]) OVS_CHECK_XT() OVS_CHECK_MIN_KERNEL(3, 10) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl -- set bridge br0 other-config:hwaddr=\"f2:ff:00:00:00:01\"]) ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f2:ff:00:00:00:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", f2:ff:00:00:00:03) AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and simulate a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=2 options:erspan_dir=1 options:erspan_hwid=0x7]) XT_ACCEPT([br-underlay]) NETNS_DAEMONIZE([at_ns0], [tcpdump -n -x -i p0 dst host 172.31.1.1 -l > p0.pcap 2>/dev/null], [tcpdump.pid]) sleep 1 dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, send raw arp request and icmp echo request. ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff00000003080045000052373d4000402fa89cac1f0101ac1f0164100088be00000006200000016f54b41700008078fffffffffffff2ff0000000408060001080006040001f2ff000000040a0101010000000000000a010164 actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0030: 0000 0001 0806 0001 0800 0604 0002 f2ff" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0040: 0000 0001 0a01 0164 f2ff 0000 0004 0a01" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0050: 0101" 2>&1 1>/dev/null]) dnl Because tcpdump might not be able to parse erspan headers, we check icmp echo reply dnl by packet length. AT_FAIL_IF([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0,.* length 126" 2>&1 1>/dev/null]) ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000308004500009287e14000402f57b8ac1f0101ac1f0164100088be0000000520000001144cd5a400008078f2ff00000001f2ff0000000408004500005c38d640004001eb640a0101010a01016408005e57585f0001df6c6b5b0000000045bc050000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP 172.31.1.100 > 172.31.1.1: GREv0,.* length 126" 2>&1 1>/dev/null]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over ip6erspan v1 tunnel by simulated packets]) OVS_CHECK_XT() OVS_CHECK_MIN_KERNEL(3, 10) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl -- set bridge br0 other-config:hwaddr=\"f2:ff:00:00:00:01\"]) ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f2:ff:00:00:00:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00:100::1/96", f2:ff:00:00:00:03, [], nodad) AT_CHECK([ip addr add dev br-underlay "fc00:100::100/96" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and simulate a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([ip6erspan], [br0], [at_erspan0], [fc00:100::1], [10.1.1.100/24], [options:key=123 options:erspan_ver=1 options:erspan_idx=0x7]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 2 fc00:100::100]) XT6_ACCEPT([br-underlay]) NETNS_DAEMONIZE([at_ns0], [tcpdump -n -x -i p0 dst host fc00:100::1 -l > p0.pcap 2>/dev/null], [tcpdump.pid]) sleep 1 dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now send raw arp request and icmp echo request. ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000386dd60008531003a2f40fc000100000000000000000000000001fc000100000000000000000000000100100088be000000051000007b00000007fffffffffffff2ff0000000408060001080006040001f2ff000000040a0101010000000000000a010164 actions=normal" dnl Check arp reply. OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0040: 0000 0001 0806 0001 0800 0604 0002 f2ff" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0050: 0000 0001 0a01 0164 f2ff 0000 0004 0a01" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0060: 0101" 2>&1 1>/dev/null]) AT_FAIL_IF([cat p0.pcap | grep -E "IP6 fc00:100::100 > fc00:100::1: GREv0,.* length 114" 2>&1 1>/dev/null]) ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000386dd60008531007a3c40fc000100000000000000000000000001fc0001000000000000000000000001002f00040104010100100088be000000061000407b00000007f2ff00000001f2ff0000000408004500005429b640004001fa8c0a0101010a01016408005c2c7526000118d3685b00000000e4aa020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP6 fc00:100::100 > fc00:100::1: GREv0,.* length 114" 2>&1 1>/dev/null]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over ip6erspan v2 tunnel by simulated packets]) OVS_CHECK_XT() OVS_CHECK_MIN_KERNEL(3, 10) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-vsctl -- set bridge br0 other-config:hwaddr=\"f2:ff:00:00:00:01\"]) ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f2:ff:00:00:00:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "fc00:100::1/96", f2:ff:00:00:00:03, [], nodad) AT_CHECK([ip addr add dev br-underlay "fc00:100::100/96" nodad]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and simulate a native dnl linux device inside the namespace. ADD_OVS_TUNNEL6([ip6erspan], [br0], [at_erspan0], [fc00:100::1], [10.1.1.100/24], [options:key=121 options:erspan_ver=2 options:erspan_dir=0 options:erspan_hwid=0x7]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 2 fc00:100::100]) XT6_ACCEPT([br-underlay]) NETNS_DAEMONIZE([at_ns0], [tcpdump -n -x -i p0 dst host fc00:100::1 -l > p0.pcap 2>/dev/null], [tcpdump.pid]) sleep 1 dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now send raw arp request and icmp echo request. ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000386dd60008531003e2f40fc000100000000000000000000000001fc000100000000000000000000000100100088be0000000620000079af514f9900008070fffffffffffff2ff0000000408060001080006040001f2ff000000040a0101010000000000000a010164 actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0040: 0004 f2ff 0000 0001 0806 0001 0800 0604" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0050: 0002 f2ff 0000 0001 0a01 0164 f2ff 0000" 2>&1 1>/dev/null]) OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0060: 0004 0a01 0101" 2>&1 1>/dev/null]) AT_FAIL_IF([cat p0.pcap | grep -E "IP6 fc00:100::100 > fc00:100::1: GREv0, .* length 118" 2>&1 1>/dev/null]) ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=f2ff00000002f2ff0000000386dd60008531007e3c40fc000100000000000000000000000001fc0001000000000000000000000001002f00040104010100100088be0000000720004079af514f9b00008070f2ff00000001f2ff00000004080045000054ffcb4000400124770a0101010a0101640800419e23ac000112d7685b000000004caf0c0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 actions=normal" OVS_WAIT_UNTIL([cat p0.pcap | grep -E "IP6 fc00:100::100 > fc00:100::1: GREv0, .* length 118" 2>&1 1>/dev/null]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping over srv6 tunnel]) OVS_CHECK_SRV6() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.default.seg6_enabled=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv4.conf.default.forwarding=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.default.forwarding=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.seg6_enabled=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv4.conf.all.forwarding=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.forwarding=1]) dnl Set up underlay link from host into the namespace 'at_ns0' dnl using veth pair. Kernel side tunnel endpoint (SID) is dnl 'fc00:a::1/128', so add it to the route. dnl Only IPPROTO_IPIP(4) and IPPROTO_ICMPV6(58) are needed in underlay link. ADD_BR([br-underlay]) ADD_VETH(p0, at_ns0, br-underlay, "fc00::1/64", [], [], "nodad") AT_CHECK([ovs-ofctl add-flow br-underlay "priority=1,actions=drop"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ipv6,nw_proto=4,actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ipv6,nw_proto=58,actions=normal"]) AT_CHECK([ip addr add dev br-underlay "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay up]) AT_CHECK([ip route add fc00:a::1/128 dev br-underlay via fc00::1]) dnl Set up tunnel endpoints on OVS outside the namespace. ADD_OVS_TUNNEL6([srv6], [br0], [at_srv6], [fc00:a::1], [10.100.100.100/24]) AT_CHECK([ovs-vsctl set bridge br0 other_config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ip route add 10.1.1.0/24 dev br0 via 10.100.100.1]) AT_CHECK([arp -s 10.100.100.1 aa:55:aa:55:00:01]) AT_CHECK([ovs-ofctl add-flow br0 in_port=LOCAL,actions=output:at_srv6]) AT_CHECK([ovs-ofctl add-flow br0 in_port=at_srv6,actions=mod_dl_dst:aa:55:aa:55:00:00,output:LOCAL]) dnl Set up tunnel endpoints on the namespace 'at_ns0', dnl and overlay port on the namespace 'at_ns1' ADD_VETH_NS([at_ns0], [ovs-veth0], [10.1.1.2/24], [at_ns1], [ovs-veth1], [10.1.1.1/24]) NS_CHECK_EXEC([at_ns0], [ip sr tunsrc set fc00:a::1]) NS_CHECK_EXEC([at_ns0], [ip route add 10.100.100.0/24 encap seg6 mode encap segs fc00::100 dev p0]) NS_CHECK_EXEC([at_ns0], [ip -6 route add fc00:a::1 encap seg6local action End.DX4 nh4 0.0.0.0 dev ovs-veth0]) NS_CHECK_EXEC([at_ns1], [ip route add 10.100.100.0/24 via 10.1.1.2 dev ovs-veth1]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::100]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay. NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.100.100.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - ping6 over srv6 tunnel]) OVS_CHECK_SRV6() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.default.seg6_enabled=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.default.forwarding=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.seg6_enabled=1]) NS_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.forwarding=1]) dnl Set up underlay link from host into the namespace 'at_ns0' dnl using veth pair. Kernel side tunnel endpoint (SID) is dnl 'fc00:a::1/128', so add it to the route. dnl Only IPPROTO_IPV6(41) and IPPROTO_ICMPV6(58) are needed in underlay link. ADD_BR([br-underlay]) ADD_VETH(p0, at_ns0, br-underlay, "fc00::1/64", [], [], "nodad") AT_CHECK([ovs-ofctl add-flow br-underlay "priority=1,actions=drop"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ipv6,nw_proto=41,actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ipv6,nw_proto=58,actions=normal"]) AT_CHECK([ip addr add dev br-underlay "fc00::100/64" nodad]) AT_CHECK([ip link set dev br-underlay up]) AT_CHECK([ip -6 route add fc00:a::1/128 dev br-underlay via fc00::1]) dnl Set up tunnel endpoints on OVS outside the namespace. ADD_OVS_TUNNEL6([srv6], [br0], [at_srv6], [fc00:a::1], [fc00:100::100/64]) AT_CHECK([ovs-vsctl set bridge br0 other_config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ip addr add dev br0 fc00:100::100/64]) AT_CHECK([ip -6 route add fc00:1::1/128 dev br0 via fc00:100::1]) AT_CHECK([ip -6 neigh add fc00:100::1 lladdr aa:55:aa:55:00:01 dev br0]) AT_CHECK([ovs-ofctl add-flow br0 in_port=LOCAL,actions=output:at_srv6]) AT_CHECK([ovs-ofctl add-flow br0 in_port=at_srv6,actions=mod_dl_dst:aa:55:aa:55:00:00,output:LOCAL]) dnl Set up tunnel endpoints on the namespace 'at_ns0', dnl and overlay port on the namespace 'at_ns1' ADD_VETH_NS([at_ns0], [ovs-veth0], [fc00:1::2/64], [at_ns1], [ovs-veth1], [fc00:1::1/64]) NS_CHECK_EXEC([at_ns0], [ip sr tunsrc set fc00:a::1]) NS_CHECK_EXEC([at_ns0], [ip -6 route add fc00:100::0/64 encap seg6 mode encap segs fc00::100 dev p0]) NS_CHECK_EXEC([at_ns0], [ip -6 route add fc00:a::1 encap seg6local action End.DX6 nh6 :: dev ovs-veth0]) NS_CHECK_EXEC([at_ns1], [ip -6 route add fc00:100::/64 via fc00:1::2 dev ovs-veth1]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::100]) OVS_WAIT_UNTIL([ip netns exec at_ns1 ping6 -c 1 fc00:100::100]) dnl First, check the underlay. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay. NS_CHECK_EXEC([at_ns1], [ping6 -q -c 3 -i 0.3 -W 2 fc00:100::100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - clone action]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl -- set interface ovs-p0 ofport_request=1 \ -- set interface ovs-p1 ofport_request=2]) AT_DATA([flows.txt], [dnl priority=1 actions=NORMAL priority=10 in_port=1,ip,actions=clone(mod_dl_dst(50:54:00:00:00:0a),set_field:192.168.3.3->ip_dst), output:2 priority=10 in_port=2,ip,actions=clone(mod_dl_src(ae:c6:7e:54:8d:4d),mod_dl_dst(50:54:00:00:00:0b),set_field:192.168.4.4->ip_dst, controller), output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log | STRIP_MONITOR_CSUM], [0], [dnl icmp,vlan_tci=0x0000,dl_src=ae:c6:7e:54:8d:4d,dl_dst=50:54:00:00:00:0b,nw_src=10.1.1.2,nw_dst=192.168.4.4,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=0,icmp_code=0 icmp_csum: icmp,vlan_tci=0x0000,dl_src=ae:c6:7e:54:8d:4d,dl_dst=50:54:00:00:00:0b,nw_src=10.1.1.2,nw_dst=192.168.4.4,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=0,icmp_code=0 icmp_csum: icmp,vlan_tci=0x0000,dl_src=ae:c6:7e:54:8d:4d,dl_dst=50:54:00:00:00:0b,nw_src=10.1.1.2,nw_dst=192.168.4.4,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=0,icmp_code=0 icmp_csum: ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - mpls actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0]) AT_CHECK([ovs-vsctl add-port br1 patch1]) AT_DATA([flows.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_mpls_label:4,resubmit(,1) table=0,priority=100,dl_type=0x8847,mpls_label=4 actions=pop_mpls:0x0800,resubmit(,1) table=0,priority=10 actions=resubmit(,1) table=1,priority=10 actions=normal ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flows br1 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - multiple mpls label pop]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0]) AT_CHECK([ovs-vsctl add-port br1 patch1]) AT_DATA([flows.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_mpls_label:4,push_mpls:0x8847,set_mpls_label:2,push_mpls:0x8847,set_mpls_label:1,resubmit(,3) table=0,priority=100,dl_type=0x8847,mpls_label=1 actions=pop_mpls:0x8847,resubmit(,1) table=1,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x8847,resubmit(,2) table=2,priority=100,dl_type=0x8847,mpls_label=4 actions=pop_mpls:0x0800,resubmit(,3) table=0,priority=10 actions=resubmit(,3) table=3,priority=10 actions=normal ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl add-flows br1 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - encap decap mpls actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=ovs-p0 actions=encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,in_port=ovs-p1 actions=encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - encap decap mpls_mc actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=ovs-p0 actions=encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8848,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p0 ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,in_port=ovs-p1 actions=encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8848,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1 ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - multiple encap decap mpls actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=ovs-p0 actions=encap(mpls),set_mpls_label:3, encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p0 ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,in_port=ovs-p1 actions=encap(mpls),set_mpls_label:3, encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8847)),decap(packet_type(ns=0,type=0)),ovs-p1 ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - multiple encap decap mpls_mc actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=ovs-p0 actions=encap(mpls_mc),set_mpls_label:3, encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8848,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8848)),decap(packet_type(ns=0,type=0)),ovs-p0 ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,in_port=ovs-p1 actions=encap(mpls_mc),set_mpls_label:3, encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,output:100 table=0,priority=100,in_port=100,dl_type=0x8848,mpls_label=2 actions=decap(),decap(packet_type(ns=1,type=0x8848)),decap(packet_type(ns=0,type=0)),ovs-p1 ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - encap mpls pop mpls actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=decap,encap(mpls),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,output:100 table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,resubmit(,3) table=0,priority=10 actions=resubmit(,3) table=3,priority=10 actions=normal ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=decap,encap(mpls),set_mpls_label:2,encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,output:100 table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=pop_mpls:0x0800,resubmit(,3) table=0,priority=10 actions=resubmit(,3) table=3,priority=10 actions=normal ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - push mpls decap mpls actions]) OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24", 36:b1:ee:7c:01:02) AT_CHECK([ip link add patch0 type veth peer name patch1]) on_exit 'ip link del patch0' AT_CHECK([ip link set dev patch0 up]) AT_CHECK([ip link set dev patch1 up]) AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) AT_DATA([flows.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:03,mod_dl_src:36:b1:ee:7c:01:02,resubmit(,3) table=0,priority=10 actions=resubmit(,3) table=3,priority=10 actions=normal ]) AT_DATA([flows1.txt], [dnl table=0,priority=100,dl_type=0x0800 actions=push_mpls:0x8847,set_field:2->mpls_label,output:100 table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap,decap(packet_type(ns=1,type=0x0800)),encap(ethernet),mod_dl_dst:36:b1:ee:7c:01:02,mod_dl_src:36:b1:ee:7c:01:03,resubmit(,3) table=0,priority=10 actions=resubmit(,3) table=3,priority=10 actions=normal ]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows1.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - basic truncate action]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl del-flows br0]) dnl Create p0 and ovs-p0(1) ADD_NAMESPACES(at_ns0) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) dnl Create p1(3) and ovs-p1(2), packets received from ovs-p1 will appear in p1 AT_CHECK([ip link add p1 type veth peer name ovs-p1]) on_exit 'ip link del ovs-p1' AT_CHECK([ip link set dev ovs-p1 up]) AT_CHECK([ip link set dev p1 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p1 -- set interface ovs-p1 ofport_request=2]) dnl Use p1 to check the truncated packet AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 ofport_request=3]) dnl Create p2(5) and ovs-p2(4) AT_CHECK([ip link add p2 type veth peer name ovs-p2]) on_exit 'ip link del ovs-p2' AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev p2 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=4]) dnl Use p2 to check the truncated packet AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=5]) dnl basic test AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output(port=2,max_len=100),output:4 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl use this file as payload file for ncat AT_CHECK([dd if=/dev/urandom of=payload200.bin bs=200 count=1 2> /dev/null]) on_exit 'rm -f payload200.bin' NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u 10.1.1.2 1234 < payload200.bin]) dnl packet with truncated size AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=100 ]) dnl packet with original size AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=242 ]) dnl more complicated output actions AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output(port=2,max_len=100),output:4,output(port=2,max_len=100),output(port=4,max_len=100),output:2,output(port=4,max_len=200),output(port=2,max_len=65535) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u 10.1.1.2 1234 < payload200.bin]) dnl 100 + 100 + 242 + min(65535,242) = 684 AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=684 ]) dnl 242 + 100 + min(242,200) = 542 AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=542 ]) dnl SLOW_ACTION: disable kernel datapath truncate support dnl Repeat the test above, but exercise the SLOW_ACTION code path AT_CHECK([ovs-appctl dpif/set-dp-features br0 trunc false], [0]) dnl SLOW_ACTION test1: check datapatch actions AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x800,dl_src=e6:66:c1:11:11:11,dl_dst=e6:66:c1:22:22:22,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,tp_src=8,tp_dst=9"], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [Datapath actions: trunc(100),3,5,trunc(100),3,trunc(100),5,3,trunc(200),5,trunc(65535),3 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) dnl SLOW_ACTION test2: check actual packet truncate AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u 10.1.1.2 1234 < payload200.bin]) dnl 100 + 100 + 242 + min(65535,242) = 684 AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=684 ]) dnl 242 + 100 + min(242,200) = 542 AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=542 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Create 2 bridges and 2 namespaces to test truncate over dnl GRE tunnel: dnl br0: overlay bridge dnl ns1: connect to br0, with IP:10.1.1.2 dnl br-underlay: with IP: 172.31.1.100 dnl ns0: connect to br-underlay, with IP: 10.1.1.1 AT_SETUP([datapath - truncate and output to gre tunnel by simulated packets]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_NO_TC_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"02:90:8c:a8:a1:49\"]) ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", fa:ad:fa:25:05:60) AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) dnl The below native tunnel isn't actually added. We simulate it to send dnl and receive packets. dnl ADD_NATIVE_TUNNEL([gretap], [ns_gre0], [at_ns0], [172.31.1.100], [10.1.1.1/24], dnl [], [address e6:66:c1:11:11:11]) dnl AT_CHECK([ovs-vsctl -- set interface at_gre0 ofport_request=1]) dnl NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) dnl Set up (p1 and ovs-p1) at br0 ADD_VETH(p1, at_ns1, br0, '10.1.1.2/24') AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=2]) NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Set up (p2 and ovs-p2) as loopback for verifying packet size AT_CHECK([ip link add p2 type veth peer name ovs-p2]) on_exit 'ip link del ovs-p2' AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev p2 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=3]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=4]) dnl use this file as payload file for ncat AT_CHECK([dd if=/dev/urandom of=payload200.bin bs=200 count=1 2> /dev/null]) on_exit 'rm -f payload200.bin' AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl priority=99,in_port=1,actions=output(port=2,max_len=100),output(port=3,max_len=100) priority=99,in_port=2,udp,actions=output(port=1,max_len=100) priority=1,in_port=4,ip,actions=drop priority=1,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl del-flows br-underlay]) AT_DATA([flows-underlay.txt], [dnl priority=99,dl_type=0x0800,nw_proto=47,in_port=1,actions=LOCAL priority=99,dl_type=0x0800,nw_proto=47,in_port=LOCAL,ip_dst=172.31.1.1/24,actions=1 priority=1,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br-underlay flows-underlay.txt]) dnl check tunnel push path, from at_ns1 to at_ns0 NS_CHECK_EXEC([at_ns1], [nc $NC_EOF_OPT -u 10.1.1.1 1234 < payload200.bin]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Before truncation = ETH(14) + IP(20) + UDP(8) + 200 = 242B AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=2" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=242 ]) dnl After truncation = outer ETH(14) + outer IP(20) + GRE(4) + 100 = 138B OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows br-underlay | grep "in_port=LOCAL" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [dnl n_bytes=138]) dnl check tunnel pop path, from at_ns0 to at_ns1 dnl This 200-byte packet is simulated on behalf of ns_gre0 ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=02908ca8a149faadfa25056008004500010a9e9d4000402f4084ac1f0101ac1f016400006558e666c1222222e666c11111110800450000e46f8e40004011b4760a0101010a010102e026162e00d016e6a366ebf904c74132c6fed42a9e9e46240b4d9fd13c9b47d9704a388e70a5e77db16934a6188dc01d86aa20007ace2cf9cdb111f208474b88ffc851c871f0e3fb4fff138c1d288d437efff487e2b86a9c99fbf4229a6485e133bcf3e16f6e345207fda0932d9eeb602740456fd077b4847d25481337bd716155cc245be129ccc11bf82b834767b3760b52fe913c0e24f31c0e1b27f88acf7bba6b985fb64ee2cd6fc6bba1a9c1f021e253e1728b046fd4d023307e3296361a37ea2617ebcb2537e0284a81050dd0ee actions=LOCAL" dnl After truncation = 100 byte at loopback device p2(4) AT_CHECK([ovs-appctl revalidator/purge], [0]) OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows br0 | grep "in_port=4" | ofctl_strip], [dnl n_packets=1, n_bytes=100, priority=1,ip,in_port=4 actions=drop]) dnl SLOW_ACTION: disable datapath truncate support dnl Repeat the test above, but exercise the SLOW_ACTION code path AT_CHECK([ovs-appctl dpif/set-dp-features br0 trunc false], [0]) dnl SLOW_ACTION test1: check datapatch actions AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl SLOW_ACTION test2: check actual packet truncate AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl del-flows br-underlay]) AT_CHECK([ovs-ofctl add-flows br-underlay flows-underlay.txt]) dnl check tunnel push path, from at_ns1 to at_ns0 NS_CHECK_EXEC([at_ns1], [nc $NC_EOF_OPT -u 10.1.1.1 1234 < payload200.bin]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Before truncation = ETH(14) + IP(20) + UDP(8) + 200 = 242B AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=2" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=242 ]) dnl After truncation = outer ETH(14) + outer IP(20) + GRE(4) + 100 = 138B OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows br-underlay | grep "in_port=LOCAL" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [dnl n_bytes=138]) dnl check tunnel pop path, from at_ns0 to at_ns1 dnl This 200-byte packet is simulated on behalf of ns_gre0 ovs-ofctl -O OpenFlow13 packet-out br-underlay "in_port=1 packet=02908ca8a149faadfa25056008004500010a9e9d4000402f4084ac1f0101ac1f016400006558e666c1222222e666c11111110800450000e46f8e40004011b4760a0101010a010102e026162e00d016e6a366ebf904c74132c6fed42a9e9e46240b4d9fd13c9b47d9704a388e70a5e77db16934a6188dc01d86aa20007ace2cf9cdb111f208474b88ffc851c871f0e3fb4fff138c1d288d437efff487e2b86a9c99fbf4229a6485e133bcf3e16f6e345207fda0932d9eeb602740456fd077b4847d25481337bd716155cc245be129ccc11bf82b834767b3760b52fe913c0e24f31c0e1b27f88acf7bba6b985fb64ee2cd6fc6bba1a9c1f021e253e1728b046fd4d023307e3296361a37ea2617ebcb2537e0284a81050dd0ee actions=LOCAL" dnl After truncation = 100 byte at loopback device p2(4) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=4" | ofctl_strip], [0], [dnl n_packets=1, n_bytes=100, priority=1,ip,in_port=4 actions=drop ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Create 2 bridges and 2 namespaces to test truncate over dnl GRE tunnel: dnl br0: overlay bridge dnl ns1: connect to br0, with IP:10.1.1.2 dnl br-underlay: with IP: 172.31.1.100 dnl ns0: connect to br-underlay, with IP: 10.1.1.1 AT_SETUP([datapath - truncate and output to gre tunnel]) AT_SKIP_IF([test $HAVE_NC = no]) OVS_CHECK_GRE() CHECK_NO_TC_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) ADD_NAMESPACES(at_ns0) ADD_NAMESPACES(at_ns1) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([gretap], [ns_gre0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [], [address e6:66:c1:11:11:11]) AT_CHECK([ovs-vsctl -- set interface at_gre0 ofport_request=1]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) dnl Set up (p1 and ovs-p1) at br0 ADD_VETH(p1, at_ns1, br0, '10.1.1.2/24') AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=2]) NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Set up (p2 and ovs-p2) as loopback for verifying packet size AT_CHECK([ip link add p2 type veth peer name ovs-p2]) on_exit 'ip link del ovs-p2' AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev p2 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=3]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=4]) dnl use this file as payload file for ncat AT_CHECK([dd if=/dev/urandom of=payload200.bin bs=200 count=1 2> /dev/null]) on_exit 'rm -f payload200.bin' AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows.txt], [dnl priority=99,in_port=1,actions=output(port=2,max_len=100),output(port=3,max_len=100) priority=99,in_port=2,udp,actions=output(port=1,max_len=100) priority=1,in_port=4,ip,actions=drop priority=1,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl del-flows br-underlay]) AT_DATA([flows-underlay.txt], [dnl priority=99,dl_type=0x0800,nw_proto=47,in_port=1,actions=LOCAL priority=99,dl_type=0x0800,nw_proto=47,in_port=LOCAL,ip_dst=172.31.1.1/24,actions=1 priority=1,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br-underlay flows-underlay.txt]) dnl check tunnel push path, from at_ns1 to at_ns0 NS_CHECK_EXEC([at_ns1], [nc $NC_EOF_OPT -u 10.1.1.1 1234 < payload200.bin]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Before truncation = ETH(14) + IP(20) + UDP(8) + 200 = 242B AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=2" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=242 ]) dnl After truncation = outer ETH(14) + outer IP(20) + GRE(4) + 100 = 138B AT_CHECK([ovs-ofctl dump-flows br-underlay | grep "in_port=LOCAL" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=138 ]) dnl check tunnel pop path, from at_ns0 to at_ns1 NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u 10.1.1.2 5678 < payload200.bin]) dnl After truncation = 100 byte at loopback device p2(4) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=4" | ofctl_strip], [0], [dnl n_packets=1, n_bytes=100, priority=1,ip,in_port=4 actions=drop ]) dnl SLOW_ACTION: disable datapath truncate support dnl Repeat the test above, but exercise the SLOW_ACTION code path AT_CHECK([ovs-appctl dpif/set-dp-features br0 trunc false], [0]) dnl SLOW_ACTION test1: check datapatch actions AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl SLOW_ACTION test2: check actual packet truncate AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl del-flows br-underlay]) AT_CHECK([ovs-ofctl add-flows br-underlay flows-underlay.txt]) dnl check tunnel push path, from at_ns1 to at_ns0 NS_CHECK_EXEC([at_ns1], [nc $NC_EOF_OPT -u 10.1.1.1 1234 < payload200.bin]) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl Before truncation = ETH(14) + IP(20) + UDP(8) + 200 = 242B AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=2" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=242 ]) dnl After truncation = outer ETH(14) + outer IP(20) + GRE(4) + 100 = 138B AT_CHECK([ovs-ofctl dump-flows br-underlay | grep "in_port=LOCAL" | sed -n 's/.*\(n\_bytes=[[0-9]]*\).*/\1/p'], [0], [dnl n_bytes=138 ]) dnl check tunnel pop path, from at_ns0 to at_ns1 NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u 10.1.1.2 5678 < payload200.bin]) dnl After truncation = 100 byte at loopback device p2(4) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=4" | ofctl_strip], [0], [dnl n_packets=1, n_bytes=100, priority=1,ip,in_port=4 actions=drop ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - configure cache size]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_KERNEL_EXCL(3, 10, 5, 8) AT_CHECK([ovs-dpctl cache-get-size one-bad-dp], [1], [], [dnl ovs-dpctl: Opening datapath one-bad-dp failed (No such device) ]) AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl masks-cache:size:256 ]) AT_CHECK([ovs-dpctl cache-set-size one-bad-dp masks-cache 0], [1], [], [dnl ovs-dpctl: Opening datapath one-bad-dp failed (No such device) ]) AT_CHECK([ovs-dpctl cache-set-size system@ovs-system dummy-cache 0], [1], [], [dnl ovs-dpctl: Cache name "dummy-cache" not found on dpif (Invalid argument) ]) AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 80000], [1], [], [dnl ovs-dpctl: Setting cache size failed (Numerical result out of range) ]) AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 0], [0], [dnl Setting cache size successful, new size 0 ]) AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl masks-cache:size:0 ]) AT_CHECK([ovs-dpctl cache-set-size system@ovs-system masks-cache 256], [0], [dnl Setting cache size successful, new size 256 ]) AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], [dnl masks-cache:size:256 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - drop action]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_DROP_ACTION() AT_KEYWORDS(drop_action) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Exceed the max number of resubmits. (echo "dl_type=0x806, actions=normal" for i in $(seq 1 64); do j=$(expr $i + 1) echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local" done echo "in_port=65, actions=local" ) > flows.txt AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Generate some traffic. NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2], [1], [ignore]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | dnl strip_ptype | strip_eth | strip_recirc | strip_stats | dnl strip_used | sort], [dnl recirc_id(),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:drop]) OVS_TRAFFIC_VSWITCHD_STOP(["/WARN/d"]) AT_CLEANUP AT_SETUP([datapath - simulated flow action update]) CHECK_NO_DPDK_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl add in_port=ovs-p0,actions=ovs-p1,br0 add in_port=ovs-p1,actions=ovs-p0,br0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | sort | dnl strip_recirc | strip_used | dnl sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/;s/,eth(),/,/;s/bytes:756/bytes:882/'], [0], [dnl recirc_id(),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.0s, actions:3,1 recirc_id(),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:0.0s, actions:2,1 ]) AT_DATA([flows2.txt], [dnl modify in_port=ovs-p0,actions=ovs-p1 modify in_port=ovs-p1,actions=ovs-p0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows2.txt]) AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | sort | dnl strip_recirc | strip_used | dnl sed -e 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/;s/,eth(),/,/;s/bytes:1596/bytes:1862/'], [0], [dnl recirc_id(),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:1862, used:0.0s, actions:3 recirc_id(),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:19, bytes:1862, used:0.0s, actions:2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows | grep "eth_type(0x0800)" | sort | dnl strip_recirc | strip_used | dnl sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/;s/,eth(),/,/;s/bytes:2436/bytes:2842/'], [0], [dnl recirc_id(),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:29, bytes:2842, used:0.0s, actions:3,1 recirc_id(),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:29, bytes:2842, used:0.0s, actions:2,1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - netdev offload software fallback]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_NO_DPDK_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Test the case where only one side has all checksum and tso offload disabled. AT_CHECK([ethtool -K ovs-p1 tso off], [0], [ignore], [ignore]) AT_CHECK([ethtool -K ovs-p1 sg off], [0], [ignore], [ignore]) dnl Reinitialize. AT_CHECK([ovs-vsctl del-port ovs-p1]) AT_CHECK([ovs-vsctl add-port br0 ovs-p1]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns1], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NETNS_DAEMONIZE([at_ns0], [nc -l 1234 > data_0], [nc1.pid]) NETNS_DAEMONIZE([at_ns1], [nc -l 1234 > data_1], [nc2.pid]) AT_CHECK([dd if=/dev/urandom of=payload.bin bs=60000 count=1 2> /dev/null]) on_exit 'rm -f payload.bin' NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT 10.1.1.2 1234 < payload.bin]) NS_CHECK_EXEC([at_ns1], [nc $NC_EOF_OPT 10.1.1.1 1234 < payload.bin]) dnl Wait until transfer completes. OVS_WAIT_WHILE([kill -0 $(cat nc1.pid) $(cat nc2.pid)]) AT_CHECK([diff -q payload.bin data_0], [0]) AT_CHECK([diff -q payload.bin data_1], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - Neighbor Discovery with loose match]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "2001::1:0:392/64", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "2001::1:0:9/64", 36:b1:ee:7c:01:02) dnl Set up flows for moving icmp ND Solicit around. This should be the dnl same for the other ND types. AT_DATA([flows.txt], [dnl table=0 priority=95 icmp6,icmp_type=136,nd_target=2001::1:0:9 actions=resubmit(,10) table=0 priority=95 icmp6,icmp_type=136,nd_target=2001::1:0:392 actions=resubmit(,10) table=0 priority=65 actions=resubmit(,20) table=10 actions=NORMAL table=20 actions=drop ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) m4_define([ND_NS_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x86dd], [ipv6_src=fe80::f816:3eff:fe04:6604,ipv6_dst=fe80::f816:3eff:fea7:dd0e], [nw_proto=58,nw_ttl=255,nw_frag=no], [icmpv6_type=136,icmpv6_code=0], [nd_options_type=2,nd_tll=36:b1:ee:7c:01:03])]) dnl Send a mismatching neighbor discovery. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ND_NS_PKT,nd_target=3000::1')], [0], [ignore]) dnl Send a matching neighbor discovery. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ND_NS_PKT,nd_target=2001::1:0:392')], [0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows | strip_stats | strip_used | dnl strip_key32 | strip_ptype | strip_eth | strip_recirc | dnl grep ",nd" | sort], [0], [dnl recirc_id(),in_port(2),eth(src=36:b1:ee:7c:01:03,dst=36:b1:ee:7c:01:02),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=136),nd(target=2001::1:0:392), packets:0, bytes:0, used:never, actions:1,3 recirc_id(),in_port(2),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=136),nd(target=3000::1), packets:0, bytes:0, used:never, actions:drop ]) OVS_WAIT_UNTIL([ovs-appctl dpctl/dump-flows | grep ",nd" | wc -l | grep -E ^0]) dnl Send a matching neighbor discovery. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ND_NS_PKT,nd_target=2001::1:0:392')], [0], [ignore]) dnl Send a mismatching neighbor discovery. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ND_NS_PKT,nd_target=3000::1')], [0], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows | strip_stats | strip_used | dnl strip_key32 | strip_ptype | strip_eth | strip_recirc | dnl grep ",nd" | sort], [0], [dnl recirc_id(),in_port(2),eth(src=36:b1:ee:7c:01:03,dst=36:b1:ee:7c:01:02),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=136),nd(target=2001::1:0:392), packets:0, bytes:0, used:never, actions:1,3 recirc_id(),in_port(2),eth_type(0x86dd),ipv6(proto=58,frag=no),icmpv6(type=136),nd(target=3000::1), packets:0, bytes:0, used:never, actions:drop ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - Dump OF rules corresponding to UFID]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p1, at_ns0, br0, "10.0.0.1/24") ADD_VETH(p2, at_ns1, br0, "10.0.0.2/24") AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=1 \ -- set interface ovs-p2 ofport_request=2]) dnl Add some OpenFlow rules and groups. AT_DATA([groups.txt], [dnl group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2) ]) AT_DATA([flows.txt], [dnl table=0,arp,actions=NORMAL table=0,priority=100,cookie=0x12345678,in_port=ovs-p1,ip,nw_dst=10.0.0.2,actions=resubmit(,1) table=0,priority=100,cookie=0xabcedf,in_port=ovs-p2,ip,nw_dst=10.0.0.1,actions=ct(table=3) table=1,priority=200,ip,actions=group:1 table=2,ip,actions=ovs-p2 table=3,ip,actions=ovs-p1 ]) AT_CHECK([ovs-ofctl add-groups br0 groups.txt]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.1 -W 2 10.0.0.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl revalidator/wait]) # Add an unrelated OF change to populate the xcache during the next reval run. AT_CHECK([ovs-ofctl add-flow br0 table=4,ip,action=drop]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl revalidator/pause]) AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [stdout]) core=$(head -1 stdout | grep "flow-dump from pmd" | grep -o "[[-]]\?[[0-9]]\+$") if test -z $core; then pmd_id="" else pmd_id="pmd=$core" fi AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep "ipv4" | strip_used | strip_stats | \ strip_duration | strip_dp_hash | strip_recirc | sed 's/:never/:0.0s/' | \ strip_ptype | strip_eth | sort], [0], [dnl recirc_id(),in_port(ovs-p1),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no), packets:0, bytes:0, used:0.0s, actions:hash(l4(0)),recirc() recirc_id(),in_port(ovs-p1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct(commit),recirc() recirc_id(),in_port(ovs-p1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ovs-p2 recirc_id(),in_port(ovs-p2),eth_type(0x0800),ipv4(dst=10.0.0.1,frag=no), packets:0, bytes:0, used:0.0s, actions:ct,recirc() recirc_id(),in_port(ovs-p2),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ovs-p1 ]) ufid=$(ovs-appctl dpctl/dump-flows -m filter='recirc_id(0),in_port(ovs-p1),ipv4' | parse_ufid) AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \ sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl cookie=0x12345678, n_packets=3, n_bytes=294, priority=100,ip,in_port=1,nw_dst=10.0.0.2,actions=resubmit(,1) table_id=1, n_packets=3, n_bytes=294, priority=200,ip,actions=group:1 ]) ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p1),ipv4' | grep "ct(commit)" | parse_ufid) AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \ sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2) ]) ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p1),ipv4' | grep "actions:ovs-p2" | parse_ufid) AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \ sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl table_id=2, n_packets=3, n_bytes=294, ip,actions=output:2 ]) ufid=$(ovs-appctl dpctl/dump-flows -m filter='recirc_id(0),in_port(ovs-p2),ipv4' | grep "ct," | parse_ufid) AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \ sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl cookie=0xabcedf, n_packets=3, n_bytes=294, priority=100,ip,in_port=2,nw_dst=10.0.0.1,actions=ct(table=3) ]) ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p2),ipv4' | grep "actions:ovs-p1" | parse_ufid) AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \ sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl table_id=3, n_packets=3, n_bytes=294, ip,actions=output:1 ]) AT_CHECK([ovs-appctl revalidator/resume]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([datapath - mod_nw_src/set_field on IP fragments]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) AT_DATA([flows.txt], [dnl in_port=ovs-p0,ip,nw_src=10.1.1.1 actions=mod_nw_src=11.1.1.1,ovs-p1 in_port=ovs-p0,ipv6,ipv6_src=fc00::1 actions=set_field:fc00::100->ipv6_src,ovs-p1 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -nn -xx -U -i p1 -w p1.pcap 2> tcpdump.err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump.err]) dnl IPv4 Packet content: dnl Ethernet II, Src: 36:b1:ee:7c:01:03, Dst: 36:b1:ee:7c:01:02 dnl Type: IPv4 (0x0800) dnl Internet Protocol Version 4, Src: 10.1.1.1, Dst: 10.1.1.2 dnl 0100 .... = Version: 4 dnl .... 0101 = Header Length: 20 bytes (5) dnl Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) dnl Total Length: 38 dnl Identification: 0x0001 (1) dnl 001. .... = Flags: 0x1, More fragments dnl 0... .... = Reserved bit: Not set dnl .0.. .... = Don't fragment: Not set dnl ..1. .... = More fragments: Set dnl ...0 0000 0000 0000 = Fragment Offset: 0 dnl Time to Live: 64 dnl Protocol: UDP (17) dnl Header Checksum: 0x44c2 dnl Data (18 bytes) eth="36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00" ip="45 00 00 26 00 01 20 00 40 11 44 c2 0a 01 01 01 0a 01 01 02" data="0b c4 08 84 00 26 e9 64 01 02 03 04 05 06 07 08 09 0a" packet="${eth} ${ip} ${data}" dnl We send each packet multiple times, one for learning, which will go to dnl ovs-vswitchd, and the others will go through the actual datapath. for i in 1 2 3 4 5; do NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) done dnl Update source address and checksums in original packet for comparison. packet=$(echo "$packet" | sed -e 's/ //g' \ -e 's/0a010101/0b010101/g' -e 's/44c2/43c2/g' -e 's/e964/e864/g') OVS_WAIT_UNTIL([test $(ovs-pcap p1.pcap | grep -c "${packet}") -eq 5]) dnl Repeat similar test with IPv6. dnl Packet content: dnl Ethernet II, Src: 36:b1:ee:7c:01:03, Dst: 36:b1:ee:7c:01:02 dnl Type: IPv6 (0x86dd) dnl Internet Protocol Version 6, Src: fc00::1, Dst: fc00::2 dnl Payload Length: 24 dnl Next Header: Fragment Header for IPv6 (44) dnl Hop Limit: 64 dnl Fragment Header for IPv6 dnl Next header: UDP (17) dnl Reserved octet: 0x00 dnl 0000 0000 0000 0... = Offset: 0 (0 bytes) dnl .... .... .... .00. = Reserved bits: 0 dnl .... .... .... ...1 = More Fragments: Yes dnl Identification: 0x2316ab36 dnl Data (16 bytes) eth="36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 86 dd" ip="60 00 00 00 00 18 2c 40 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 11 00 00 01 23 16 ab 36" data="0b c4 08 84 00 26 07 65 01 02 03 04 05 06 07 08" packet="${eth} ${ip} ${data}" for i in 1 2 3 4 5; do NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) done dnl Update checksum and source address in original packet for comparison. packet=$(echo "$packet" | sed -e 's/ //g' -e 's/0765/0666/g' -e \ 's/fc000000000000000000000000000001/fc000000000000000000000000000100/g') OVS_WAIT_UNTIL([test $(ovs-pcap p1.pcap | grep -c "${packet}") -eq 5]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([Meters]) AT_SETUP([meters - datapath probe]) dnl Linux kernel 4.18+ should properly support meters. OVS_CHECK_MIN_KERNEL(4, 18) OVS_TRAFFIC_VSWITCHD_START() dnl Create a lot of meters. for i in $(seq 1023); do AT_CHECK([ovs-ofctl -OOpenFlow13 add-meter br0 "meter=$i kbps band=type=drop rate=100"]) done dnl Kill the process to avoid any potential datapath cleanups. pid=$(cat ovs-vswitchd.pid) AT_CHECK([kill $pid]) OVS_WAIT_WHILE([kill -0 $pid]) dnl Start it back. AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --log-file], [0], [], [stderr]) on_exit "kill_ovs_vswitchd" dnl It should still be possible to create meters. AT_CHECK([ovs-ofctl -OOpenFlow13 add-meter br0 "meter=1 kbps band=type=drop rate=100"]) AT_CHECK([grep -q "broken meter implementation" ovs-vswitchd.log], [1]) OVS_TRAFFIC_VSWITCHD_STOP(['/terminating with signal/d']) AT_CLEANUP AT_BANNER([MPLS]) AT_SETUP([mpls - encap header dp-support]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will encap a mpls header to the ip packet dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is sent from p0(at_ns0) interface directed to dnl p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT')], [0], [ignore]) dnl Check the expected mpls encapsulated packet on the egress interface. m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8847], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'MPLS_HEADER'), $(ovs-ofctl compose-packet --bare 'ICMP_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mpls - encap header slow-path]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will encap a mpls header to the ip packet dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is sent from p0(at_ns0) interface directed to dnl p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT')], [0], [ignore]) dnl Check the expected mpls encapsulated packet on the egress interface. m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8847], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'MPLS_HEADER'), $(ovs-ofctl compose-packet --bare 'ICMP_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mpls_mc - encap header dp-support]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will encap a mpls header to the ip packet dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is sent from p0(at_ns0) interface directed to dnl p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT')], [0], [ignore]) dnl Check the expected mpls encapsulated packet on the egress interface. m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8848], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'MPLS_HEADER'), $(ovs-ofctl compose-packet --bare 'ICMP_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mpls_mc - encap header slow-path]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will encap a mpls header to the ip packet dnl eth/ip/icmp --> OVS --> eth/mpls/eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x0800 actions=encap(mpls_mc),set_mpls_label:2,encap(ethernet),set_field:00:00:00:00:00:02->dl_dst,set_field:00:00:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is sent from p0(at_ns0) interface directed to dnl p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT')], [0], [ignore]) dnl Check the expected mpls encapsulated packet on the egress interface. m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8848], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'MPLS_HEADER'), $(ovs-ofctl compose-packet --bare 'ICMP_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mpls - decap header dp-support]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_SKIP_IF([! ovs-appctl dpif/show-dp-features br0 2>&1 | grep "MPLS Label add: Yes" >/dev/null]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will decap a mpls header which in turn carries a icmp packet dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8847], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is an eth/mpls/eth/ip/icmp sent from p0(at_ns0) interface dnl directed to p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ "$(ovs-ofctl compose-packet --bare 'MPLS_HEADER')" \ "$(ovs-ofctl compose-packet --bare 'ICMP_PKT')"], [0], [ignore]) dnl Check the expected decapsulated on the egress interface. OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q \ "^$(ovs-ofctl compose-packet --bare 'ICMP_PKT')\$"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([mpls - decap header slow-path]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK(ovs-appctl dpif/set-dp-features br0 add_mpls false) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", 36:b1:ee:7c:01:03) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", 36:b1:ee:7c:01:02) dnl The flow will decap a mpls header which in turn carries a icmp packet dnl eth/mpls/eth/ip/icmp --> OVS --> eth/ip/icmp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([MPLS_HEADER], [m4_join([,], [eth_src=00:00:00:00:00:01,eth_dst=00:00:00:00:00:02,eth_type=0x8847], [mpls_label=2,mpls_ttl=64,mpls_bos=1])]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=36:b1:ee:7c:01:03,eth_dst=36:b1:ee:7c:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) dnl The packet is an eth/mpls/eth/ip/icmp sent from p0(at_ns0) interface dnl directed to p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ "$(ovs-ofctl compose-packet --bare 'MPLS_HEADER')" \ "$(ovs-ofctl compose-packet --bare 'ICMP_PKT')"], [0], [ignore]) dnl Check the expected decapsulated on the egress interface. OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q \ "^$(ovs-ofctl compose-packet --bare 'ICMP_PKT')\$"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([QoS]) AT_SETUP([QoS - basic configuration]) OVS_CHECK_TC_QDISC() AT_SKIP_IF([test $HAVE_ETHTOOL = "no"]) OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ip tuntap add ovs-tap0 mode tap]) on_exit 'ip link del ovs-tap0' AT_CHECK([ip tuntap add ovs-tap1 mode tap]) on_exit 'ip link del ovs-tap1' dnl Set maximum link speed to 5Gb. AT_CHECK([ethtool -s ovs-tap0 speed 5000 duplex full]) AT_CHECK([ip link set dev ovs-tap0 up]) AT_CHECK([ethtool -s ovs-tap1 speed 5000 duplex full]) AT_CHECK([ip link set dev ovs-tap1 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-tap0 -- set int ovs-tap0 type=tap]) AT_CHECK([ovs-vsctl add-port br0 ovs-tap1 -- set int ovs-tap1 type=tap]) dnl Adding a custom qdisc to ovs-tap1, ovs-tap0 will have the default qdisc. AT_CHECK([tc qdisc add dev ovs-tap1 root noqueue]) AT_CHECK([tc qdisc show dev ovs-tap1 | grep -q noqueue]) dnl Configure the same QoS for both ports: dnl queue0 uses fixed max-rate. dnl queue1 relies on underlying link speed. AT_CHECK([ovs-vsctl dnl -- --id=@queue0 create queue dnl other_config:min-rate=2000000 other_config:max-rate=3000000 dnl other_config:burst=3000000 dnl -- --id=@queue1 create queue dnl other_config:min-rate=4000000 other_config:burst=4000000 dnl -- --id=@qos create qos dnl type=linux-htb queues:0=@queue0 dnl queues:1=@queue1 -- dnl -- set port ovs-tap0 qos=@qos -- set port ovs-tap1 qos=@qos], [ignore], [ignore]) dnl Wait for qdiscs to be applied. OVS_WAIT_UNTIL([tc qdisc show dev ovs-tap0 | grep -q htb]) OVS_WAIT_UNTIL([tc qdisc show dev ovs-tap1 | grep -q htb]) dnl Check the configuration. m4_define([HTB_CONF0], [rate 2Mbit ceil 3Mbit burst 3.....b cburst 3.....b]) m4_define([HTB_CONF1], [rate 4Mbit ceil 5Gbit burst 5.....b cburst 5.....b]) AT_CHECK([tc class show dev ovs-tap0 | grep -q 'class htb .* HTB_CONF0']) AT_CHECK([tc class show dev ovs-tap0 | grep -q 'class htb .* HTB_CONF1']) AT_CHECK([tc class show dev ovs-tap1 | grep -q 'class htb .* HTB_CONF0']) AT_CHECK([tc class show dev ovs-tap1 | grep -q 'class htb .* HTB_CONF1']) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([QoS - 64bit]) OVS_CHECK_TC_QDISC() AT_SKIP_IF([test $HAVE_TCA_HTB_RATE64 = no]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Configure the QoS with rates that require 64bits, i.e: > 34Gbps. AT_CHECK([ovs-vsctl set port ovs-p0 qos=@qos -- set port ovs-p1 qos=@qos dnl -- --id=@qos create qos dnl type=linux-htb other-config:max-rate=50000000000 queues:0=@queue dnl -- --id=@queue create queue dnl other_config:min-rate=40000000000 other_config:max-rate=50000000000 dnl other_config:burst=5000000], [ignore], [ignore]) OVS_WAIT_UNTIL([tc qdisc show dev ovs-p0 | grep -q htb]) OVS_WAIT_UNTIL([tc qdisc show dev ovs-p1 | grep -q htb]) m4_define([HTB_CONF], [rate 40Gbit ceil 50Gbit burst 6.....b cburst 6.....b]) AT_CHECK([tc class show dev ovs-p0 | grep -q 'class htb .* HTB_CONF']) AT_CHECK([tc class show dev ovs-p1 | grep -q 'class htb .* HTB_CONF']) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([Ingress Policing - 64-bit]) OVS_CHECK_TC_QDISC() AT_SKIP_IF([test $HAVE_TCA_POLICE_PKTRATE64 = no]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(ns0) ADD_VETH(p0, ns0, br0, "10.1.1.1/24") AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_rate=50000000]) AT_CHECK([ovs-vsctl set interface ovs-p0 ingress_policing_burst=400000]) m4_define([POLICE_CONF], [rate 50Gbit burst 7.......b]) AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress | sed -n 's/.*\(rate [[0-9]]*[[a-zA-Z]]* burst [[0-9]]*[[a-zA-Z]]*\).*/\1/; T; p; q' | grep -q 'POLICE_CONF' ]) AT_CHECK([tc -s -d filter show dev ovs-p0 ingress | grep -E "basic|matchall" > /dev/null], [0]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([conntrack]) AT_SETUP([conntrack - controller]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,udp,action=ct(commit),controller priority=100,in_port=2,ct_state=-trk,udp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk+est,udp,action=controller ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Send an unsolicited reply from port 2. This should be dropped. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 ct\(table=0\) '50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000']) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 1 ct\(commit\),controller '50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000']) dnl Now try a reply from port 2. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 ct\(table=0\) '50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000']) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:0 NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ip,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - force commit]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,udp,action=ct(force,commit),controller priority=100,in_port=2,ct_state=-trk,udp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk+est,udp,action=ct(force,commit,table=1) table=1,in_port=2,ct_state=+trk,udp,action=controller ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Send an unsolicited reply from port 2. This should be dropped. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) dnl OK, now start a new connection from port 1. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) dnl Now try a reply from port 2. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl revalidator/purge], [0]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=1,tp_dst=2 udp_csum:0 NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=new|trk,ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=17,ct_tp_src=2,ct_tp_dst=1,ip,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=0,nw_frag=no,tp_src=2,tp_dst=1 udp_csum:0 ]) dnl dnl Check that the directionality has been changed by force commit. dnl AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], [], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2) ]) dnl OK, now send another packet from port 1 and see that it switches again AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], [], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct flush]) CHECK_CONNTRACK() CHECK_CONNTRACK_SCTP() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,ip,action=ct(commit,exec(set_field:0xaa->ct_mark)),2 priority=100,in_port=2,ip,action=ct(zone=5,commit,exec(set_field:0xaa00000000->ct_label)),1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dp=$(ovs-appctl dpctl/dump-dps) m4_foreach([FLUSH_CMD], [[ovs-appctl dpctl/flush-conntrack], [ovs-appctl dpctl/flush-conntrack $dp], [ovs-ofctl ct-flush br0]], [ AS_BOX([Testing with FLUSH_CMD]) dnl Test UDP from port 1 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], [], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 ]) AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=17,ct_tp_src=2,ct_tp_dst=1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], [1]) dnl Test UDP from port 2 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD zone=5 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0]) dnl Test ICMP traffic NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -W 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], [0], [stdout]) AT_CHECK([cat stdout | FORMAT_CT(10.1.1.1)], [0],[dnl icmp,orig=(src=10.1.1.2,dst=10.1.1.1,id=,type=8,code=0),reply=(src=10.1.1.1,dst=10.1.1.2,id=,type=0,code=0),zone=5,labels=0xaa00000000 ]) ICMP_ID=`cat stdout | cut -d ',' -f4 | cut -d '=' -f2` ICMP_TUPLE=ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=1,icmp_id=$ICMP_ID,icmp_type=8,icmp_code=0 AT_CHECK([FLUSH_CMD zone=5 $ICMP_TUPLE]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], [1], [dnl ]) dnl Test UDP from port 1 and 2, partial flush by src port AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_proto=17,ct_tp_src=1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_proto=17,ct_tp_src=2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, partial flush by dst port AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_proto=17,ct_tp_dst=2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_proto=17,ct_tp_dst=1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, partial flush by src address AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, partial flush by dst address AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_dst=10.1.1.2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_dst=10.1.1.1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, partial flush by src address in reply direction AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD '' 'ct_nw_src=10.1.1.2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD zone=5 '' 'ct_nw_src=10.1.1.1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, flush without arguments AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test SCTP flush based on port. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500003400010000408464410a0101010a01010200010002000000009178f7d30100001470e18ccc00000000000a000a00000000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000950540000000a08004500003400010000408464410a0101020a010101000200010000000098f29e470100001470e18ccc00000000000a000a00000000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sed "s/,protoinfo=.*$//" | sort], [0], [dnl sctp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 sctp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.1,ct_nw_proto=132,ct_tp_src=1,ct_tp_dst=2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sed "s/,protoinfo=.*$//" | sort], [0], [dnl sctp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD 'ct_nw_src=10.1.1.2,ct_nw_proto=132,ct_tp_src=2,ct_tp_dst=1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test UDP from port 1 and 2, partial flush by mark and labels. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD mark=0xaa]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD labels=0xaa00000000]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD mark=2/2]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD labels=0x0200000000/0x0200000000]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) dnl Test flush with invalid arguments. AT_CHECK([FLUSH_CMD zone=invalid 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1'], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "failed to parse zone" stderr]) AT_CHECK([FLUSH_CMD zone=1 'ct_nw_src=10.1.1.1,invalid=invalid' 'ct_nw_dst=10.1.1.1'], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "invalid conntrack tuple field: invalid" stderr]) AT_CHECK([FLUSH_CMD zone=1 'ct_nw_src=invalid' 'ct_nw_dst=10.1.1.1'], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "failed to parse field ct_nw_src" stderr]) AT_CHECK([FLUSH_CMD zone=1 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "invalid arguments" stderr]) AT_CHECK([FLUSH_CMD zone=1 mark=1 labels=1 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid invalid], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "command takes at most 6 arguments" stderr]) AT_CHECK([FLUSH_CMD mark=invalid], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "failed to parse mark" stderr]) AT_CHECK([FLUSH_CMD labels=invalid], [ignore], [ignore], [stderr]) AT_CHECK([grep -q "failed to parse labels" stderr]) dnl Test UDP from port 1 and 2, partial flush by zone. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD zone=5]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),mark=170 ]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) AT_CHECK([FLUSH_CMD zone=0]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [0], [dnl udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5,labels=0xaa00000000 ]) AT_CHECK([FLUSH_CMD]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1,"], [1]) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 ping]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit),2 priority=100,in_port=2,icmp,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,icmp,ct_state=+trk+est,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Pings from ns0->ns1 should work fine. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0) ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) dnl Pings from ns1->ns0 should fail. NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], [dnl 7 packets transmitted, 0 received, 100% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - get_nconns and get/set_maxconns]) CHECK_CONNTRACK() CHECK_CT_DPIF_SET_GET_MAXCONNS() CHECK_CT_DPIF_GET_NCONNS() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit),2 priority=100,in_port=2,icmp,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,icmp,ct_state=+trk+est,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Pings from ns0->ns1 should work fine. NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0) ]) AT_CHECK([ovs-appctl dpctl/ct-set-maxconns one-bad-dp], [2], [], [dnl ovs-vswitchd: maxconns missing or malformed (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-set-maxconns a], [2], [], [dnl ovs-vswitchd: maxconns missing or malformed (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-set-maxconns one-bad-dp 10], [2], [], [dnl ovs-vswitchd: datapath not found (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-get-maxconns one-bad-dp], [2], [], [dnl ovs-vswitchd: datapath not found (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-get-nconns one-bad-dp], [2], [], [dnl ovs-vswitchd: datapath not found (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-get-nconns], [], [dnl 1 ]) AT_CHECK([ovs-appctl dpctl/ct-get-maxconns], [], [dnl 3000000 ]) AT_CHECK([ovs-appctl dpctl/ct-set-maxconns 10], [], [dnl setting maxconns successful ]) AT_CHECK([ovs-appctl dpctl/ct-get-maxconns], [], [dnl 10 ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) AT_CHECK([ovs-appctl dpctl/ct-get-nconns], [], [dnl 0 ]) AT_CHECK([ovs-appctl dpctl/ct-get-maxconns], [], [dnl 10 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 ping]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_DATA([flows.txt], [dnl dnl ICMPv6 echo request and reply go to table 1. The rest of the traffic goes dnl through normal action. table=0,priority=10,icmp6,icmp_type=128,action=goto_table:1 table=0,priority=10,icmp6,icmp_type=129,action=goto_table:1 table=0,priority=1,action=normal dnl Allow everything from ns0->ns1. Only allow return traffic from ns1->ns0. table=1,priority=100,in_port=1,icmp6,action=ct(commit),2 table=1,priority=100,in_port=2,icmp6,ct_state=-trk,action=ct(table=0) table=1,priority=100,in_port=2,icmp6,ct_state=+trk+est,action=1 table=1,priority=1,action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl The above ping creates state in the connection tracker. We're not dnl interested in that state. AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Pings from ns1->ns0 should fail. NS_CHECK_EXEC([at_ns1], [ping6 -q -c 3 -i 0.3 -w 2 fc00::1 | FORMAT_PING], [0], [dnl 7 packets transmitted, 0 received, 100% packet loss, time 0ms ]) dnl Pings from ns0->ns1 should work fine. NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl icmpv6,orig=(src=fc00::1,dst=fc00::2,id=,type=128,code=0),reply=(src=fc00::2,dst=fc00::1,id=,type=129,code=0) ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_ipv6_src=fc00::1,ct_ipv6_dst=fc00::2']) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - preserve registers]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow any traffic from ns0->ns1, ns2->ns3. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(commit,table=0) priority=100,in_port=1,tcp,ct_state=+trk,action=2 priority=100,in_port=2,tcp,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,tcp,ct_state=+trk,action=1 priority=100,in_port=3,tcp,ct_state=-trk,action=load:0->NXM_NX_REG0[[]],ct(table=0) priority=100,in_port=3,tcp,ct_state=+trk,reg0=0,action=load:1->NXM_NX_REG0[[]],ct(commit,table=0) priority=100,in_port=3,tcp,ct_state=+trk,reg0=1,action=4 priority=100,in_port=4,tcp,ct_state=-trk,action=ct(commit,table=0) priority=100,in_port=4,tcp,ct_state=+trk,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl HTTP requests from p2->p3 should work fine. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - invalid]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Pass traffic from ns0->ns1 without committing, but attempt to track in dnl the opposite direction. This should fail. dnl Pass traffic from ns3->ns4 without committing, and this time match dnl invalid traffic and allow it through. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk+new,tcp,action=1 priority=100,in_port=3,tcp,action=ct(),4 priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=4,ct_state=+trk+inv,tcp,action=3 priority=100,in_port=4,ct_state=+trk+new,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl We set up our rules to allow the request without committing. The return dnl traffic can't be identified, because the initial request wasn't committed. dnl For the first pair of ports, this means that the connection fails. OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [28], [ignore], [ignore]) dnl For the second pair, we allow packets from invalid connections, so it works. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - zones]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow any traffic from ns0->ns1. Allow return traffic, matching on zone. dnl For ns2->ns3, use a different zone and see that the match fails. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(commit,zone=1),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=1) priority=100,in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1 priority=100,in_port=3,tcp,action=ct(commit,zone=2),4 priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0,zone=2) priority=100,in_port=4,ct_state=+trk,ct_zone=1,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) dnl HTTP requests from p2->p3 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.4)], [0], [dnl tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=,dport=),reply=(src=10.1.1.4,dst=10.1.1.3,sport=,dport=),zone=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - zones from field]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=load:0x1001->NXM_NX_REG0[[0..15]],ct(commit,zone=NXM_NX_REG0[[0..15]]),2 priority=100,in_port=2,ct_state=-trk,tcp,action=load:0x1001->NXM_NX_REG0[[0..15]],ct(table=0,zone=NXM_NX_REG0[[0..15]]) priority=100,in_port=2,ct_state=+trk,ct_zone=0x1001,tcp,action=1 priority=100,in_port=3,tcp,action=load:0x1002->NXM_NX_REG0[[0..15]],ct(commit,zone=NXM_NX_REG0[[0..15]]),4 priority=100,in_port=4,ct_state=-trk,tcp,action=load:0x1002->NXM_NX_REG0[[0..15]],ct(table=0,zone=NXM_NX_REG0[[0..15]]) priority=100,in_port=4,ct_state=+trk,ct_zone=0x1001,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=4097,protoinfo=(state=) ]) dnl HTTP requests from p2->p3 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.4)], [0], [dnl tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=,dport=),reply=(src=10.1.1.4,dst=10.1.1.3,sport=,dport=),zone=4098,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - zones from other field]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(zone=5,table=0) priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_ZONE[]),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5) priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,dnl orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),dnl reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),dnl zone=5,protoinfo=(state=) ]) dnl This is to test when the zoneid is set by a field variable like dnl NXM_NX_CT_ZONE, the OVS xlate should generate a megaflow with a form of dnl "ct_zone(5), ... actions: ct(commit, zone=5)". The match "ct_zone(5)" dnl is needed as if we changes the zoneid into 15 in the following, the old dnl "ct_zone(5), ... actions: ct(commit, zone=5)" megaflow will not get hit, dnl and OVS will generate a new megaflow with the match "ct_zone(0xf)". dnl This will make sure that the new packets are committing to zoneid 15 dnl rather than old 5. AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl | grep "+trk" | grep -q "ct_zone(0x5)" ], [0], []) AT_CHECK([ovs-ofctl mod-flows br0 dnl 'priority=100,ct_state=-trk,tcp,in_port="ovs-p0" actions=ct(table=0,zone=15)']) dnl Wait for a flow flush as some datapaths (read TC) might take time to clear. AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl | grep "+trk" | grep -q "ct_zone(0xf)" ], [0], []) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - zones from other field, more tests]) CHECK_CONNTRACK() OVS_CHECK_GITHUB_ACTION() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(zone=5,table=0,commit,exec(load:0xffff0005->NXM_NX_CT_LABEL[[0..31]])) priority=100,in_port=1,tcp,ct_state=+trk,action=ct(commit,zone=NXM_NX_CT_LABEL[[0..15]]),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=5) priority=100,in_port=2,ct_state=+trk,ct_zone=5,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,dnl orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),dnl reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),dnl zone=5,labels=0xffff0005,protoinfo=(state=) ]) AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl | grep "+trk" | sed 's/0xffff0005\/0xffff/0x5\/0xffff/' dnl | grep -q "ct_label(0x5/0xffff)" ], [0], []) AT_CHECK([ovs-ofctl mod-flows br0 'priority=100,ct_state=-trk,tcp,in_port="ovs-p0" actions=ct(table=0,zone=15,commit,exec(load:0xffff000f->NXM_NX_CT_LABEL[[0..31]]))']) dnl Wait for a flow flush as some datapaths (read TC) might take time to clear. AT_CHECK([ovs-appctl revalidator/wait], [0]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p0 dnl | grep "+trk" | sed 's/0xffff000f\/0xffff/0xf\/0xffff/' dnl | grep -q "ct_label(0xf/0xffff)" ], [0], []) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - multiple bridges]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START( [_ADD_BR([br1]) --\ add-port br0 patch+ -- set int patch+ type=patch options:peer=patch- --\ add-port br1 patch- -- set int patch- type=patch options:peer=patch+ --]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") dnl Allow any traffic from ns0->br1, allow established in reverse. AT_DATA([flows-br0.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=2,tcp,ct_state=-trk,action=ct(commit,zone=1),1 priority=100,in_port=1,tcp,ct_state=-trk,action=ct(table=0,zone=1) priority=100,in_port=1,tcp,ct_state=+trk+est,ct_zone=1,action=2 ]) dnl Allow any traffic from br0->ns1, allow established in reverse. AT_DATA([flows-br1.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(table=0,zone=2) priority=100,in_port=1,tcp,ct_state=+trk+new,ct_zone=2,action=ct(commit,zone=2),2 priority=100,in_port=1,tcp,ct_state=+trk+est,ct_zone=2,action=2 priority=100,in_port=2,tcp,ct_state=-trk,action=ct(table=0,zone=2) priority=100,in_port=2,tcp,ct_state=+trk+est,ct_zone=2,action=ct(commit,zone=2),1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows-br0.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br1 flows-br1.txt]) dnl HTTP requests from p0->p1 should work fine. OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - multiple zones]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(commit,zone=1),ct(commit,zone=2),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=2) priority=100,in_port=2,ct_state=+trk,ct_zone=2,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl (again) HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - multiple namespaces, internal ports]) CHECK_CONNTRACK() CHECK_CONNTRACK_LOCAL_STACK() CHECK_NO_TC_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START( [set-fail-mode br0 secure -- ]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_INT(p0, at_ns0, br0, "10.1.1.1/24") ADD_INT(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. dnl dnl If skb->nfct is leaking from inside the namespace, this test will fail. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=1),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0,zone=1) priority=100,in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl (again) HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP(["dnl /ioctl(SIOCGIFINDEX) on .* device failed: No such device/d /removing policing failed: No such device/d"]) AT_CLEANUP AT_SETUP([conntrack - ct_mark]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow traffic between ns0<->ns1 using the ct_mark. dnl Check that different marks do not match for traffic between ns2<->ns3. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1 priority=100,in_port=3,tcp,action=ct(commit,exec(set_field:2->ct_mark)),4 priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=4,ct_state=+trk,ct_mark=1,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),mark=1,protoinfo=(state=) ]) dnl HTTP requests from p2->p3 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.4)], [0], [dnl tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=,dport=),reply=(src=10.1.1.4,dst=10.1.1.3,sport=,dport=),mark=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct_mark bit-fiddling]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow traffic between ns0<->ns1 using the ct_mark. Return traffic should dnl cause an additional bit to be set in the connection (and be allowed). AT_DATA([flows.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(table=1) table=0,priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=1,commit,exec(set_field:0x2/0x6->ct_mark)) table=1,in_port=1,ct_state=+new,tcp,action=ct(commit,exec(set_field:0x5/0x5->ct_mark)),2 table=1,in_port=1,ct_state=-new,tcp,action=2 table=1,in_port=2,ct_state=+trk,ct_mark=3,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),mark=3,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct_mark from register]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=load:1->NXM_NX_REG0[[0..31]],ct(commit,exec(move:NXM_NX_REG0[[0..31]]->NXM_NX_CT_MARK[[]])),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1 priority=100,in_port=3,tcp,action=load:2->NXM_NX_REG0[[0..31]],ct(commit,exec(move:NXM_NX_REG0[[0..31]]->NXM_NX_CT_MARK[[]])),4 priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=4,ct_state=+trk,ct_mark=1,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),mark=1,protoinfo=(state=) ]) dnl HTTP requests from p2->p3 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.4)], [0], [dnl tcp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=,dport=),reply=(src=10.1.1.4,dst=10.1.1.3,sport=,dport=),mark=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct_label]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow traffic between ns0<->ns1 using the ct_label. dnl Check that different labels do not match for traffic between ns2<->ns3. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(commit,exec(set_field:0x0a000d000005000001->ct_label)),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk,ct_label=0x0a000d000005000001,tcp,action=1 priority=100,in_port=3,tcp,action=ct(commit,exec(set_field:0x2->ct_label)),4 priority=100,in_port=4,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=4,ct_state=+trk,ct_label=0x0a000d000005000001,tcp,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl HTTP requests from p2->p3 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [28], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct_label bit-fiddling]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow traffic between ns0<->ns1 using the ct_labels. Return traffic should dnl cause an additional bit to be set in the connection labels (and be allowed) AT_DATA([flows.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(table=1) table=0,priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=1,commit,exec(set_field:0x200000000/0x200000004->ct_label)) table=1,in_port=1,tcp,ct_state=+new,action=ct(commit,exec(set_field:0x5/0x5->ct_label)),2 table=1,in_port=1,tcp,ct_state=-new,action=2 table=1,in_port=2,ct_state=+trk,ct_label=0x200000001,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),labels=0x200000001,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ct metadata, multiple zones]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow traffic between ns0<->ns1 using the ct_mark and ct_labels in zone=1, dnl but do *not* set any of these for the ct() in zone=2. Traffic should pass, dnl and we should see that the conntrack entries only apply the ct_mark and dnl ct_labels to the connection in zone=1. AT_DATA([flows.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(zone=1,table=1) table=0,priority=100,in_port=2,ct_state=-trk,tcp,action=ct(zone=1,table=1,commit,exec(set_field:0x200000000/0x200000004->ct_label,set_field:0x2/0x6->ct_mark)) table=1,in_port=1,tcp,ct_state=+new,action=ct(zone=1,commit,exec(set_field:0x5/0x5->ct_label,set_field:0x5/0x5->ct_mark)),ct(commit,zone=2),2 table=1,in_port=1,tcp,ct_state=-new,action=ct(zone=2),2 table=1,in_port=2,tcp,action=ct(zone=2),1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,mark=3,labels=0x200000001,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - new connections]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows1.txt], [dnl table=0, priority=1,action=drop table=0, priority=10,arp,action=normal table=0, priority=100,tcp,action=ct(table=1) table=0, priority=100,udp,action=ct(table=1) table=1, priority=100,in_port=1,tcp,ct_state=+trk+new,action=ct(commit) table=1, priority=100,in_port=1,udp,ct_state=+trk+new,action=ct(commit) table=1, priority=100,in_port=1,ct_state=+trk+est,action=2 table=1, priority=100,in_port=2,ct_state=+trk+est,action=1 ]) ovs-appctl vlog/set dbg AT_CHECK([ovs-ofctl --bundle add-flows br0 flows1.txt]) dnl TCP traffic from ns0 to ns1 should fail. OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [28], [ignore], [ignore]) dnl Send UDP packet on port 1 twice. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) dnl There should not be any packet that matches the established ct_state. AT_CHECK([ovs-ofctl dump-flows br0 "table=1 in_port=1,ct_state=+trk+est" | ofctl_strip], [0], [dnl NXST_FLOW reply: table=1, priority=100,ct_state=+est+trk,in_port=1 actions=output:2 ]) dnl Send a 3rd UDP packet on port 1 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) dnl There still should not be any packet that matches the established ct_state. AT_CHECK([ovs-ofctl dump-flows br0 "table=1 in_port=1,ct_state=+trk+est" | ofctl_strip], [0], [dnl NXST_FLOW reply: table=1, priority=100,ct_state=+est+trk,in_port=1 actions=output:2 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - generic IP protocol]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl table=0, priority=1,action=drop table=0, priority=10,arp,action=normal table=0, priority=100,ip,action=ct(table=1) table=1, priority=100,in_port=1,ip,ct_state=+trk+new,action=ct(commit) table=1, priority=100,in_port=1,ct_state=+trk+est,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=01005e00001200005e000101080045c0002800000000ff7019cdc0a8001ee0000012210164010001ba52c0a800010000000000000000000000000000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=192\.168\.0\.30,"], [], [dnl 112,orig=(src=192.168.0.30,dst=224.0.0.18,sport=0,dport=0),reply=(src=224.0.0.18,dst=192.168.0.30,sport=0,dport=0) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_NO_DPDK_OFFLOAD() CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow UDP traffic from ns0->ns1. Only allow related ICMP responses back. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,udp,action=ct(commit,exec(set_field:1->ct_mark)),2 priority=100,in_port=2,icmp,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,icmp,ct_state=+trk+rel,ct_mark=1,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response. NS_CHECK_EXEC([at_ns0], [echo a | nc $NC_EOF_OPT -u 10.1.1.2 10000]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort | grep -v drop], [0], [dnl n_packets=1, n_bytes=44, priority=100,udp,in_port=1 actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[[]])),output:2 n_packets=1, n_bytes=72, priority=100,ct_state=+rel+trk,ct_mark=0x1,icmp,in_port=2 actions=output:1 n_packets=1, n_bytes=72, priority=100,ct_state=-trk,icmp,in_port=2 actions=ct(table=0) n_packets=2, n_bytes=84, priority=10,arp actions=NORMAL NXST_FLOW reply: ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related to original direction]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow UDP traffic from ns0->ns1. Only allow related ICMP responses back. AT_DATA([flows.txt], [dnl priority=1000,arp,action=normal priority=100,ip,action=ct(table=1) priority=1,action=drop table=1,ip,action=ct(zone=34673,table=2) table=2,in_port=2,udp,action=ct(commit,zone=34673),1 table=2,in_port=1,udp,action=ct(commit,zone=34673),2 table=2,in_port=2,ct_state=+rel,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl 1. Send and UDP packet to port 53 (src=192.100.1.8,dst=192.100.2.5) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 resubmit\(,0\) '00010200020400232211223308004500001c000100004011f6fac0640108c06402050035003500087b9e']) dnl 2. Send and UDP packet to port 53 (src=192.100.2.5,dst=192.100.1.8) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 1 resubmit\(,0\) '00232211223300010200020408004500001c000100004011f6fac0640205c06401080035003500087b9e']) dnl 3. Send an ICMP port unreach reply for port 53, related to the 2nd dnl packet, but in the original direction of the conntrack entry created dnl for the 1st packet. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 resubmit\(,0\) '000102000204002322112233080045000038000100003f01f7eec0640108c0640205030a80e5ffffffff4500001c000100003f11f7fac0640205c06401080035003500087b9e']) AT_CHECK([ovs-appctl revalidator/purge], [0]) dnl 4. Repeat 3. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 resubmit\(,0\) '000102000204002322112233080045000038000100003f01f7eec0640108c0640205030a80e5ffffffff4500001c000100003f11f7fac0640205c06401080035003500087b9e']) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort | grep -v drop], [0], [dnl n_packets=4, n_bytes=224, priority=100,ip actions=ct(table=1) priority=1000,arp actions=NORMAL table=1, n_packets=4, n_bytes=224, ip actions=ct(table=2,zone=34673) table=2, n_packets=1, n_bytes=42, udp,in_port=1 actions=ct(commit,zone=34673),output:2 table=2, n_packets=1, n_bytes=42, udp,in_port=2 actions=ct(commit,zone=34673),output:1 table=2, n_packets=2, n_bytes=140, ct_state=+rel,icmp,in_port=2 actions=output:1 NXST_FLOW reply: ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.100.1.8)], [0], [dnl udp,orig=(src=192.100.1.8,dst=192.100.2.5,sport=,dport=),reply=(src=192.100.2.5,dst=192.100.1.8,sport=,dport=),zone=34673 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related 2]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "172.16.0.1/24") ADD_VETH(p1, at_ns1, br0, "172.16.0.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl table=0,ip,action=ct(commit,table=1) table=1,ip,action=controller ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl 1. Send an ICMP port unreach reply for port 8738, without any previous request AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 resubmit\(,0\) 'f64c473528c9c6f54ecb72db080045c0003d2e8700004001f351ac100004ac1000030303da490000000045000021317040004011b138ac100003ac10000411112222000d20966369616f0a']) dnl 2. Send and UDP packet to port 5555 AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 1 resubmit\(,0\) 'c6f94ecb72dbe64c473528c9080045000021317040004011b138ac100001ac100002a28e15b3000d20966369616f0a']) dnl 3. Send an ICMP port unreach reply from a path midpoint for port 5555, related to the first packet AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 2 resubmit\(,0\) 'e64c473528c9c6f94ecb72db080045c0003d2e8700004001f354ac100003ac1000010303553f0000000045000021317040004011b138ac100001ac100002a28e15b3000d20966369616f0a']) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log | grep -v ff02 | grep -v fe80 | grep -v no_match], [0], [dnl NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=inv|trk,ip,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f5:4e:cb:72:db,dl_dst=f6:4c:47:35:28:c9,nw_src=172.16.0.4,nw_dst=172.16.0.3,nw_tos=192,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=3,icmp_code=3 icmp_csum:da49 NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,ip,in_port=1 (via action) data_len=47 (unbuffered) udp,vlan_tci=0x0000,dl_src=e6:4c:47:35:28:c9,dl_dst=c6:f9:4e:cb:72:db,nw_src=172.16.0.1,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=41614,tp_dst=5555 udp_csum:2096 NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,ip,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f9:4e:cb:72:db,dl_dst=e6:4c:47:35:28:c9,nw_src=172.16.0.3,nw_dst=172.16.0.1,nw_tos=192,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=3,icmp_code=3 icmp_csum:553f ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1)], [0], [dnl udp,orig=(src=172.16.0.1,dst=172.16.0.2,sport=,dport=),reply=(src=172.16.0.2,dst=172.16.0.1,sport=,dport=) ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.3)], [0], [dnl ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related NAT with single port]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") AT_DATA([flows.txt], [dnl table=0,ip,ct_state=-trk,actions=ct(table=0,nat) table=0,in_port=ovs-p0,ct_state=+trk+new,udp,actions=ct(commit,nat(dst=10.1.1.2:8080)),ovs-p1 table=0,in_port=ovs-p1,ct_state=+trk+rel+rpl,icmp,actions=ovs-p0 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) rm p0.pcap NETNS_DAEMONIZE([at_ns0], [tcpdump -n -l -U -i p0 -w p0.pcap 2> tcpdump0_err], [tcpdump0.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump0_err]) dnl Send UDP packet from 10.1.1.1:1234 to 10.1.1.240:80 AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-p0,packet=f00000010102f0000001010108004500002944c140004011df100a0101010a0101f004d2005000156b24646573745f756e72656163680a,actions=resubmit(,0)"]) dnl Send "destination unreachable" response AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-p1,packet=f00000010101f00000010102080045c000456a3700004001f9bc0a0101020a01010103031328000000004500002944c140004011dffe0a0101010a01010204d21f9000154cd2646573745f756e72656163680a,actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1," | sort], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.240,sport=1234,dport=80),reply=(src=10.1.1.2,dst=10.1.1.1,sport=8080,dport=1234) ]) OVS_WAIT_UNTIL([ovs-pcap p0.pcap | grep -q "f00000010101f00000010102080045c000456a3700004001f8ce0a0101f00a01010103031416000000004500002944c140004011df100a0101010a0101f004d2005000156b24646573745f756e72656163680a"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit,zone=9),2 priority=100,in_port=2,ct_state=-trk,icmp,action=ct(table=0,zone=9) priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Modify userspace conntrack fragmentation handling. DPCTL_MODIFY_FRAGMENTATION() dnl Ipv4 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check userspace conntrack fragmentation counters. DPCTL_CHECK_FRAGMENTATION_PASS() dnl Ipv4 max packet size fragmentation dropped. NS_EXEC([at_ns0], [ping -s 65507 -q -c 1 -W 0.5 10.1.1.2]) OVS_CHECK_FRAG_LARGE() OVS_TRAFFIC_VSWITCHD_STOP(["/Unsupported big reassembled v4 packet/d"]) AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation with ct orig match]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,ip,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,icmp,ct_state=+rpl,action=1 priority=100,in_port=1,ip,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ct_state=+new+trk,action=ct(commit) priority=100,in_port=1,ip,ct_nw_proto=1,ct_tp_src=8,ct_tp_dst=0,ct_state=+new+trk,action=ct(commit),2 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Packet content: dnl Ethernet II, Src: 50:54:00:00:00:0a, Dst: 50:54:00:00:00:09 dnl Type: IPv4 (0x0800) dnl Internet Protocol Version 4, Src: 10.1.1.1, Dst: 10.1.1.2 dnl Total Length: 1420 dnl Identification: 0x0001 (1) dnl 001. .... = Flags: 0x1, More fragments dnl 0... .... = Reserved bit: Not set dnl .0.. .... = Don't fragment: Not set dnl ..1. .... = More fragments: Set dnl ...0 0000 0000 0000 = Fragment Offset: 0 dnl Time to Live: 64 dnl Protocol: UDP (17) dnl User Datagram Protocol, Src Port: 1, Dst Port: 2 dnl Source Port: 1 dnl Destination Port: 2 dnl Length: 1608 dnl UDP payload (1392 bytes) dnl Data (1392 bytes) eth="50 54 00 00 00 09 50 54 00 00 00 0a 08 00" ip="45 00 05 8c 00 01 20 00 40 11 3f 5c 0a 01 01 01 0a 01 01 02" udp="00 01 00 02 06 48 dd 56" data_len=$(seq 1392) data=$(printf '00 %.0s' ${data_len}) packet="${eth} ${ip} ${udp} ${data}" NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) dnl Packet content: dnl Ethernet II, Src: 50:54:00:00:00:0a, Dst: 50:54:00:00:00:09 dnl Type: IPv4 (0x0800) dnl Internet Protocol Version 4, Src: 10.1.1.1, Dst: 10.1.1.2 dnl 0100 .... = Version: 4 dnl .... 0101 = Header Length: 20 bytes (5) dnl Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) dnl 0000 00.. = Differentiated Services Codepoint: Default (0) dnl .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) dnl Total Length: 228 dnl Identification: 0x0001 (1) dnl 000. .... = Flags: 0x0 dnl 0... .... = Reserved bit: Not set dnl .0.. .... = Don't fragment: Not set dnl ..0. .... = More fragments: Not set dnl ...0 0000 1010 1111 = Fragment Offset: 1400 dnl Time to Live: 64 dnl Protocol: UDP (17) dnl Data (208 bytes) eth="50 54 00 00 00 09 50 54 00 00 00 0a 08 00" ip="45 00 00 e4 00 01 00 af 40 11 63 55 0a 01 01 01 0a 01 01 02" data_len=$(seq 208) data=$(printf '00 %.0s' ${data_len}) packet="${eth} ${ip} ${data}" NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 1 -W 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0) udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation expiry]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal dnl Only allow non-fragmented messages and 1st fragments of each message priority=100,in_port=1,icmp,ip_frag=no,action=ct(commit,zone=9),2 priority=100,in_port=1,icmp,ip_frag=first,action=ct(commit,zone=9),2 priority=100,in_port=2,ct_state=-trk,icmp,action=ct(table=0,zone=9) priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Modify userspace conntrack fragmentation handling. DPCTL_MODIFY_FRAGMENTATION() dnl Ipv4 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 1 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl 7 packets transmitted, 0 received, 100% packet loss, time 0ms ]) dnl Check userspace conntrack fragmentation counters. DPCTL_CHECK_FRAGMENTATION_FAIL() OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation + vlan]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VLAN(p0, at_ns0, 100, "10.2.2.1/24") ADD_VLAN(p1, at_ns1, 100, "10.2.2.2/24") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit,zone=9),2 priority=100,in_port=2,ct_state=-trk,icmp,action=ct(table=0,zone=9) priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Modify userspace conntrack fragmentation handling. DPCTL_MODIFY_FRAGMENTATION() dnl Ipv4 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Check userspace conntrack fragmentation counters. DPCTL_CHECK_FRAGMENTATION_PASS() OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation + cvlan]) CHECK_CONNTRACK() CHECK_NO_TC_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_SVLAN(p0, at_ns0, 4094, "10.255.2.1/24") ADD_SVLAN(p1, at_ns1, 4094, "10.255.2.2/24") ADD_CVLAN(p0.4094, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p1.4094, at_ns1, 100, "10.2.2.2/24") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit,zone=9),2 priority=100,in_port=2,ct_state=-trk,icmp,action=ct(table=0,zone=9) priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) dnl Ipv4 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 fragmentation connectivity check. (outer svlan) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.255.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 larger fragmentation connectivity check. (outer svlan) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.255.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation incomplete reassembled packet]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a5054000000090800450001a400012000001183440a0101010a01010200010002000800000304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) dnl Send the second fragment in order to avoid keeping the first fragment dnl in the queue until the expiration occurs. Fragments already queued, if resent, dnl may lead to failures on the kernel datapath. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1, packet=50540000000a505400000009080045000030000100320011a4860a0101010a01010200010002000800000010203040506070809000010203040506070809, actions=ct(commit)"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Uses same first fragment as above 'incomplete reassembled packet' test. AT_SETUP([conntrack - IPv4 fragmentation with fragments specified]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a5054000000090800450001a400012000001183440a0101010a01010200010002000800000304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809, actions=ct(commit) packet-out in_port=1, packet=50540000000a505400000009080045000030000100320011a4860a0101010a01010200010002000800000010203040506070809000010203040506070809, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation out of order]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a505400000009080045000030000100320011a4860a0101010a01010200010002000800000010203040506070809000010203040506070809, actions=ct(commit) packet-out in_port=1, packet=50540000000a5054000000090800450001a400012000001183440a0101010a01010200010002000800000304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation overlapping fragments by 1 octet]) CHECK_CONNTRACK() CHECK_CONNTRACK_FRAG_OVERLAP() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a5054000000090800450001a400012000001183440a0101010a01010200010002000800000304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809, actions=ct(commit) packet-out in_port=1, packet=50540000000a505400000009080045000030000100310011a4870a0101010a01010200010002000800000010203040506070809000010203040506070809, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) dnl There is one byte of overlap, hence no packet gets thru. conntrack. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 fragmentation overlapping fragments by 1 octet out of order]) CHECK_CONNTRACK() CHECK_CONNTRACK_FRAG_OVERLAP() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a505400000009080045000030000100310011a4870a0101010a01010200010002000800000010203040506070809000010203040506070809, actions=ct(commit) packet-out in_port=1, packet=50540000000a5054000000090800450001a400012000001183440a0101010a01010200010002000800000304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) dnl There is one byte of overlap, hence no packet gets thru. conntrack. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,in_port=1,ipv6,action=ct(commit,zone=9),2 priority=10,in_port=2,ct_state=-trk,ipv6,action=ct(table=0,zone=9) priority=10,in_port=2,ct_state=+trk+est-new,ipv6,action=1 priority=100,icmp6,icmp_type=135,action=normal priority=100,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl Ipv6 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv6 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv6 max packet size fragmentation dropped. NS_EXEC([at_ns0], [ping6 -s 65487 -q -c 1 -W 0.5 fc00::2]) OVS_CHECK_FRAG_LARGE() OVS_TRAFFIC_VSWITCHD_STOP(["/Unsupported big reassembled v6 packet/d"]) AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation with ct orig match]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", "50:54:00:00:00:09", [], "nodad") ADD_VETH(p1, at_ns1, br0, "fc00::2/96", "50:54:00:00:00:0a", [], "nodad") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,ipv6,ct_state=-trk,action=ct(table=0) priority=10,in_port=2,ipv6,ct_tp_src=128,ct_state=+trk+est+rpl,action=1 priority=10,in_port=1,ipv6,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,ct_state=+new+trk,action=ct(commit) priority=10,in_port=1,ipv6,ct_nw_proto=58,ct_tp_src=128,ct_tp_dst=0,ct_state=+new+trk,action=ct(commit),2 priority=100,icmp6,icmp_type=135,action=normal priority=100,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Packet content: dnl Ethernet II, Src: 50:54:00:00:00:0a, Dst: 50:54:00:00:00:09 dnl Type: IPv6 (0x86dd) dnl Internet Protocol Version 6, Src: fc00::1, Dst: fc00::2 dnl Payload Length: 1344 dnl Next Header: Fragment Header for IPv6 (44) dnl Hop Limit: 64 dnl Fragment Header for IPv6 dnl Next header: UDP (17) dnl Reserved octet: 0x00 dnl 0000 0000 0000 0... = Offset: 0 (0 bytes) dnl .... .... .... .00. = Reserved bits: 0 dnl .... .... .... ...1 = More Fragments: Yes dnl Identification: 0x9bdb1fa7 dnl User Datagram Protocol, Src Port: 1, Dst Port: 2 dnl Source Port: 1 dnl Destination Port: 2 dnl Length: 1608 dnl UDP payload (1328 bytes) dnl Data (1328 bytes) eth="50 54 00 00 00 09 50 54 00 00 00 0a 86 dd" ipv6="60 00 00 00 05 40 2c 40 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 11 00 00 01 9b db 1f a7" udp="00 01 00 02 06 48 fb 56" data_len=$(seq 1328) data=$(printf '00 %.0s' ${data_len}) packet="${eth} ${ipv6} ${udp} ${data}" NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) dnl IPv6 Packet content dnl Ethernet II, Src: 50:54:00:00:00:0a, Dst: 50:54:00:00:00:09 dnl Type: IPv6 (0x86dd) dnl Internet Protocol Version 6, Src: fc00::1, Dst: fc00::2 dnl Payload Length: 280 dnl Next Header: Fragment Header for IPv6 (44) dnl Hop Limit: 64 dnl Fragment Header for IPv6 dnl Next header: UDP (17) dnl Reserved octet: 0x00 dnl 0000 0101 0011 1... = Offset: 167 (1336 bytes) dnl .... .... .... .00. = Reserved bits: 0 dnl .... .... .... ...0 = More Fragments: No dnl Identification: 0x9bdb1fa7 dnl Data (272 bytes) eth="50 54 00 00 00 09 50 54 00 00 00 0a 86 dd" ipv6="60 00 00 00 01 18 2c 40 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 \ fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 11 00 05 38 9b db 1f a7" data_len=$(seq 272) data=$(printf '00 %.0s' ${data_len}) packet="${eth} ${ipv6} ${data}" NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 ${packet} > /dev/null]) dnl Send also fragmented ICMPv6. NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 1 -W 1 fc00::2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2) | sort], [0], [dnl icmpv6,orig=(src=fc00::1,dst=fc00::2,id=,type=128,code=0),reply=(src=fc00::2,dst=fc00::1,id=,type=129,code=0) udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation expiry]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_DATA([flows.txt], [dnl priority=1,action=drop dnl Only allow non-fragmented messages and 1st fragments of each message priority=10,in_port=1,ipv6,ip_frag=first,action=ct(commit,zone=9),2 priority=10,in_port=1,ipv6,ip_frag=no,action=ct(commit,zone=9),2 priority=10,in_port=2,ct_state=-trk,ipv6,action=ct(table=0,zone=9) priority=10,in_port=2,ct_state=+trk+est-new,ipv6,action=1 dnl Neighbour Discovery priority=100,icmp6,icmp_type=135,action=normal priority=100,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl Send an IPv6 fragment. Some time later, it should expire. NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 1 -i 0.3 -w 2 fc00::2 | FORMAT_PING], [0], [dnl 7 packets transmitted, 0 received, 100% packet loss, time 0ms ]) dnl At this point, the kernel will either crash or everything is OK. OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation + vlan]) CHECK_NO_DPDK_OFFLOAD() CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") ADD_VLAN(p0, at_ns0, 100, "fc00:1::3/96") ADD_VLAN(p1, at_ns1, 100, "fc00:1::4/96") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,in_port=1,ipv6,action=ct(commit,zone=9),2 priority=10,in_port=2,ct_state=-trk,ipv6,action=ct(table=0,zone=9) priority=10,in_port=2,ct_state=+trk+est-new,ipv6,action=1 priority=100,icmp6,icmp_type=135,action=normal priority=100,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl Ipv4 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00:1::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv4 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00:1::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation + cvlan]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") ADD_SVLAN(p0, at_ns0, 4094, "fc00:ffff::3/96") ADD_SVLAN(p1, at_ns1, 4094, "fc00:ffff::4/96") ADD_CVLAN(p0.4094, at_ns0, 100, "fc00:1::3/96") ADD_CVLAN(p1.4094, at_ns1, 100, "fc00:1::4/96") dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,in_port=1,ipv6,action=ct(commit,zone=9),2 priority=10,in_port=2,ct_state=-trk,ipv6,action=ct(table=0,zone=9) priority=10,in_port=2,ct_state=+trk+est-new,ipv6,action=1 priority=100,icmp6,icmp_type=135,action=normal priority=100,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00:1::4]) dnl Ipv6 fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00:1::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv6 fragmentation connectivity check. (outer svlan) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00:ffff::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv6 larger fragmentation connectivity check. NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00:1::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Ipv6 larger fragmentation connectivity check. (outer svlan) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00:ffff::4 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation incomplete reassembled packet]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_DATA([bundle.txt], [dnl packet-out in_port=1, packet=50540000000a50540000000986dd6000000005002cfffc000000000000000000000000000001fc0000000000000000000000000000021100000100000001000100020008f62900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090010203040506070809001020304050607080900102030405060708090010203040506070809001020304050607080900102030405060708090010203040506070, actions=ct(commit) ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation with fragments specified]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000A50540000000986DD6000000005002CFFFC00]dnl [0000000000000000000000000001FC00000000000000000000000000000211000001000000010]dnl [00100020008267100010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900010203040506070809000102030405]dnl [06070809000102030405060708090001020304050607080900010203040506070809000102030]dnl [40506070809000102030405060708090001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900010203040506070809000102030405]dnl [06070809000102030405060708090001020304050607080900010203040506070809000102030]dnl [40506070809000102030405060708090001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809001020304050607080900102030405060708090010203040506]dnl [07080900102030405060708090010203040506070809001020304050607080900102030405060]dnl [70, actions=ct(commit, zone=1)] [packet-out in_port=1, packet=50540000000A50540000000986DD6000000000242CFFFC00]dnl [0000000000000000000000000001FC000000000000000000000000000002110004F8000000010]dnl 0010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=1)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=1 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation out of order]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000A50540000000986DD6000000000242CFFFC00]dnl [0000000000000000000000000001FC000000000000000000000000000002110004F8000000010]dnl [0010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=2)] [packet-out in_port=1, packet=50540000000A50540000000986DD6000000005002CFFFC00]dnl [0000000000000000000000000001FC00000000000000000000000000000211000001000000010]dnl [00100020008267100010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900010203040506070809000102030405]dnl [06070809000102030405060708090001020304050607080900010203040506070809000102030]dnl [40506070809000102030405060708090001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900010203040506070809000102030405060]dnl [70809000102030405060708090001020304050607080900010203040506070809000102030405]dnl [06070809000102030405060708090001020304050607080900010203040506070809000102030]dnl [40506070809000102030405060708090001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809001020304050607080900102030405060708090010203040506]dnl [07080900102030405060708090010203040506070809001020304050607080900102030405060]dnl [70, actions=ct(commit, zone=2)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=2 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=2 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") # Add different extension headers AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000A50540000000986DD60000000050800FFFC00]dnl [0000000000000000000000000001FC0000000000000000000000000000022C000000000000001]dnl [10000010000000100010002000826710001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900102030405060708090010203040506070]dnl [80900102030405060708090010203040506070809001020304050607080900102030405060708]dnl [090010203040506070, actions=ct(commit, zone=3)] [packet-out in_port=1, packet=50540000000a50540000000986dd60000000002c00fffc00]dnl [0000000000000000000000000001fc0000000000000000000000000000022c000000000000001]dnl [10004f80000000100010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=3)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=3 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=3 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers + out of order]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") # Add different extension headers AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000a50540000000986dd60000000002c00fffc00]dnl [0000000000000000000000000001fc0000000000000000000000000000022c000000000000001]dnl [10004f80000000100010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=4)] [packet-out in_port=1, packet=50540000000A50540000000986DD60000000050800FFFC00]dnl [0000000000000000000000000001FC0000000000000000000000000000022C000000000000001]dnl [10000010000000100010002000826710001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900102030405060708090010203040506070]dnl [80900102030405060708090010203040506070809001020304050607080900102030405060708]dnl [090010203040506070, actions=ct(commit, zone=4)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=4 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=4 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers 2]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") # Add different extension headers AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000A50540000000986DD60000000050800FFFC00]dnl [0000000000000000000000000001FC0000000000000000000000000000022C000000050200001]dnl [10000010000000100010002000826710001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900102030405060708090010203040506070]dnl [80900102030405060708090010203040506070809001020304050607080900102030405060708]dnl [090010203040506070, actions=ct(commit, zone=5)] [packet-out in_port=1, packet=50540000000a50540000000986dd60000000002c00fffc00]dnl [0000000000000000000000000001fc0000000000000000000000000000022c000000050200001]dnl [10004f80000000100010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=5)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=5 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=5 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers 2 + out of order]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() DPCTL_SET_MIN_FRAG_SIZE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") # Add different extension headers AT_DATA([bundle.txt], [dnl [packet-out in_port=1, packet=50540000000a50540000000986dd60000000002c00fffc00]dnl [0000000000000000000000000001fc0000000000000000000000000000022c000000050200001]dnl [10004f80000000100010002000800000001020304050607080900010203040506070809, ]dnl [actions=ct(commit, zone=6)] [packet-out in_port=1, packet=50540000000A50540000000986DD60000000050800FFFC00]dnl [0000000000000000000000000001FC0000000000000000000000000000022C000000050200001]dnl [10000010000000100010002000826710001020304050607080900010203040506070809000102]dnl [03040506070809000102030405060708090001020304050607080900010203040506070809000]dnl [10203040506070809000102030405060708090001020304050607080900010203040506070809]dnl [00010203040506070809000102030405060708090001020304050607080900010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090010203040506070]dnl [80900010203040506070809000102030405060708090001020304050607080900010203040506]dnl [07080900010203040506070809000102030405060708090001020304050607080900010203040]dnl [50607080900010203040506070809000102030405060708090001020304050607080900010203]dnl [04050607080900010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090010203040506070809000102030405060708090001020304050607080900010]dnl [20304050607080900010203040506070809000102030405060708090001020304050607080900]dnl [01020304050607080900010203040506070809000102030405060708090001020304050607080]dnl [90001020304050607080900010203040506070809000102030405060708090001020304050607]dnl [08090001020304050607080900010203040506070809000102030405060708090001020304050]dnl [60708090001020304050607080900010203040506070809000102030405060708090001020304]dnl [05060708090001020304050607080900010203040506070809000102030405060708090001020]dnl [30405060708090001020304050607080900010203040506070809000102030405060708090001]dnl [02030405060708090001020304050607080900010203040506070809000102030405060708090]dnl [00102030405060708090001020304050607080900010203040506070809000102030405060708]dnl [09000102030405060708090001020304050607080900102030405060708090010203040506070]dnl [80900102030405060708090010203040506070809001020304050607080900102030405060708]dnl [090010203040506070, actions=ct(commit, zone=6)] ]) AT_CHECK([ovs-ofctl bundle br0 bundle.txt]) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=6 | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=6 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - Fragmentation over vxlan]) OVS_CHECK_VXLAN() CHECK_CONNTRACK() CHECK_CONNTRACK_LOCAL_STACK() CHECK_NO_TC_OFFLOAD() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,icmp,action=ct(commit,zone=9),LOCAL priority=100,in_port=LOCAL,icmp,action=ct(table=1,zone=9) table=1,in_port=LOCAL,ct_state=+trk+est,icmp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.1.1.1/24], [id 0 dstport 4789]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 Fragmentation over vxlan]) OVS_CHECK_VXLAN() CHECK_CONNTRACK() CHECK_CONNTRACK_LOCAL_STACK() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay]) AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) ADD_NAMESPACES(at_ns0) dnl Sending ping through conntrack AT_DATA([flows.txt], [dnl priority=1,action=drop priority=100,in_port=1,ipv6,action=ct(commit,zone=9),LOCAL priority=100,in_port=LOCAL,ipv6,action=ct(table=1,zone=9) table=1,in_port=LOCAL,ct_state=+trk+est,ipv6,action=1 dnl Neighbour Discovery priority=1000,icmp6,icmp_type=135,action=normal priority=1000,icmp6,icmp_type=136,action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], ["fc00::2/96"]) ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], ["fc00::1/96"], [id 0 dstport 4789]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay with different packet sizes NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 1600 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -W 2 fc00::2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 Fragmentation + NAT]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START( [set-fail-mode br0 secure -- ]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.2.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.2.1.2/24") dnl Create a dummy route for NAT NS_CHECK_EXEC([at_ns1], [ip addr add 10.1.1.2/32 dev lo]) NS_CHECK_EXEC([at_ns0], [ip route add 10.1.1.0/24 via 10.2.1.2]) NS_CHECK_EXEC([at_ns1], [ip route add 10.1.1.0/24 via 10.2.1.1]) dnl Solely for debugging when things go wrong NETNS_DAEMONIZE([at_ns0], [tcpdump -l -n -xx -U -i p0 -w p0.pcap >tcpdump.out 2>/dev/null], [tcpdump_0.pid]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap >tcpdump.out 2>/dev/null], [tcpdump_1.pid]) AT_DATA([flows.txt], [dnl table=0,arp,actions=normal table=0,ct_state=-trk,ip,in_port=ovs-p0, actions=ct(table=1, nat) table=0,ct_state=-trk,ip,in_port=ovs-p1, actions=ct(table=1, nat) table=1,ct_state=+trk+new,ip,in_port=ovs-p0, actions=ct(commit, nat(src=10.1.1.1)),ovs-p1 table=1,ct_state=+trk+est,ip,in_port=ovs-p0, actions=ovs-p1 table=1,ct_state=+trk+est,ip,in_port=ovs-p1, actions=ovs-p0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Check connectivity NS_CHECK_EXEC([at_ns0], [ping -c 1 10.1.1.2 -M dont -s 4500 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - resubmit to ct multiple times]) CHECK_NO_DPDK_OFFLOAD() CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START( [set-fail-mode br0 secure -- ]) ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl table=0,priority=150,arp,action=normal table=0,priority=100,ip,in_port=1,action=resubmit(,1),resubmit(,2) table=1,ip,action=ct(table=3) table=2,ip,action=ct(table=3) table=3,ip,action=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 0 received, 100% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl n_packets=1, n_bytes=98, priority=100,ip,in_port=1 actions=resubmit(,1),resubmit(,2) n_packets=2, n_bytes=84, priority=150,arp actions=NORMAL table=1, n_packets=1, n_bytes=98, ip actions=ct(table=3) table=2, n_packets=1, n_bytes=98, ip actions=ct(table=3) table=3, n_packets=2, n_bytes=196, ip actions=drop NXST_FLOW reply: ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - zone-based timeout policy]) CHECK_CONNTRACK() CHECK_CONNTRACK_TIMEOUT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,ip,action=ct(zone=5, table=1) priority=100,in_port=2,ip,action=ct(zone=5, table=1) table=1,in_port=2,ip,ct_state=+trk+est,action=1 table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit,zone=5),2 table=1,in_port=1,ip,ct_state=+trk+est,action=2 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Test with default timeout dnl The default udp_single and icmp_first timeouts are 30 seconds in dnl kernel DP, and 60 seconds in userspace DP. dnl Send ICMP and UDP traffic NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) sleep 4 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=5 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=5 ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Shorten the udp_single and icmp_first timeout in zone 5 dnl Userspace datapath uses udp_first and icmp_reply, and dnl kernel datapath uses udp_single and icmp_first VSCTL_ADD_DATAPATH_TABLE() dnl Creating more timeout policies for i in `seq 1 255`; do ovs-vsctl --may-exist add-zone-tp $DP_TYPE zone=$i udp_first=$i udp_single=$i icmp_first=$i icmp_reply=$i; done AT_CHECK([ovs-vsctl --may-exist add-zone-tp $DP_TYPE zone=5 udp_first=1 udp_single=1 icmp_first=1 icmp_reply=1]) dnl Send ICMP and UDP traffic NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=5 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=5 ]) dnl Wait until the timeout expire. dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired. sleep 6 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) dnl Re-send ICMP and UDP traffic to test conntrack cache NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=5 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=5 ]) dnl Wait until the timeout expire. dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired. sleep 6 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ]) dnl Set the timeout policy to default again. AT_CHECK([ovs-vsctl del-zone-tp $DP_TYPE zone=5]) dnl Send ICMP and UDP traffic NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) sleep 1 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=5 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=5 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - SCTP SNAT with port range]) CHECK_CONNTRACK() CHECK_CONNTRACK_SCTP() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) dnl Allow any traffic from ns0->ns1. Only allow return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl table=0,priority=100,in_port=1,sctp,action=ct(commit,zone=1,nat(src=10.1.1.240:34567)),controller table=0,priority=100,in_port=2,ct_state=-trk,sctp,tp_dst=34567,action=ct(table=1,zone=1,nat) table=0,priority=0,action=drop table=1,priority=100,in_port=2,ct_state=+trk+rpl,ct_zone=1,sctp,action=controller table=1,priority=0,action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log]) dnl Simple SCTP association local and remote single homing dnl Send INIT. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=e666c1222222e666c111111108004502004400004000408424300a0101010a010102d6b9303900000000c5cc426b0100002470e18ccc0001a000000affff7ae1c142000c00060005000080000004c0000004 actions=resubmit(,0)"]) dnl Reply INIT_ACK. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=e666c1111111e666c122222208004502012400004000408422610a0101020a0101f03039870770e18ccc97abd49a0200010425bb9dfa0001a000000a000abb90fba5000700e827a048cd1474b111490710816ec95cfc501126b200000000000000000000000000000000fa9dbb25cc8ce17000000000000000002b953b0e1d346d160a000a00a5fb90bb020087070a0101f00000000000000000000000000000000000000000393001000000000080020024fbb82eae13af8d70329bc42bb7cd7e6458d60ff1a181e9b41167c2cab54471bf0000000000000000000000000000000000000000000000000000000000000000000000000100002470e18ccc0001a000000affff7ae1c142000c00060005000080000004c00000040000000000000000000000000000000080000004c0000004 actions=resubmit(,0)"]) dnl Send COOKIE_ECHO. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=e666c1222222e666c1111111080045020108000040004084236c0a0101010a010102d6b9303925bb9dfaf2c860300a0000e827a048cd1474b111490710816ec95cfc501126b200000000000000000000000000000000fa9dbb25cc8ce17000000000000000002b953b0e1d346d160a000a00a5fb90bb020087070a0101f00000000000000000000000000000000000000000393001000000000080020024fbb82eae13af8d70329bc42bb7cd7e6458d60ff1a181e9b41167c2cab54471bf0000000000000000000000000000000000000000000000000000000000000000000000000100002470e18ccc0001a000000affff7ae1c142000c00060005000080000004c000000400000000000000000000000000000000 actions=resubmit(,0)"]) dnl Reply COOKIE_ACK. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=e666c1111111e666c122222208004502002400004000408423610a0101020a0101f03039870770e18ccc0391398b0b000004 actions=resubmit(,0)"]) dnl Send DATA. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=e666c1222222e666c1111111080045020034000140004084243f0a0101010a010102d6b9303925bb9dfabc366345000300147ae1c1420000000000000000666f6f0a actions=resubmit(,0)"]) dnl Reply SACK. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=e666c1111111e666c122222208004502003042c840004084e08c0a0101020a0101f03039870770e18ccc6a990714030000107ae1c14200019ffc00000000 actions=resubmit(,0)"]) dnl ABORT the association. The association cannot be gracefully terminated because of dnl a small timeouts in SHUTDOWN_SENT in the kernel datapath that would make the test unreliable AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=e666c1222222e666c111111108004500002400010000408464510a0101010a010102d6b9303925bb9dfae3b82c3806000004 actions=resubmit(,0)"]) AT_CHECK([ovs-appctl revalidator/purge], [0]) OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=82 in_port=1 (via action) data_len=82 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:11:11:11,dl_dst=e6:66:c1:22:22:22,nw_src=10.1.1.240,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=34567,tp_dst=12345 sctp_csum:9670267b NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=306 ct_state=est|rpl|trk|dnat,ct_zone=1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=132,ct_tp_src=54969,ct_tp_dst=12345,ip,in_port=2 (via action) data_len=306 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:22:22:22,dl_dst=e6:66:c1:11:11:11,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=12345,tp_dst=54969 sctp_csum:49864886 NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=278 in_port=1 (via action) data_len=278 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:11:11:11,dl_dst=e6:66:c1:22:22:22,nw_src=10.1.1.240,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=34567,tp_dst=12345 sctp_csum:8c816918 NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=50 ct_state=est|rpl|trk|dnat,ct_zone=1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=132,ct_tp_src=54969,ct_tp_dst=12345,ip,in_port=2 (via action) data_len=50 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:22:22:22,dl_dst=e6:66:c1:11:11:11,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=12345,tp_dst=54969 sctp_csum:ef4749fc NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=66 in_port=1 (via action) data_len=66 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:11:11:11,dl_dst=e6:66:c1:22:22:22,nw_src=10.1.1.240,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=34567,tp_dst=12345 sctp_csum:eb2b2c17 NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=62 ct_state=est|rpl|trk|dnat,ct_zone=1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=132,ct_tp_src=54969,ct_tp_dst=12345,ip,in_port=2 (via action) data_len=62 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:22:22:22,dl_dst=e6:66:c1:11:11:11,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=2,nw_ttl=64,nw_frag=no,tp_src=12345,tp_dst=54969 sctp_csum:9b67e853 NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=50 in_port=1 (via action) data_len=50 (unbuffered) sctp,vlan_tci=0x0000,dl_src=e6:66:c1:11:11:11,dl_dst=e6:66:c1:22:22:22,nw_src=10.1.1.240,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=34567,tp_dst=12345 sctp_csum:4bb49f65 ]) dnl Check the ct entry dnl protoinfo has to be removed in order to normalize the current difference between user and kernel output AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed 's/,protoinfo=.*$//' ], [], [dnl sctp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),zone=1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl Check kernel datapath to make sure conntrack fills in L3 and L4 dnl protocol information AT_SETUP([conntrack - fragment reassembly with L3 L4 protocol information]) CHECK_CONNTRACK() CHECK_L3L4_CONNTRACK_REASM() OVS_TRAFFIC_VSWITCHD_START() AT_DATA([flows.txt], [dnl action=normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f70605b02c4020010001000000000000000000000020200100010000000000000000000000101100000134e88deb13891389080803136161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl "16161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161"dnl "61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl "1616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161, actions=ct(table=1)"]) AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f70602682c402001000100000000000000000000002020010001000000000000000000000010110005a834e88deb6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl "161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161, actions=ct(table=1)"]) AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f706033d1140200100010000000000000000000000202001000100000000000000000000001013891389033d923861616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl "1616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610a, actions=ct(table=1)"]) AT_CHECK([ovs-appctl dpctl/dump-flows | head -2 | tail -1 | grep -q -e ["]udp[(]src=5001["]]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([conntrack - L7]) AT_SETUP([conntrack - IPv4 HTTP]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,action=ct(commit),2 priority=100,in_port=2,ct_state=-trk,tcp,action=ct(table=0) priority=100,in_port=2,ct_state=+trk+est,tcp,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns0], [http]) OVS_START_L7([at_ns1], [http]) dnl HTTP requests from ns0->ns1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) ]) dnl HTTP requests from ns1->ns0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_HTTP([10.1.1.1]), [28], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 HTTP]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", [], [], "nodad") ADD_VETH(p1, at_ns1, br0, "fc00::2/96", [], [], "nodad") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,icmp6,action=normal priority=100,in_port=1,tcp6,action=ct(commit),2 priority=100,in_port=2,ct_state=-trk,tcp6,action=ct(table=0) priority=100,in_port=2,ct_state=+trk+est,tcp6,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) OVS_START_L7([at_ns0], [http6]) OVS_START_L7([at_ns1], [http6]) dnl HTTP requests from ns0->ns1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([[http://[[fc00::2]]]]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),protoinfo=(state=) ]) dnl HTTP requests from ns1->ns0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_HTTP([[http://[[fc00::1]]]]), [28], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - commit, recirc]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.4/24") dnl Allow any traffic from ns0->ns1, ns2->ns3. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=10,icmp,action=normal priority=100,in_port=1,tcp,ct_state=-trk,action=ct(commit,table=0) priority=100,in_port=1,tcp,ct_state=+trk,action=2 priority=100,in_port=2,tcp,ct_state=-trk,action=ct(table=0) priority=100,in_port=2,tcp,ct_state=+trk,action=1 priority=100,in_port=3,tcp,ct_state=-trk,action=set_field:0->metadata,ct(table=0) priority=100,in_port=3,tcp,ct_state=+trk,metadata=0,action=set_field:1->metadata,ct(commit,table=0) priority=100,in_port=3,tcp,ct_state=+trk,metadata=1,action=4 priority=100,in_port=4,tcp,ct_state=-trk,action=ct(commit,table=0) priority=100,in_port=4,tcp,ct_state=+trk,action=3 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [http]) OVS_START_L7([at_ns3], [http]) dnl HTTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl HTTP requests from p2->p3 should work fine. NS_CHECK_EXEC([at_ns2], OVS_GET_HTTP([10.1.1.4]), [0], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - multiple zones, local]) CHECK_EXTERNAL_CT() CHECK_CONNTRACK() CHECK_CONNTRACK_LOCAL_STACK() OVS_TRAFFIC_VSWITCHD_START() ADD_EXTERNAL_CT([br0]) ADD_NAMESPACES(at_ns0) AT_CHECK([ip addr add dev br0 "10.1.1.1/24"]) AT_CHECK([ip link set dev br0 up]) on_exit 'ip addr del dev br0 "10.1.1.1/24"' ADD_VETH(p0, at_ns0, br0, "10.1.1.2/24") dnl Allow traffic from local stack to ns0. Only allow neighbour discovery, dnl return traffic from ns0 back to the local stack. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=LOCAL,ip,ct_state=-trk,action=drop priority=100,in_port=LOCAL,ip,ct_state=+trk+new,action=ct(commit,zone=1),ct(commit,zone=2),1 priority=100,in_port=LOCAL,ip,ct_state=+trk+est,action=ct(commit,zone=1),ct(commit,zone=2),1 priority=100,in_port=1,ip,ct_state=-trk,action=ct(table=1,zone=1) table=1,in_port=1,ip,ct_state=+trk+est,ct_zone=1,action=ct(table=2,zone=2) table=2,in_port=1,ip,ct_state=+trk+est,ct_zone=2,action=LOCAL ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CHECK([ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_START_L7([at_ns0], [http]) dnl HTTP requests from root namespace to p0 should work fine. AT_CHECK(OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl (again) HTTP requests from root namespace to p0 should work fine. AT_CHECK(OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | grep "zone"], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=1 icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=2 tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - multi-stage pipeline, local]) CHECK_EXTERNAL_CT() CHECK_CONNTRACK() CHECK_CONNTRACK_LOCAL_STACK() OVS_TRAFFIC_VSWITCHD_START() ADD_EXTERNAL_CT([br0]) ADD_NAMESPACES(at_ns0) AT_CHECK([ip addr add dev br0 "10.1.1.1/24"]) AT_CHECK([ip link set dev br0 up]) on_exit 'ip addr del dev br0 "10.1.1.1/24"' ADD_VETH(p0, at_ns0, br0, "10.1.1.2/24") dnl Allow traffic from local stack to ns0. Only allow neighbour discovery, dnl return traffic from ns0 back to the local stack. AT_DATA([flows.txt], [dnl dnl default table=0,priority=1,action=drop table=0,priority=10,arp,action=normal dnl Load the output port to REG0 table=0,priority=100,ip,in_port=LOCAL,action=load:1->NXM_NX_REG0[[0..15]],goto_table:1 table=0,priority=100,ip,in_port=1,action=load:65534->NXM_NX_REG0[[0..15]],goto_table:1 dnl Ingress pipeline dnl - Allow all connections from LOCAL port (commit and proceed to egress) dnl - All other connections go through conntracker using the input port as dnl a connection tracking zone. table=1,priority=150,in_port=LOCAL,ip,ct_state=+trk+new,action=ct(commit,table=2,zone=OXM_OF_IN_PORT[[0..15]]) table=1,priority=100,ip,action=ct(table=2,zone=OXM_OF_IN_PORT[[0..15]]) table=1,priority=1,action=drop dnl Egress pipeline dnl - Allow all connections from LOCAL port (commit and skip to output) dnl - Allow other established connections to go through conntracker using dnl output port as a connection tracking zone. table=2,priority=150,in_port=LOCAL,ip,ct_state=+trk+new,action=ct(commit,table=4,zone=NXM_NX_REG0[[0..15]]) table=2,priority=100,ip,ct_state=+trk+est,action=ct(table=3,zone=NXM_NX_REG0[[0..15]]) table=2,priority=1,action=drop dnl Only allow established traffic from egress ct lookup table=3,priority=100,ip,ct_state=+trk+est,action=goto_table:4 table=3,priority=1,action=drop dnl output table table=4,priority=100,ip,action=output:NXM_NX_REG0[[]] ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) AT_CHECK([ping -q -c 3 -i 0.3 -W 2 10.1.1.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_START_L7([at_ns0], [http]) dnl HTTP requests from root namespace to p0 should work fine. AT_CHECK(OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) dnl (again) HTTP requests from root namespace to p0 should work fine. AT_CHECK(OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | grep "zone"], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=1 icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0),zone=65534 tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=65534,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - limit by zone]) CHECK_CONNTRACK() OVS_CHECK_GITHUB_ACTION() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,arp,action=normal priority=100,in_port=1,udp,action=ct(zone=1,commit),2 priority=100,in_port=2,udp,action=ct(zone=3,commit),1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Test values out of range for the default limit. dnl Try to set a negative value. AT_CHECK([ovs-appctl dpctl/ct-set-limits default=-1], [2], [ignore], [dnl ovs-vswitchd: invalid default limit (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Try to set UINT32_MAX. AT_CHECK([ovs-appctl dpctl/ct-set-limits default=4294967296], [2], [ignore], [dnl ovs-vswitchd: invalid default limit (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Same range checks for zones. AT_CHECK([ovs-appctl dpctl/ct-set-limits zone=1,limit=-1], [2], [ignore], [dnl ovs-vswitchd: failed to parse field limit (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-set-limits zone=1,limit=4294967296], [2], [ignore], [dnl ovs-vswitchd: failed to parse field limit (Invalid argument) ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Double check no limits have been applied. AT_CHECK([ovs-appctl dpctl/ct-get-limits], [],[dnl default limit=0 ]) m4_define([UDP_PKT], [m4_join([,], [eth_src=50:54:00:00:00:0$1,eth_dst=50:54:00:00:00:0$2,dl_type=0x0800], [nw_src=10.1.1.$1,nw_dst=10.1.1.$2], [nw_proto=17,nw_ttl=64,nw_frag=no], [udp_src=1,udp_dst=$3])]) AT_CHECK([ovs-appctl dpctl/ct-set-limits zone=1,limit=0]) pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([1], [2], [2])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=${pkt} actions=resubmit(,0)"]) dnl Double check the zl entry exists but no connection was added. AT_CHECK([ovs-appctl dpctl/ct-get-limits], [],[dnl default limit=0 zone=1,limit=0,count=0 ]) dnl Remove limit for zone=1. AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=1]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=3]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [],[dnl default limit=3 ]) dnl Send 5 packets from each port in order to hit the default zone limit. for i in $(seq 2 6); do pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([1], [2], [$i])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=${pkt} actions=resubmit(,0)"]) done AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=1], [],[dnl default limit=3 zone=1,limit=3,count=3 ]) for i in $(seq 2 6); do pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([3], [4], [$i])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=${pkt} actions=resubmit(,0)"]) done AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=1,3], [],[dnl default limit=3 zone=1,limit=3,count=3 zone=3,limit=3,count=3 ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10 zone=1,limit=5 zone=2,limit=3 zone=3,limit=3 zone=4,limit=15]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=2,4,5]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=1,2,3,4], [],[dnl default limit=10 zone=1,limit=5,count=0 zone=2,limit=10,count=0 zone=3,limit=3,count=0 zone=4,limit=10,count=0 ]) dnl Test UDP from port 1 for i in $(seq 2 10); do pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([1], [2], [$i])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=${pkt} actions=resubmit(,0)"]) done AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=1,2,3,4,5], [0], [dnl default limit=10 zone=1,limit=5,count=5 zone=2,limit=10,count=0 zone=3,limit=3,count=0 zone=4,limit=10,count=0 zone=5,limit=10,count=0 ]) dnl Test ct-get-limits for all zones AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=10 zone=1,limit=5,count=5 zone=3,limit=3,count=0 ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1," | sort ], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),zone=1 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=3),reply=(src=10.1.1.2,dst=10.1.1.1,sport=3,dport=1),zone=1 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=4),reply=(src=10.1.1.2,dst=10.1.1.1,sport=4,dport=1),zone=1 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=5),reply=(src=10.1.1.2,dst=10.1.1.1,sport=5,dport=1),zone=1 udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=6),reply=(src=10.1.1.2,dst=10.1.1.1,sport=6,dport=1),zone=1 ]) dnl Test UDP from port 2 for i in $(seq 2 6); do pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([3], [4], [$i])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=${pkt} actions=resubmit(,0)"]) done AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=1,3], [0], [dnl default limit=10 zone=1,limit=5,count=5 zone=3,limit=3,count=3 ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3 udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3 udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3 ]) dnl Test ct-del-limits for default zone. AT_CHECK([ovs-appctl dpctl/ct-set-limits default=15 zone=4,limit=4]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=4], [0], [dnl default limit=15 zone=4,limit=4,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits default]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=4], [0], [dnl default limit=0 zone=4,limit=4,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=15]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=4], [0], [dnl default limit=15 zone=4,limit=4,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits default zone=4]) AT_CHECK([ovs-appctl dpctl/ct-get-limits zone=4], [0], [dnl default limit=0 zone=4,limit=0,count=0 ]) dnl Test limit set via database. VSCTL_ADD_DATAPATH_TABLE() AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=1]) AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=3]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=10]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=3]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=10 zone=1,limit=5,count=0 ]) AT_CHECK([ovs-vsctl set-zone-limit $DP_TYPE 1 3]) AT_CHECK([ovs-vsctl set-zone-limit $DP_TYPE 3 3]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl default limit=10 zone=1,limit=3,count=0 zone=3,limit=3,count=0]) for i in $(seq 2 6); do pkt=$(ovs-ofctl compose-packet --bare "UDP_PKT([3], [4], [$i])") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=${pkt} actions=resubmit(,0)"]) done AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.3," | sort ], [0], [dnl udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=2),reply=(src=10.1.1.4,dst=10.1.1.3,sport=2,dport=1),zone=3 udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=3),reply=(src=10.1.1.4,dst=10.1.1.3,sport=3,dport=1),zone=3 udp,orig=(src=10.1.1.3,dst=10.1.1.4,sport=1,dport=4),reply=(src=10.1.1.4,dst=10.1.1.3,sport=4,dport=1),zone=3 ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=10 zone=1,limit=3,count=0 zone=3,limit=3,count=3 ]) AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE 3]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl default limit=10 zone=1,limit=3,count=0]) AT_CHECK([ovs-vsctl set-zone-limit $DP_TYPE default 5]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl default limit=5 zone=1,limit=3,count=0]) AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE default]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/ct-get-limits], [dnl default limit=0 zone=1,limit=3,count=0]) dnl Try to overwrite the zone limit via dpctl command. AT_CHECK([ovs-appctl dpctl/ct-set-limits default=15 zone=3,limit=5 zone=1,limit=5], [2], [ignore], [dnl ovs-vswitchd: the zone limits are set via database, dnl use 'ovs-vsctl set-zone-limit <...>' instead. (Operation not permitted) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=0 zone=1,limit=3,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=1], [2], [ignore], [dnl ovs-vswitchd: the zone limits are set via database, dnl use 'ovs-vsctl del-zone-limit <...>' instead. (Operation not permitted) ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=0 zone=1,limit=3,count=0 ]) AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE 1]) AT_CHECK([ovs-vsctl set-zone-limit $DP_TYPE default 10]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=10 ]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=15 zone=1,limit=5], [2], [ignore], [dnl ovs-vswitchd: the zone limits are set via database, dnl use 'ovs-vsctl set-zone-limit <...>' instead. (Operation not permitted) ovs-appctl: ovs-vswitchd: server returned an error ]) dnl Delete all zones from DB, that should remove the protection. AT_CHECK([ovs-vsctl del-zone-limit $DP_TYPE default]) AT_CHECK([ovs-appctl dpctl/ct-set-limits default=15 zone=1,limit=5]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=15 zone=1,limit=5,count=0 ]) AT_CHECK([ovs-appctl dpctl/ct-del-limits zone=1]) AT_CHECK([ovs-appctl dpctl/ct-get-limits], [0], [dnl default limit=15 ]) OVS_TRAFFIC_VSWITCHD_STOP(["dnl /could not create datapath/d /(Cannot allocate memory) on packet/d"]) AT_CLEANUP AT_SETUP([FTP - no conntrack]) AT_SKIP_IF([test $HAVE_FTP = no]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl table=0,action=normal ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows.txt]) NETNS_DAEMONIZE([at_ns0], [[$PYTHON3 $srcdir/test-l7.py ftp]], [ftp1.pid]) NETNS_DAEMONIZE([at_ns1], [[$PYTHON3 $srcdir/test-l7.py ftp]], [ftp0.pid]) OVS_WAIT_UNTIL([ip netns exec at_ns1 netstat -l | grep ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2], [-o index.html]), [0], [ignore], [ignore]) AT_CHECK([find -name index.html], [0], [dnl ./index.html ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - FTP]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows1.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(alg=ftp,commit),2 table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk+rel,action=1 ]) dnl Similar policy but without allowing all traffic from ns0->ns1. AT_DATA([flows2.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal dnl Allow outgoing TCP connections, and treat them as FTP table=0,priority=100,in_port=1,tcp,action=ct(table=1) table=1,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,alg=ftp),2 table=1,in_port=1,tcp,ct_state=+trk+est,action=2 dnl Allow incoming FTP data connections and responses to existing connections table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1 table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk-new+rel,action=1 ]) dnl flows3 is same as flows1, except no ALG is specified. AT_DATA([flows3.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(commit),2 table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk+rel,action=1 ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows1.txt]) OVS_START_L7([at_ns0], [ftp]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p1->p0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_FTP_ACTIVE([10.1.1.1]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) dnl Try the second set of flows. AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows2.txt]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl FTP requests from p1->p0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_FTP_ACTIVE([10.1.1.1]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl Active FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),protoinfo=(state=) ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Passive FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) dnl Try the third set of flows, without alg specifier. AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows3.txt]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl FTP control requests from p0->p1 should work fine, but helper will not be assigned. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - FTP non-standard port]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows1.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(alg=ftp,commit),2 table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk+rel,action=1 ]) dnl Similar policy but without allowing all traffic from ns0->ns1. AT_DATA([flows2.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal dnl Allow outgoing TCP connections, and treat them as FTP table=0,priority=100,in_port=1,tcp,action=ct(table=1) table=1,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,alg=ftp),2 table=1,in_port=1,tcp,ct_state=+trk+est,action=2 dnl Allow incoming FTP data connections and responses to existing connections table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1 table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk-new+rel,action=1 ]) dnl flows3 is same as flows1, except no ALG is specified. AT_DATA([flows3.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(commit),2 table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk+rel,action=1 ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows1.txt]) OVS_START_L7([at_ns0], [ftp], [11111]) OVS_START_L7([at_ns1], [ftp], [11111]) dnl FTP requests from p1->p0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_FTP_ACTIVE([10.1.1.1:11111]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2:11111]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) dnl Try the second set of flows. AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows2.txt]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl FTP requests from p1->p0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_FTP_ACTIVE([10.1.1.1:11111]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl Active FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2:11111]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),protoinfo=(state=) ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Passive FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2:11111]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) dnl Try the third set of flows, without alg specifier. AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows3.txt]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl FTP control requests from p0->p1 should work fine, but helper will not be assigned. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2:11111]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - FTP with expectation dump]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() CHECK_CONNTRACK_DUMP_EXPECTATIONS() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,tcp,action=ct(alg=ftp,commit),2 table=0,priority=100,in_port=2,tcp,action=ct(table=1) table=1,in_port=2,tcp,ct_state=+trk+est,action=1 table=1,in_port=2,tcp,ct_state=+trk+rel,action=1 ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows.txt]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) dnl Verify that a dump with zero entries in a zone doesn't return any entry. AT_CHECK([ovs-appctl dpctl/dump-conntrack-exp zone=42], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack-exp | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),parent=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - FTP over IPv6]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", [], [], "nodad") ADD_VETH(p1, at_ns1, br0, "fc00::2/96", [], [], "nodad") dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl Track all IPv6 traffic and drop the rest. dnl Allow ICMPv6 both ways. No commit, so pings will not be tracked. table=0 priority=100 in_port=1 icmp6, action=2 table=0 priority=100 in_port=2 icmp6, action=1 table=0 priority=10 ip6, action=ct(table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new TCPv6 FTP control connections from port 1. table=1 in_port=1 ct_state=+new, tcp6, tp_dst=21, action=ct(alg=ftp,commit),2 dnl Allow related TCPv6 connections from port 2. table=1 in_port=2 ct_state=+new+rel, tcp6, action=ct(commit),1 dnl Allow established TCPv6 connections both ways. table=1 in_port=1 ct_state=+est, tcp6, action=2 table=1 in_port=2 ct_state=+est, tcp6, action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([[[[fc00::2]]]], [--ipv6]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=fc00::2,dst=fc00::1,sport=,dport=),reply=(src=fc00::1,dst=fc00::2,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 FTP Passive]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", [], [], "nodad") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "fc00::2/96", [], [], "nodad") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:99]) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::2 lladdr 80:88:88:88:88:99 dev p0]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::1 lladdr 80:88:88:88:88:88 dev p1]) dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl Track all IPv6 traffic and drop the rest. dnl Allow ICMPv6 both ways. No commit, so pings will not be tracked. table=0 priority=100 in_port=1 icmp6, action=2 table=0 priority=100 in_port=2 icmp6, action=1 table=0 priority=10 ip6, action=ct(table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new TCPv6 FTP control connections from port 1. table=1 in_port=1 ct_state=+new, tcp6, tp_dst=21, action=ct(alg=ftp,commit),2 dnl Allow related TCPv6 connections from port 1. table=1 in_port=1 ct_state=+new+rel, tcp6, action=ct(commit),2 dnl Allow established TCPv6 connections both ways. table=1 in_port=1 ct_state=+est, tcp6, action=2 table=1 in_port=2 ct_state=+est, tcp6, action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP passive requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([[[[fc00::2]]]], [--ipv6]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),protoinfo=(state=) tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - FTP with multiple expectations]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Dual-firewall, allow all from ns1->ns2, allow established and ftp ns2->ns1. AT_DATA([flows.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal dnl Traffic from ns1 table=0,priority=100,in_port=1,tcp,action=ct(table=1,zone=1) table=1,in_port=1,tcp,ct_zone=1,ct_state=+trk+new-rel,action=ct(commit,alg=ftp,zone=1),ct(commit,alg=ftp,zone=2),2 table=1,in_port=1,tcp,ct_zone=1,ct_state=+trk+new+rel,action=ct(commit,zone=1),ct(commit,zone=2),2 table=1,in_port=1,tcp,ct_zone=1,ct_state=+trk+est,action=ct(table=2,zone=2) table=2,in_port=1,tcp,ct_zone=2,ct_state=+trk+est,action=2 dnl Traffic from ns2 table=0,priority=100,in_port=2,tcp,action=ct(table=1,zone=2) table=1,in_port=2,tcp,ct_zone=2,ct_state=+trk+rel,action=ct(commit,zone=2),ct(commit,zone=1),1 table=1,in_port=2,tcp,ct_zone=2,ct_state=+trk+est,action=ct(table=2,zone=1) table=2,in_port=2,tcp,ct_zone=1,ct_state=+trk+rel,action=ct(commit,zone=2),ct(commit,zone=1),1 table=2,in_port=2,tcp,ct_zone=1,ct_state=+trk+est,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns0], [ftp]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p1->p0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. NS_CHECK_EXEC([at_ns1], OVS_GET_FTP_ACTIVE([10.1.1.1]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl Active FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),zone=1,protoinfo=(state=) tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),zone=2,protoinfo=(state=) ]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl Passive FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=2,protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - TFTP]) AT_SKIP_IF([test $HAVE_TFTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows1.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal table=0,priority=100,in_port=1,udp,action=ct(alg=tftp,commit),2 table=0,priority=100,in_port=2,udp,action=ct(table=1) table=1,in_port=2,udp,ct_state=+trk+est,action=1 table=1,in_port=2,udp,ct_state=+trk+rel,action=1 ]) dnl Similar policy but without allowing all traffic from ns0->ns1. AT_DATA([flows2.txt], [dnl table=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=10,icmp,action=normal dnl Allow outgoing UDP connections, and treat them as TFTP table=0,priority=100,in_port=1,udp,action=ct(table=1) table=1,in_port=1,udp,ct_state=+trk+new-rel,action=ct(commit,alg=tftp),2 table=1,in_port=1,udp,ct_state=+trk+new+rel,action=ct(commit),2 table=1,in_port=1,udp,ct_state=+trk+est,action=2 dnl Allow incoming TFTP data connections and responses to existing connections table=0,priority=100,in_port=2,udp,action=ct(table=1) table=1,in_port=2,udp,ct_state=+trk+est,action=1 table=1,in_port=2,udp,ct_state=+trk+new+rel,action=1 ]) AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows1.txt]) OVS_START_L7([at_ns0], [tftp]) OVS_START_L7([at_ns1], [tftp]) dnl TFTP requests from p1->p0 should fail due to network failure. NS_CHECK_EXEC([at_ns1], [[curl $CURL_OPT tftp://10.1.1.1/flows1.txt -o foo 2>curl0.log]], [28]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl TFTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], [[curl $CURL_OPT tftp://10.1.1.2/flows1.txt -o foo 2>curl1.log]]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),helper=tftp ]) dnl Try the second set of flows. AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows2.txt]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) dnl TFTP requests from p1->p0 should fail due to network failure. NS_CHECK_EXEC([at_ns1], [[curl $CURL_OPT tftp://10.1.1.1/flows1.txt -o foo 2>curl2.log]], [28]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl ]) dnl TFTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], [[curl $CURL_OPT tftp://10.1.1.2/flows1.txt -o foo 2>curl3.log]]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),helper=tftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([conntrack - NAT]) AT_SETUP([conntrack - simple SNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl in_port=1,ip,action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.254)),2 in_port=2,ct_state=-trk,ip,action=ct(table=0,zone=1,nat) in_port=2,ct_state=+trk,ct_zone=1,ip,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl HTTP requests from p0->p1 should work fine. OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/' | uniq], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - SNAT with ct_mark change on reply]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.240 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl in_port=1,ip,action=ct(commit,zone=1,nat(src=10.1.1.240)),2 in_port=2,ct_state=-trk,ip,action=ct(table=0,zone=1,nat) dnl dnl Setting the mark fails if the datapath can't find the existing conntrack dnl entry after NAT has been reversed and the skb was lost due to an upcall. dnl in_port=2,ct_state=+trk,ct_zone=1,ip,action=ct(table=1,commit,zone=1,exec(set_field:1->ct_mark)),1 table=1,in_port=2,ct_mark=1,ct_state=+rpl,ct_zone=1,ip,action=1 dnl priority=0,action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl ICMP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], [ping -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.2XX,id=,type=0,code=0),zone=1,mark=1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - SNAT with port range]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl in_port=1,tcp,action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.254:34567-34568,random)),2 in_port=2,ct_state=-trk,tcp,tp_dst=34567,action=ct(table=0,zone=1,nat) in_port=2,ct_state=-trk,tcp,tp_dst=34568,action=ct(table=0,zone=1,nat) in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl HTTP requests from p0->p1 should work fine. OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/' | uniq], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - SNAT with port range using ICMP]) dnl Check PAT is not attempted on ICMP packets causing corrupted packets. CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl in_port=1,ip,action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.254:20000)),2 in_port=2,ct_state=-trk,ip,action=ct(table=0,zone=1,nat) in_port=2,ct_state=+trk,ct_zone=1,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl ICMP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], [ping -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.2XX,id=,type=0,code=0),zone=1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - SNAT with port range with exhaustion]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:89:89:89:89:89]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl in_port=1,tcp,action=ct(commit,zone=1,nat(src=10.1.1.240:34568)),2 in_port=2,ct_state=-trk,tcp,tp_dst=34568,action=ct(table=0,zone=1,nat) in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl HTTP requests from p0->p1 should work fine. OVS_START_L7([at_ns1], [http]) dnl Send a valid SYN to make conntrack pick it up. dnl The source port used is 123 to prevent unwanted reuse in the next HTTP request. syn_pkt=$(ovs-ofctl compose-packet --bare "eth_src=80:88:88:88:88:88,eth_dst=80:89:89:89:89:89,\ dl_type=0x0800,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_proto=6,nw_ttl=64,nw_frag=no,tcp_flags=syn,\ tcp_src=123,tcp_dst=80") AT_CHECK([ovs-ofctl packet-out br0 "packet=${syn_pkt} actions=ct(commit,zone=1,nat(src=10.1.1.240:34568))"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | uniq], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),zone=1,protoinfo=(state=) ]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [28], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | uniq], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP(["dnl /Unable to NAT due to tuple space exhaustion - if DoS attack, use firewalling and\/or zone partitioning./d /Dropped .* log messages in last .* seconds \(most recently, .* seconds ago\) due to excessive rate/d /|WARN|.* execute ct.* failed/d"]) AT_CLEANUP AT_SETUP([conntrack - more complex SNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl dnl Track all IP traffic, NAT existing connections. priority=100 ip action=ct(table=1,zone=1,nat) dnl dnl Allow ARP, but generate responses for NATed addresses priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0 action=drop dnl dnl Allow any traffic from ns0->ns1. SNAT ns0 to 10.1.1.240-10.1.1.254 table=1 priority=100 in_port=1 ip ct_state=+trk+new-est action=ct(commit,zone=1,nat(src=10.1.1.240-10.1.1.254)),2 table=1 priority=100 in_port=1 ip ct_state=+trk-new+est action=2 dnl Only allow established traffic from ns1->ns0. table=1 priority=100 in_port=2 ip ct_state=+trk-new+est action=1 table=1 priority=0 action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8 priority=100 reg2=0x0a0101f0/0xfffffff0 action=load:0x808888888888->OXM_OF_PKT_REG0[[]] dnl Zero result means not found. table=8 priority=0 action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl ARP TPA IP in reg2. table=10 priority=100 arp xreg0=0 action=normal dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=10 arp arp_op=1 action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl HTTP requests from p0->p1 should work fine. OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/' | uniq], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - all-zero IP SNAT]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_ZEROIP_SNAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ip route add 172.1.1.0/24 via 10.1.1.2]) OVS_START_L7([at_ns1], [http]) AT_DATA([flows.txt], [dnl table=0,priority=30,ct_state=-trk,ip,action=ct(table=0) table=0,priority=20,ct_state=-rpl,ip,nw_dst=10.1.1.0/24,actions=ct(commit,nat(src=0.0.0.0),table=10) table=0,priority=20,ct_state=+rpl,ip,nw_dst=10.1.1.0/24,actions=resubmit(,10) table=0,priority=20,ip,nw_dst=172.1.1.2,actions=ct(commit,nat(dst=10.1.1.2),table=10) table=0,priority=10,arp,action=normal table=0,priority=1,action=drop table=10,priority=20,ct_state=+rpl,ip,nw_dst=10.1.1.0/24 actions=ct(table=20,nat) table=10,priority=10,ip,nw_dst=10.1.1.0/24 actions=resubmit(,20) table=20,priority=10,ip,nw_dst=10.1.1.1,action=1 table=20,priority=10,ip,nw_dst=10.1.1.2,action=2 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl - Test to make sure src nat is NOT done when not needed NS_CHECK_EXEC([at_ns0], [echo "TEST" | nc -p 30000 10.1.1.2 80 > nc-1.log]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=30000,dport=80),reply=(src=10.1.1.2,dst=10.1.1.1,sport=80,dport=30000),protoinfo=(state=TIME_WAIT) ]) dnl - Test to make sure src nat is done when needed NS_CHECK_EXEC([at_ns0], [echo "TEST2" | nc -p 30001 172.1.1.2 80 > nc-2.log]) NS_CHECK_EXEC([at_ns0], [echo "TEST3" | nc -p 30001 10.1.1.2 80 > nc-3.log]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 30001 | grep "orig=.src=10\.1\.1\.1," | sed -e 's/port=30001/port=/g' -e 's/sport=80,dport=[[0-9]]\+/sport=80,dport=/g' | sort], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=80),reply=(src=10.1.1.2,dst=10.1.1.1,sport=80,dport=),protoinfo=(state=TIME_WAIT) tcp,orig=(src=10.1.1.1,dst=172.1.1.2,sport=,dport=80),reply=(src=10.1.1.2,dst=10.1.1.1,sport=80,dport=),protoinfo=(state=TIME_WAIT) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - simple DNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:88]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=100 in_port=1,ip,nw_dst=10.1.1.64,action=ct(zone=1,nat(dst=10.1.1.2),commit),2 priority=10 in_port=1,ip,action=ct(commit,zone=1),2 priority=100 in_port=2,ct_state=-trk,ip,action=ct(table=0,nat,zone=1) priority=100 in_port=2,ct_state=+trk+est,ct_zone=1,ip,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] dnl Zero result means not found. table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. table=10 priority=100 arp xreg0=0 action=normal dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Should work with the virtual IP address through NAT OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.64]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.64)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.64,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) dnl Should work with the assigned IP address as well NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - DNAT with additional SNAT]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns0], [ip route add 172.1.1.0/24 via 10.1.1.2]) OVS_START_L7([at_ns1], [http]) AT_DATA([flows.txt], [dnl table=0,priority=30,in_port=1,ip,nw_dst=172.1.1.2,actions=ct(commit,nat(dst=10.1.1.2:80),table=1) table=0,priority=20,in_port=2,ip,actions=ct(nat),1 table=0,priority=10,arp,actions=NORMAL table=0,priority=1,actions=drop dnl Be sure all ct() actions but src nat are executed table=1,ip,actions=ct(commit,nat(src=10.1.1.240),exec(set_field:0xac->ct_mark,set_field:0xac->ct_label),table=2) table=2,in_port=1,ip,ct_mark=0xac,ct_label=0xac,actions=2 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([http://172.1.1.2:8080]), [0], [ignore], [ignore]) dnl - make sure only dst nat has been performed AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.240)], [0], [dnl ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=172.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),mark=172,labels=0xac,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - more complex DNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:88]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl Track all IP traffic table=0 priority=100 ip action=ct(table=1,zone=1,nat) dnl dnl Allow ARP, but generate responses for NATed addresses table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Allow any IP traffic from ns0->ns1. DNAT ns0 from 10.1.1.64 to 10.1.1.2 table=1 priority=100 in_port=1 ct_state=+new ip nw_dst=10.1.1.64 action=ct(zone=1,nat(dst=10.1.1.2),commit),2 table=1 priority=10 in_port=1 ct_state=+new ip action=ct(commit,zone=1),2 table=1 priority=100 in_port=1 ct_state=+est ct_zone=1 action=2 dnl Only allow established traffic from ns1->ns0. table=1 priority=100 in_port=2 ct_state=+est ct_zone=1 action=1 table=1 priority=0 action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] dnl Zero result means not found. table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. table=10 priority=100 arp xreg0=0 action=normal dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Should work with the virtual IP address through NAT OVS_START_L7([at_ns1], [http]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.64]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.64)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.64,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) dnl Should work with the assigned IP address as well NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([10.1.1.2]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related with NAT]) AT_SKIP_IF([test $HAVE_NC = no]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) CHECK_NO_DPDK_OFFLOAD() CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow UDP traffic from ns0->ns1. Only allow related ICMP responses back. dnl Make sure ICMP responses are reverse-NATted. AT_DATA([flows.txt], [dnl in_port=1,udp,action=ct(commit,nat(src=10.1.1.240-10.1.1.254),exec(set_field:1->ct_mark)),2 in_port=2,icmp,ct_state=-trk,action=ct(table=0,nat) in_port=2,icmp,nw_dst=10.1.1.1,ct_state=+trk+rel,ct_mark=1,action=1 dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) rm p0.pcap OVS_DAEMONIZE([tcpdump -n -U -i ovs-p0 -w p0.pcap], [tcpdump.pid]) sleep 1 dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response. NS_CHECK_EXEC([at_ns0], [echo a | nc $NC_EOF_OPT -u 10.1.1.2 10000]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort | grep -v drop], [0], [dnl n_packets=1, n_bytes=42, priority=10,arp actions=NORMAL n_packets=1, n_bytes=44, udp,in_port=1 actions=ct(commit,nat(src=10.1.1.240-10.1.1.254),exec(set_field:0x1->ct_mark)),output:2 n_packets=1, n_bytes=72, ct_state=+rel+trk,ct_mark=0x1,icmp,in_port=2,nw_dst=10.1.1.1 actions=output:1 n_packets=1, n_bytes=72, ct_state=-trk,icmp,in_port=2 actions=ct(table=0,nat) n_packets=2, n_bytes=84, priority=100,arp,arp_op=1 actions=move:NXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=10, n_packets=1, n_bytes=42, priority=10,arp,arp_op=1 actions=set_field:2->arp_op,move:NXM_NX_ARP_SHA[[]]->NXM_NX_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_NX_ARP_SHA[[]],move:NXM_OF_ARP_SPA[[]]->NXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->NXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],set_field:0->in_port,output:NXM_NX_REG3[[0..15]] table=10, n_packets=1, n_bytes=42, priority=100,arp,reg0=0,reg1=0 actions=NORMAL table=8, n_packets=1, n_bytes=42, priority=0 actions=set_field:0->xreg0 table=8, n_packets=1, n_bytes=42, reg2=0xa0101f0/0xfffffff0 actions=set_field:0x808888888888->xreg0 OFPST_FLOW reply (OF1.5): ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=),mark=1 ]) AT_CHECK([tcpdump -n -v "icmp" -r p0.pcap 2>/dev/null | grep -E 'wrong|bad'], [1], [ignore-nolog]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP related with SNAT]) AT_SKIP_IF([test $HAVE_NC = no]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) OVS_CHECK_MIN_KERNEL(6, 7) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow IP traffic from ns0->ns1, rewrite source IP with SNAT to 10.1.1.254. dnl Only allow related ICMP responses back and undo NAT to restore original IP. AT_DATA([flows.txt], [dnl ct_state=-trk,ip actions=ct(table=0) ct_state=+trk,ip,in_port=1 actions=ct(commit,nat(src=10.1.1.254)),2 ct_state=+rel+trk,icmp,in_port=2,nw_dst=10.1.1.254 actions=ct(commit,table=1,nat) dnl dnl Handle ICMP related packets. dnl These should match first rule with original IPs before SNAT. dnl The second rule, which matches on the SNAT IP, shouldn't match any packets. table=1,in_port=2,ct_state=+rel+trk,icmp,nw_src=10.1.1.2,nw_dst=10.1.1.1 action=1 table=1,in_port=2,ct_state=+rel+trk,icmp,nw_dst=10.1.1.254 action=goto_table:2 table=1,priority=0,action=drop dnl dnl Drop any ICMP related packets that incorrectly reach this table. table=2,priority=0,action=drop dnl dnl ARP priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 priority=10 arp action=normal priority=0,action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) rm p0.pcap OVS_DAEMONIZE([tcpdump -n -U -i ovs-p0 -w p0.pcap], [tcpdump.pid]) sleep 1 dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response. NS_CHECK_EXEC([at_ns0], [echo a | nc $NC_EOF_OPT -u 10.1.1.2 10000]) dnl Flush conntrack state. dnl To verify related packets are handled exactly the same as before flushing. AT_CHECK([ovs-appctl dpctl/flush-conntrack], [0]) dnl Solicit another "destination unreachable" response. dnl To verify that after flushing, the same openflow rules are matched. NS_CHECK_EXEC([at_ns0], [echo a | nc $NC_EOF_OPT -u 10.1.1.2 10000]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/'], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=) ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | ofctl_strip_bytes | sort | grep -v drop], [0], [dnl n_packets=1, priority=10,arp actions=NORMAL n_packets=2, ct_state=+rel+trk,icmp,in_port=2,nw_dst=10.1.1.254 actions=ct(commit,table=1,nat) n_packets=2, ct_state=+trk,ip,in_port=1 actions=ct(commit,nat(src=10.1.1.254)),output:2 n_packets=2, priority=100,arp,arp_op=1 actions=move:NXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 n_packets=4, ct_state=-trk,ip actions=ct(table=0) table=1, ct_state=+rel+trk,icmp,in_port=2,nw_dst=10.1.1.254 actions=goto_table:2 table=1, n_packets=2, ct_state=+rel+trk,icmp,in_port=2,nw_src=10.1.1.2,nw_dst=10.1.1.1 actions=output:1 table=10, n_packets=1, priority=10,arp,arp_op=1 actions=set_field:2->arp_op,move:NXM_NX_ARP_SHA[[]]->NXM_NX_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_NX_ARP_SHA[[]],move:NXM_OF_ARP_SPA[[]]->NXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->NXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],set_field:0->in_port,output:NXM_NX_REG3[[0..15]] table=10, n_packets=1, priority=100,arp,reg0=0,reg1=0 actions=NORMAL table=8, n_packets=1, priority=0 actions=set_field:0->xreg0 table=8, n_packets=1, reg2=0xa0101f0/0xfffffff0 actions=set_field:0x808888888888->xreg0 OFPST_FLOW reply (OF1.5): ]) AT_CHECK([tcpdump -n -v "icmp" -r p0.pcap 2>/dev/null | grep -E 'wrong|bad'], [1], [ignore-nolog]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP dnl CHECK_FTP_NAT(TITLE, IP_ADDR, FLOWS, CT_DUMP) dnl dnl Checks the implementation of conntrack with FTP ALGs in combination with dnl NAT, using the provided flow table. m4_define([CHECK_FTP_NAT], [AT_SETUP([conntrack - FTP $1]) AT_SKIP_IF([test $HAVE_FTP = no]) AT_SKIP_IF([test $HAVE_LFTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [$3]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. AT_DATA([ftp.cmd], [dnl set net:max-retries 1 set net:timeout 1 set ftp:passive-mode off cache off connect ftp://anonymous:@10.1.1.2 ls ls ls ls ]) NS_CHECK_EXEC([at_ns0], [lftp -f ftp.cmd > lftp.log]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [$4]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP]) dnl CHECK_FTP_SNAT_PRE_RECIRC(TITLE, IP_ADDR, IP_ADDR_AS_HEX) dnl dnl Checks the implementation of conntrack with FTP ALGs in combination with dnl NAT, with flow tables that implement the NATing as part of handling of dnl initial incoming packets - ie, the first flow is ct(nat,table=foo). dnl dnl IP_ADDR must specify the NAT address in standard "10.1.1.x" format, dnl and IP_ADDR_AS_HEX must specify the same address as hex, eg 0x0a0101xx. m4_define([CHECK_FTP_SNAT_PRE_RECIRC], [dnl CHECK_FTP_NAT([SNAT prerecirc $1], [$2], [dnl dnl track all IP traffic, de-mangle non-NEW connections table=0 in_port=1, ip, action=ct(table=1,nat) table=0 in_port=2, ip, action=ct(table=2,nat) dnl dnl ARP dnl table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Table 1: port 1 -> 2 dnl dnl Allow new FTP connections. These need to be commited. table=1 ct_state=+new, tcp, tp_dst=21, nw_src=10.1.1.1, action=ct(alg=ftp,commit,nat(src=$2)),2 dnl Allow established TCP connections, make sure they are NATted already. table=1 ct_state=+est, tcp, nw_src=$2, action=2 dnl dnl Table 1: droppers dnl table=1 priority=10, tcp, action=drop table=1 priority=0,action=drop dnl dnl Table 2: port 2 -> 1 dnl dnl Allow established TCP connections, make sure they are reverse NATted table=2 ct_state=+est, tcp, nw_dst=10.1.1.1, action=1 dnl Allow (new) related (data) connections. These need to be commited. table=2 ct_state=+new+rel, tcp, nw_dst=$2, action=ct(commit,nat),1 dnl Allow related ICMP packets, make sure they are reverse NATted table=2 ct_state=+rel, icmp, nw_dst=10.1.1.1, action=1 dnl dnl Table 2: droppers dnl table=2 priority=10, tcp, action=drop table=2 priority=0, action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 dnl table=8,reg2=$3/0xffffffff,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=$2,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=$2,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),protoinfo=(state=) ]) ]) dnl Check that ct(nat,table=foo) works without TCP sequence adjustment. CHECK_FTP_SNAT_PRE_RECIRC([], [10.1.1.9], [0x0a010109]) dnl Check that ct(nat,table=foo) works with TCP sequence adjustment. dnl dnl The FTP PORT command includes the ASCII representation of the address, dnl so when these messages need to be NATed between addresses that have dnl different lengths when represented in ASCII (such as the original address dnl of 10.1.1.1 used in the test and 10.1.1.240 here), the FTP NAT ALG must dnl resize the packet and adjust TCP sequence numbers. This test is kept dnl separate from the above to easier identify issues in this code on different dnl kernels. CHECK_FTP_SNAT_PRE_RECIRC([seqadj], [10.1.1.240], [0x0a0101f0]) dnl CHECK_FTP_SNAT_POST_RECIRC(TITLE, IP_ADDR, IP_ADDR_AS_HEX) dnl dnl Checks the implementation of conntrack with FTP ALGs in combination with dnl NAT, with flow tables that implement the NATing after the first round dnl of recirculation - that is, the first flow ct(table=foo) then a subsequent dnl flow will implement the NATing with ct(nat..),output:foo. dnl dnl IP_ADDR must specify the NAT address in standard "10.1.1.x" format, dnl and IP_ADDR_AS_HEX must specify the same address as hex, eg 0x0a0101xx. m4_define([CHECK_FTP_SNAT_POST_RECIRC], [dnl CHECK_FTP_NAT([SNAT postrecirc $1], [$2], [dnl dnl track all IP traffic (this includes a helper call to non-NEW packets.) table=0 ip, action=ct(table=1) dnl dnl ARP dnl table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP connections. These need to be commited. dnl This does helper for new packets. table=1 in_port=1 ct_state=+new, tcp, tp_dst=21, action=ct(alg=ftp,commit,nat(src=$2)),2 dnl Allow and NAT established TCP connections table=1 in_port=1 ct_state=+est, tcp, action=ct(nat),2 table=1 in_port=2 ct_state=+est, tcp, action=ct(nat),1 dnl Allow and NAT (new) related active (data) connections. dnl These need to be commited. table=1 in_port=2 ct_state=+new+rel, tcp, action=ct(commit,nat),1 dnl Allow related ICMP packets. table=1 in_port=2 ct_state=+rel, icmp, action=ct(nat),1 dnl Drop everything else. table=1 priority=0, action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 dnl table=8,reg2=$3/0xffffffff,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=$2,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=$2,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),protoinfo=(state=) ]) ]) dnl Check that ct(nat,table=foo) works without TCP sequence adjustment. CHECK_FTP_SNAT_POST_RECIRC([], [10.1.1.9], [0x0a010109]) dnl Check that ct(nat,table=foo) works with TCP sequence adjustment. dnl dnl The FTP PORT command includes the ASCII representation of the address, dnl so when these messages need to be NATed between addresses that have dnl different lengths when represented in ASCII (such as the original address dnl of 10.1.1.1 used in the test and 10.1.1.240 here), the FTP NAT ALG must dnl resize the packet and adjust TCP sequence numbers. This test is kept dnl separate from the above to easier identify issues in this code on different dnl kernels. CHECK_FTP_SNAT_POST_RECIRC([seqadj], [10.1.1.240], [0x0a0101f0]) dnl CHECK_FTP_SNAT_ORIG_TUPLE(TITLE, IP_ADDR, IP_ADDR_AS_HEX) dnl dnl Checks the implementation of conntrack original direction tuple matching dnl with FTP ALGs in combination with NAT, with flow tables that implement dnl the NATing before the first round of recirculation - that is, the first dnl flow ct(nat, table=foo) then a subsequent flow will implement the dnl commiting of NATed and other connections with ct(nat..),output:foo. dnl dnl IP_ADDR must specify the NAT address in standard "10.1.1.x" format, dnl and IP_ADDR_AS_HEX must specify the same address as hex, eg 0x0a0101xx. m4_define([CHECK_FTP_SNAT_ORIG_TUPLE], [dnl CHECK_FTP_NAT([SNAT orig tuple $1], [$2], [dnl dnl Store zone in reg4 and packet direction in reg3 (IN=1, OUT=2). dnl NAT is only applied to OUT-direction packets, so that ACL dnl processing can be done with non-NATted headers. dnl dnl Track all IP traffic in the IN-direction (IN from Port 1). table=0 in_port=1, ip, action=set_field:1->reg4,set_field:1->reg3,ct(zone=NXM_NX_REG4[[0..15]],table=1) dnl Track all IP traffic in the OUT-direction (OUT to the Port 1). table=0 in_port=2, ip, action=set_field:1->reg4,set_field:2->reg3,ct(zone=NXM_NX_REG4[[0..15]],nat,table=1) dnl dnl ARP dnl table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Pass tracked traffic through ACL, drop everything else. dnl Non-REPLY/RELATED packets get the ACL lookup with the packet headers dnl in the actual packet direction in reg0 (IN=1, OUT=2). REPLY packets dnl get the ACL lookup using the conntrack tuple and the inverted direction. dnl RELATED packets get ACL lookup using the conntrack tuple in the direction dnl of the parent connection, as stored in ct_label[0]. dnl dnl Incoming non-related packet in the original direction (ACL IN) table=1 reg3=1, ip, ct_state=-rel-rpl+trk-inv action=set_field:1->reg0,resubmit(,3),goto_table:5 dnl Incoming non-related reply packet (CT ACL OUT) table=1 reg3=1, ip, ct_state=-rel+rpl+trk-inv action=set_field:2->reg0,resubmit(,3,ct),goto_table:4 dnl Outgoing non-related packet (ACL OUT) table=1 reg3=2, ip, ct_state=-rel-rpl+trk-inv action=set_field:2->reg0,resubmit(,3),goto_table:5 dnl Outgoing non-related reply packet (CT ACL IN) table=1 reg3=2, ip, ct_state=-rel+rpl+trk-inv action=set_field:1->reg0,resubmit(,3,ct),goto_table:4 dnl dnl Related packet (CT ACL in the direction of the parent connection.) table=1 ip, ct_state=+rel+trk-inv, action=move:NXM_NX_CT_LABEL[[0]]->NXM_NX_REG0[[0]],resubmit(,3,ct),goto_table:4 dnl Drop everything else. table=1 priority=0, action=drop dnl dnl "ACL table" dnl dnl Stateful accept (1->reg2) all incoming (reg0=1) IP connections with dnl IP source address '10.1.1.1'. Store rule ID (1234) in reg1, verdict dnl in reg2. table=3 priority=10, reg0=1, ip, nw_src=10.1.1.1 action=set_field:1234->reg1,set_field:1->reg2 dnl Stateless drop (0->reg2) everything else in both directions. (Rule ID: 1235) table=3 priority=0, action=set_field:1235->reg1,set_field:0->reg2 dnl dnl Re-process stateful traffic that was not accepted by a stateful rule as dnl normal traffic in the current direction. This should also delete the dnl now stale conntrack state, so that new state can be created in it's place. dnl dnl Stateful accepts go to next table. table=4 priority=100 reg2=1, action=goto_table:5 dnl Everything else is reprocessed disregarding the CT state, using the actual dnl packet direction. table=4 priority=0 action=move:NXM_NX_REG3[[]]->NXM_NX_REG0[[]],resubmit(,3),goto_table:5 dnl dnl "ACL verdict processing table." dnl dnl Handle stateful (reg2=1) / stateless (reg2=2) accepts and drops (reg2=0) dnl dnl Drop all non-accepted packets. table=5 reg2=0 priority=1000 action=drop dnl dnl Commit new incoming FTP control connections with SNAT range. Must match on dnl 'tcp' when setting 'alg=ftp'. Store the directionality of non-related dnl connections to ct_label[0] Store the rule ID to ct_label[96..127]. table=5 priority=100 reg2=1 reg3=1 ct_state=+new-rel, tcp, tp_dst=21, action=ct(zone=NXM_NX_REG4[[0..15]],alg=ftp,commit,nat(src=$2),exec(move:NXM_NX_REG3[[0]]->NXM_NX_CT_LABEL[[0]],move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl Commit other new incoming non-related IP connections with SNAT range. table=5 priority=10 reg2=1 reg3=1 ct_state=+new-rel, ip, action=ct(zone=NXM_NX_REG4[[0..15]],commit,nat(src=$2),exec(move:NXM_NX_REG3[[0]]->NXM_NX_CT_LABEL[[0]],move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl Commit non-related outgoing new IP connections with DNAT range. dnl (This should not get any packets in this test.) table=5 priority=10 reg2=1 reg3=2 ct_state=+new-rel, ip, action=ct(zone=NXM_NX_REG4[[0..15]],commit,nat(dst=$2),exec(move:NXM_NX_REG3[[0]]->NXM_NX_CT_LABEL[[0]],move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl Commit new related connections in either direction, which need 'nat' dnl and which inherit the label (the direction of the original direction dnl parent tuple) from the parent connection. table=5 priority=10 reg2=1 ct_state=+new+rel, ip, action=ct(zone=NXM_NX_REG4[[0..15]],commit,nat,exec(move:NXM_NX_REG1[[0..31]]->NXM_NX_CT_LABEL[[96..127]])),goto_table:6 dnl dnl NAT incoming non-NEW packets. Outgoing packets were NATted in table 0. dnl table=5 priority=10 ct_state=-new+trk-inv reg3=1 ip, action=ct(zone=NXM_NX_REG4[[0..15]],nat),goto_table:6 dnl Forward everything else, including stateless accepts. table=5 priority=0 action=goto_table:6 dnl dnl "Forwarding table" dnl table=6 in_port=1 action=2 table=6 in_port=2 action=1 dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 dnl table=8,reg2=$3,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=100 arp xreg0=0 action=normal table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=drop ], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=$2,sport=,dport=),zone=1,labels=0x4d2000000000000000000000001,protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=$2,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),zone=1,labels=0x4d2000000000000000000000001,protoinfo=(state=) ]) ]) dnl Check that ct(nat,table=foo) works without TCP sequence adjustment with dnl an ACL table based on matching on conntrack original direction tuple only. CHECK_FTP_SNAT_ORIG_TUPLE([], [10.1.1.9], [0x0a010109]) dnl Check that ct(nat,table=foo) works with TCP sequence adjustment with dnl an ACL table based on matching on conntrack original direction tuple only. CHECK_FTP_SNAT_ORIG_TUPLE([seqadj], [10.1.1.240], [0x0a0101f0]) AT_SETUP([conntrack - IPv4 FTP Passive with SNAT]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.240 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic and NAT any established traffic. table=0 priority=10 ip, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP control connections. table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21 action=ct(alg=ftp,commit,nat(src=10.1.1.240)),2 dnl Allow related TCP connections from port 1. table=1 in_port=1 ct_state=+new+rel tcp nw_src=10.1.1.1 action=ct(commit,nat),2 dnl Allow established TCP connections both ways, post-NAT match. table=1 in_port=1 ct_state=+est tcp nw_src=10.1.1.240 action=2 table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine, with and without EPSV. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2], [--epsv]), [0], [ignore], [ignore]) NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2], [--disable-epsv]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 FTP Passive with DNAT]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.240 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.240/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic and NAT any established traffic. table=0 priority=10 ip, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP control connections. table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21 action=ct(alg=ftp,commit,nat(dst=10.1.1.240)),2 dnl Allow related TCP connections from port 1. table=1 in_port=1 ct_state=+new+rel tcp nw_src=10.1.1.1 action=ct(commit,nat),2 dnl Allow established TCP connections both ways, post-NAT match. table=1 in_port=1 ct_state=+est tcp nw_dst=10.1.1.240 action=2 table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.240 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.2]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.240,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.240,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 FTP Passive with DNAT 2]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/16") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.200 e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.100.1 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.100.1/16") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic and NAT any established traffic. table=0 priority=10 ip, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP control connections. table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21 action=ct(alg=ftp,commit,nat(dst=10.1.100.1)),2 dnl Allow related TCP connections from port 1. table=1 in_port=1 ct_state=+new+rel tcp nw_src=10.1.1.1 action=ct(commit,nat),2 dnl Allow established TCP connections both ways, post-NAT match. table=1 in_port=1 ct_state=+est tcp nw_dst=10.1.100.1 action=2 table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.100.1 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([10.1.1.200]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.200)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.200,sport=,dport=),reply=(src=10.1.100.1,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.200,sport=,dport=),reply=(src=10.1.100.1,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 FTP Active with DNAT]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.240 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.240/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic and NAT any established traffic. table=0 priority=10 ip, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP control connections. table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21 action=ct(alg=ftp,commit,nat(dst=10.1.1.240)),2 dnl Allow related TCP connections from port 1. table=1 in_port=2 ct_state=+new+rel tcp nw_src=10.1.1.240 action=ct(commit,nat),1 dnl Allow established TCP connections both ways, post-NAT match. table=1 in_port=1 ct_state=+est tcp nw_dst=10.1.1.240 action=2 table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.240 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine with and without EPRT. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2], [--eprt]), [0], [ignore], [ignore]) NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.1.2], [--disable-eprt]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.240,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.240,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 FTP Active with DNAT with reverse skew]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/16") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.120.240 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/16") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic and NAT any established traffic. table=0 priority=10 ip, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new FTP control connections. table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21 action=ct(alg=ftp,commit,nat(dst=10.1.1.2)),2 dnl Allow related TCP connections from port 1. table=1 in_port=2 ct_state=+new+rel tcp nw_src=10.1.1.2 action=ct(commit,nat),1 dnl Allow established TCP connections both ways, post-NAT match. table=1 in_port=1 ct_state=+est tcp nw_dst=10.1.1.2 action=2 table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([10.1.120.240]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.120.240)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.120.240,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.120.240,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 HTTP with SNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "fc00::2/96") NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::241 lladdr 80:88:88:88:88:88 dev p1]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=1,action=drop priority=10,icmp6,action=normal priority=100,in_port=1,ip6,action=ct(commit,nat(src=fc00::240-fc00::241)),2 priority=100,in_port=2,ct_state=-trk,ip6,action=ct(nat,table=0) priority=100,in_port=2,ct_state=+trk+est,ip6,action=1 priority=200,in_port=2,ct_state=+trk+new,icmp6,icmpv6_code=0,icmpv6_type=135,nd_target=fc00::240,action=ct(commit,nat(dst=fc00::1)),1 priority=200,in_port=2,ct_state=+trk+new,icmp6,icmpv6_code=0,icmpv6_type=135,nd_target=fc00::241,action=ct(commit,nat(dst=fc00::1)),1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) dnl HTTP requests from ns0->ns1 should work fine. OVS_START_L7([at_ns1], [http6]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([[http://[[fc00::2]]]]), [0], [ignore], [ignore]) dnl HTTP requests from ns1->ns0 should fail due to network failure. dnl Try 3 times, in 1 second intervals. OVS_START_L7([at_ns0], [http6]) NS_CHECK_EXEC([at_ns1], OVS_GET_HTTP([[http://[[fc00::1]]]]), [28], [ignore], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 HTTP with DNAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") NS_CHECK_EXEC([at_ns0], [ip -6 link set dev p0 address 80:88:88:88:88:77]) NS_CHECK_EXEC([at_ns1], [ip -6 link set dev p1 address 80:88:88:88:88:88]) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p0]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::1 lladdr 80:88:88:88:88:77 dev p1]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=100 in_port=1,ip6,ipv6_dst=fc00::240,action=ct(zone=1,nat(dst=fc00::2),commit),2 priority=100 in_port=2,ct_state=-trk,ip6,action=ct(table=0,nat,zone=1) priority=100 in_port=2,ct_state=+trk+est,ct_zone=1,ip6,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::240]) NS_CHECK_EXEC([at_ns0], [ping6 -q -c 3 -i 0.3 -W 2 fc00::240 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Should work with the virtual IP address through NAT OVS_START_L7([at_ns1], [http6]) NS_CHECK_EXEC([at_ns0], OVS_GET_HTTP([[http://[[fc00::240]]]]), [0], [ignore], [ignore]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::1)], [0], [dnl icmpv6,orig=(src=fc00::1,dst=fc00::240,id=,type=128,code=0),reply=(src=fc00::2,dst=fc00::1,id=,type=129,code=0),zone=1 tcp,orig=(src=fc00::1,dst=fc00::240,sport=,dport=),reply=(src=fc00::2,dst=fc00::1,sport=,dport=),zone=1,protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 ICMP6 Related with SNAT]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") NS_CHECK_EXEC([at_ns0], [ip -6 link set dev p0 address 80:88:88:88:88:77]) NS_CHECK_EXEC([at_ns1], [ip -6 link set dev p1 address 80:88:88:88:88:88]) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::2 lladdr 80:88:88:88:88:88 dev p0]) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::3 lladdr 80:88:88:88:88:88 dev p0]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:77 dev p1]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::1 lladdr 80:88:88:88:88:77 dev p1]) NS_CHECK_EXEC([at_ns0], [ip -6 route add default via fc00::2]) dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl priority=100 in_port=1,ip6,action=ct(nat(src=fc00::240),commit),2 priority=100 in_port=2,ct_state=-trk,ip6,action=ct(table=0,nat) priority=100 in_port=2,ct_state=+trk+est,ip6,action=1 priority=100 in_port=2,ct_state=+trk+rel,ip6,action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2]) AT_CHECK([ovs-appctl dpctl/flush-conntrack]) rm p0.pcap OVS_DAEMONIZE([tcpdump -n -U -i ovs-p0 -w p0.pcap], [tcpdump.pid]) sleep 1 dnl UDP packets from ns0->ns1 should solicit "destination unreachable" response. NS_CHECK_EXEC([at_ns0], [echo a | nc -6 $NC_EOF_OPT -u fc00::2 1]) AT_CHECK([tcpdump -n -v "icmp6" -r p0.pcap 2>/dev/null | grep -E 'wrong|bad'], [1], [ignore-nolog]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMPv6 related NAT with single port]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96", "f0:00:00:01:01:01", [], "nodad") ADD_VETH(p1, at_ns1, br0, "fc00::2/96", "f0:00:00:01:01:02", [], "nodad") AT_DATA([flows.txt], [dnl table=0,ipv6,ct_state=-trk,actions=ct(table=0,nat) table=0,in_port=ovs-p0,ct_state=+trk+new,udp6,actions=ct(commit,nat(dst=[[fc00::2]]:8080)),ovs-p1 table=0,in_port=ovs-p1,ct_state=+trk+rel+rpl,icmp6,actions=ovs-p0 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) rm p0.pcap NETNS_DAEMONIZE([at_ns0], [tcpdump -n -l -U -i p0 -w p0.pcap 2> tcpdump0_err], [tcpdump0.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump0_err]) dnl Send UDP packet from [[fc00::1]]:1234 to [[fc00::240]]:80 AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-p0,packet=f00000010102f0000001010186dd60066ced00151140fc000000000000000000000000000001fc00000000000000000000000000024004d20050001587d4646573745f756e72656163680a,actions=resubmit(,0)"]) dnl Send "destination unreachable" response AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-p1,packet=f00000010101f0000001010286dd600733ed00453a40fc000000000000000000000000000002fc000000000000000000000000000001010428550000000060066ced00151140fc000000000000000000000000000001fc00000000000000000000000000000204d21f9000156ad2646573745f756e72656163680a,actions=resubmit(,0)"]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=fc00::1," | sort], [0], [dnl udp,orig=(src=fc00::1,dst=fc00::240,sport=1234,dport=80),reply=(src=fc00::2,dst=fc00::1,sport=8080,dport=1234) ]) OVS_WAIT_UNTIL([ovs-pcap p0.pcap | grep -q "f00000010101f0000001010286dd600733ed00453a40fc000000000000000000000000000240fc000000000000000000000000000001010426170000000060066ced00151140fc000000000000000000000000000001fc00000000000000000000000000024004d20050001587d4646573745f756e72656163680a"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 FTP with SNAT]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "fc00::2/96") dnl Would be nice if NAT could translate neighbor discovery messages, too. NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1]) dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.) table=0 priority=10 ip6, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new TCPv6 FTP control connections. table=1 in_port=1 ct_state=+new tcp6 ipv6_src=fc00::1 tp_dst=21 action=ct(alg=ftp,commit,nat(src=fc00::240)),2 dnl Allow related TCPv6 connections from port 2 to the NATted address. table=1 in_port=2 ct_state=+new+rel tcp6 ipv6_dst=fc00::240 action=ct(commit,nat),1 dnl Allow established TCPv6 connections both ways, enforce NATting table=1 in_port=1 ct_state=+est tcp6 ipv6_src=fc00::240 action=2 table=1 in_port=2 ct_state=+est tcp6 ipv6_dst=fc00::1 action=1 dnl Allow other ICMPv6 both ways (without commit). table=1 priority=100 in_port=1 icmp6, action=2 table=1 priority=100 in_port=2 icmp6, action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([[[[fc00::2]]]], [--ipv6]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=fc00::2,dst=fc00::240,sport=,dport=),reply=(src=fc00::1,dst=fc00::2,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 FTP Passive with SNAT]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "fc00::2/96") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:99]) NS_CHECK_EXEC([at_ns0], [ip -6 neigh add fc00::2 lladdr 80:88:88:88:88:99 dev p0]) NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1]) dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.) table=0 priority=10 ip6, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow new TCPv6 FTP control connections. table=1 in_port=1 ct_state=+new tcp6 ipv6_src=fc00::1 tp_dst=21 action=ct(alg=ftp,commit,nat(src=fc00::240)),2 dnl Allow related TCPv6 connections from port 1. table=1 in_port=1 ct_state=+new+rel tcp6 ipv6_dst=fc00::2 action=ct(commit,nat),2 dnl Allow established TCPv6 connections both ways, enforce NATting table=1 in_port=1 ct_state=+est tcp6 ipv6_src=fc00::240 action=2 table=1 in_port=2 ct_state=+est tcp6 ipv6_dst=fc00::1 action=1 dnl Allow other ICMPv6 both ways (without commit). table=1 priority=100 in_port=1 icmp6, action=2 table=1 priority=100 in_port=2 icmp6, action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) OVS_START_L7([at_ns1], [ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP([[[[fc00::2]]]], [--ipv6]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=),protoinfo=(state=) tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=),protoinfo=(state=),helper=ftp ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv6 FTP with SNAT - orig tuple]) AT_SKIP_IF([test $HAVE_FTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) ADD_VETH(p1, at_ns1, br0, "fc00::2/96") dnl Would be nice if NAT could translate neighbor discovery messages, too. NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1]) dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.) table=0 priority=10 ip6, action=ct(nat,table=1) table=0 priority=0 action=drop dnl dnl Table 1 dnl dnl Allow other ICMPv6 both ways (without commit). table=1 priority=100 in_port=1 icmp6, action=2 table=1 priority=100 in_port=2 icmp6, action=1 dnl Allow new TCPv6 FTP control connections. table=1 priority=10 in_port=1 ct_state=+new+trk-inv tcp6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=ct(alg=ftp,commit,nat(src=fc00::240)),2 dnl Allow related TCPv6 connections from port 2 to the NATted address. table=1 priority=10 in_port=2 ct_state=+new+rel+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=ct(commit,nat),1 dnl Allow established TCPv6 connections both ways, enforce NATting table=1 priority=10 in_port=1 ct_state=+est+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=2 table=1 priority=10 in_port=2 ct_state=+est+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Linux seems to take a little time to get its IPv6 stack in order. Without dnl waiting, we get occasional failures due to the following error: dnl "connect: Cannot assign requested address" OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) NETNS_DAEMONIZE([at_ns1], [[$PYTHON3 $srcdir/test-l7.py ftp]], [ftp0.pid]) OVS_WAIT_UNTIL([ip netns exec at_ns1 netstat -l | grep ftp]) dnl FTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], OVS_GET_FTP_ACTIVE([[[[fc00::2]]]], [--ipv6]), [0], [ignore], [ignore]) dnl Discards CLOSE_WAIT and CLOSING AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=),protoinfo=(state=),helper=ftp tcp,orig=(src=fc00::2,dst=fc00::240,sport=,dport=),reply=(src=fc00::1,dst=fc00::2,sport=,dport=),protoinfo=(state=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - IPv4 TFTP with SNAT]) AT_SKIP_IF([test $HAVE_TFTP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() CHECK_CONNTRACK_ALG() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.240 e6:66:c1:11:11:11]) dnl Allow any traffic from ns0->ns1. AT_DATA([flows.txt], [dnl dnl track all IPv4 traffic. table=0 priority=10 ip, action=ct(table=1) dnl drop everything else. table=0 priority=0 action=drop dnl dnl Table 1 dnl Allow ICMP both ways. table=1 priority=100 in_port=1 icmp, action=2 table=1 priority=100 in_port=2 icmp, action=1 dnl dnl Allow new TFTP control connections. table=1 in_port=1 ct_state=+new udp nw_src=10.1.1.1 tp_dst=69 action=ct(alg=tftp,commit,nat(src=10.1.1.240)),2 dnl Allow related UDP connections from port 1. table=1 in_port=2 ct_state=+new+rel udp nw_src=10.1.1.2 action=ct(commit,nat),1 dnl Allow established and NAT them. table=1 in_port=1 ct_state=+est udp nw_src=10.1.1.1 action=ct(nat,table=2) table=1 in_port=2 ct_state=+est udp nw_src=10.1.1.2 action=ct(nat,table=2) dnl table=1 priority=0, action=drop dnl table=2 in_port=1 ct_state=+est udp nw_src=10.1.1.240 action=2 table=2 in_port=2 ct_state=+est udp nw_dst=10.1.1.1 action=1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Check that the stacks working to avoid races. OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.1.2 >/dev/null]) OVS_START_L7([at_ns0], [tftp]) OVS_START_L7([at_ns1], [tftp]) dnl TFTP requests from p0->p1 should work fine. NS_CHECK_EXEC([at_ns0], [[curl $CURL_OPT tftp://10.1.1.2/flows.txt -o foo 2>curl0.log]]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),helper=tftp udp,orig=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - DNAT load balancing]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns1, at_ns2, at_ns3, at_ns4) ADD_VETH(p1, at_ns1, br0, "10.1.1.1/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.2/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.3/24") ADD_VETH(p4, at_ns4, br0, "10.1.1.4/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:11]) NS_CHECK_EXEC([at_ns2], [ip link set dev p2 address 80:88:88:88:88:22]) NS_CHECK_EXEC([at_ns3], [ip link set dev p3 address 80:88:88:88:88:33]) NS_CHECK_EXEC([at_ns4], [ip link set dev p4 address 80:88:88:88:88:44]) dnl Select group for load balancing. One bucket per server. Each bucket dnl tracks and NATs the connection and recirculates to table 4 for egress dnl routing. Packets of existing connections are always NATted based on dnl connection state, only new connections are NATted according to the dnl specific NAT parameters in each bucket. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-group br0 "group_id=234,type=select,bucket=weight=100,ct(nat(dst=10.1.1.2),commit,table=4),bucket=weight=100,ct(nat(dst=10.1.1.3),commit,table=4),bucket=weight=100,ct(nat(dst=10.1.1.4),commit,table=4)"]) AT_DATA([flows.txt], [dnl dnl Track connections to the virtual IP address. table=0 priority=100 ip nw_dst=10.1.1.64 action=group:234 dnl All other IP traffic is allowed but the connection state is no commited. table=0 priority=90 ip action=ct(table=4,nat) dnl dnl Allow ARP, but generate responses for virtual addresses table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Routing table dnl table=4,ip,nw_dst=10.1.1.1 action=mod_dl_dst:80:88:88:88:88:11,output:1 table=4,ip,nw_dst=10.1.1.2 action=mod_dl_dst:80:88:88:88:88:22,output:2 table=4,ip,nw_dst=10.1.1.3 action=mod_dl_dst:80:88:88:88:88:33,output:3 table=4,ip,nw_dst=10.1.1.4 action=mod_dl_dst:80:88:88:88:88:44,output:4 table=4 priority=0 action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] dnl Zero result means not found. table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. table=10 priority=100 arp xreg0=0 action=normal dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=controller ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Start web servers OVS_START_L7([at_ns2], [http]) OVS_START_L7([at_ns3], [http]) OVS_START_L7([at_ns4], [http]) on_exit 'ovs-ofctl -O OpenFlow15 dump-flows br0' on_exit 'ovs-appctl revalidator/purge' on_exit 'ovs-appctl dpif/dump-flows br0' dnl Should work with the virtual IP address through NAT for i in $(seq 1 50); do echo Request $i NS_CHECK_EXEC([at_ns1], OVS_GET_HTTP([10.1.1.64]), [0], [ignore], [ignore]) done dnl Each server should have at least one connection. AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.64)], [0], [dnl tcp,orig=(src=10.1.1.1,dst=10.1.1.64,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.64,sport=,dport=),reply=(src=10.1.1.3,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) tcp,orig=(src=10.1.1.1,dst=10.1.1.64,sport=,dport=),reply=(src=10.1.1.4,dst=10.1.1.1,sport=,dport=),protoinfo=(state=) ]) ovs-appctl dpif/dump-flows br0 ovs-appctl revalidator/purge ovs-ofctl -O OpenFlow15 dump-flows br0 ovs-ofctl -O OpenFlow15 dump-group-stats br0 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - DNAT load balancing with NC]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns1, at_ns2, at_ns3, at_ns4, at_ns5) ADD_VETH(p1, at_ns1, br0, "10.1.1.1/24") ADD_VETH(p2, at_ns2, br0, "10.1.1.2/24") ADD_VETH(p3, at_ns3, br0, "10.1.1.3/24") ADD_VETH(p4, at_ns4, br0, "10.1.1.4/24") ADD_VETH(p5, at_ns5, br0, "10.1.1.5/24") NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:88:88:88:88:11]) NS_CHECK_EXEC([at_ns2], [ip link set dev p2 address 80:88:88:88:88:22]) NS_CHECK_EXEC([at_ns3], [ip link set dev p3 address 80:88:88:88:88:33]) NS_CHECK_EXEC([at_ns4], [ip link set dev p4 address 80:88:88:88:88:44]) NS_CHECK_EXEC([at_ns5], [ip link set dev p5 address 80:88:88:88:88:55]) dnl Select group for load balancing. One bucket per server. Each bucket dnl tracks and NATs the connection and recirculates to table 4 for egress dnl routing. Packets of existing connections are always NATted based on dnl connection state, only new connections are NATted according to the dnl specific NAT parameters in each bucket. AT_CHECK([ovs-ofctl -O OpenFlow15 -vwarn add-group br0 "group_id=234,type=select,bucket=weight=100,ct(nat(dst=10.1.1.2),commit,table=4),bucket=weight=100,ct(nat(dst=10.1.1.3),commit,table=4),bucket=weight=100,ct(nat(dst=10.1.1.4),commit,table=4)"]) AT_DATA([flows.txt], [dnl dnl Track connections to the virtual IP address. table=0 priority=100 ip nw_dst=10.1.1.64 action=group:234 dnl All other IP traffic is allowed but the connection state is no commited. table=0 priority=90 ip action=ct(table=4,nat) dnl dnl Allow ARP, but generate responses for virtual addresses table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 table=0 priority=10 arp action=normal table=0 priority=0 action=drop dnl dnl Routing table dnl table=4,ip,nw_dst=10.1.1.1 action=mod_dl_dst:80:88:88:88:88:11,output:1 table=4,ip,nw_dst=10.1.1.2 action=mod_dl_dst:80:88:88:88:88:22,output:2 table=4,ip,nw_dst=10.1.1.3 action=mod_dl_dst:80:88:88:88:88:33,output:3 table=4,ip,nw_dst=10.1.1.4 action=mod_dl_dst:80:88:88:88:88:44,output:4 table=4,ip,nw_dst=10.1.1.5 action=mod_dl_dst:80:88:88:88:88:55,output:5 table=4 priority=0 action=drop dnl dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 table=8,reg2=0x0a010140,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] dnl Zero result means not found. table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. dnl TPA IP in reg2. table=10 priority=100 arp xreg0=0 action=normal dnl Swaps the fields of the ARP message to turn a query to a response. table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] table=10 priority=0 action=controller ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl Start web servers OVS_START_L7([at_ns2], [http]) OVS_START_L7([at_ns3], [http]) OVS_START_L7([at_ns4], [http]) on_exit 'ovs-ofctl -O OpenFlow15 dump-flows br0' on_exit 'ovs-appctl revalidator/purge' on_exit 'ovs-appctl dpif/dump-flows br0' sleep 5 dnl Should work with the virtual IP address through NAT for i in 1 2 3 4 5 6 7 8 9; do echo Request $i NS_CHECK_EXEC([at_ns1], [echo "TEST1" | nc -p 4100$i 10.1.1.64 80 > nc-1-$i.log]) NS_CHECK_EXEC([at_ns5], [echo "TEST5" | nc -p 4100$i 10.1.1.64 80 > nc-5-$i.log]) done conntrack -L 2>&1 ovs-appctl dpif/dump-flows br0 ovs-appctl revalidator/purge ovs-ofctl -O OpenFlow15 dump-flows br0 ovs-ofctl -O OpenFlow15 dump-group-stats br0 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - floating IP]) AT_SKIP_IF([test $HAVE_NC = no]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_CT_CLEAR() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") dnl FIP 10.254.254.1 ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") dnl FIP 10.254.254.2 dnl Static ARPs NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.2 lladdr f0:00:00:01:01:02 dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.1 lladdr f0:00:00:01:01:01 dev p1]) dnl Static ARP and route entries for the FIP "gateway" NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.254 lladdr f0:00:00:01:01:FE dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.254 lladdr f0:00:00:01:01:FE dev p1]) NS_CHECK_EXEC([at_ns0], [ip route add default nexthop via 10.1.1.254]) NS_CHECK_EXEC([at_ns1], [ip route add default nexthop via 10.1.1.254]) NETNS_DAEMONIZE([at_ns0], [nc -l -k 1234 > /dev/null], [nc0.pid]) AT_DATA([flows.txt], [dnl table=0,priority=10 ip action=ct(table=1) table=0,priority=1 action=drop dnl dst FIP table=1,priority=20 ip,ct_state=+trk+est,nw_dst=10.254.254.0/24 action=goto_table:10 table=1,priority=20 ip,ct_state=+trk+new,nw_dst=10.254.254.0/24 action=ct(commit,table=10) dnl dst local table=1,priority=10 ip,ct_state=+trk+est action=goto_table:20 table=1,priority=10 ip,ct_state=+trk+new action=ct(commit,table=20) table=1,priority=1 ip,ct_state=+trk+inv action=drop dnl dnl FIP translation (dst FIP, src local) --> (dst local, src FIP) table=10 ip,nw_dst=10.254.254.1 action=set_field:10.1.1.1->nw_dst,goto_table:11 table=10 ip,nw_dst=10.254.254.2 action=set_field:10.1.1.2->nw_dst,goto_table:11 table=11 ip,nw_src=10.1.1.1 action=set_field:10.254.254.1->nw_src,goto_table:12 table=11 ip,nw_src=10.1.1.2 action=set_field:10.254.254.2->nw_src,goto_table:12 dnl clear conntrack and do another lookup since we changed the tuple table=12,priority=10 ip action=ct_clear,ct(table=13) table=12,priority=1 action=drop table=13 ip,ct_state=+trk+est action=goto_table:20 table=13 ip,ct_state=+trk+new action=ct(commit,table=20) table=13 ip,ct_state=+trk+inv action=drop dnl dnl Output table=20 ip,nw_src=10.1.1.1 action=set_field:f0:00:00:01:01:01->eth_src,goto_table:21 table=20 ip,nw_src=10.1.1.2 action=set_field:f0:00:00:01:01:02->eth_src,goto_table:21 table=20 ip,nw_src=10.254.254.0/24 action=set_field:f0:00:00:01:01:FE->eth_src,goto_table:21 table=21 ip,nw_dst=10.1.1.1 action=set_field:f0:00:00:01:01:01->eth_dst,output:ovs-p0 table=21 ip,nw_dst=10.1.1.2 action=set_field:f0:00:00:01:01:02->eth_dst,output:ovs-p1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl non-FIP case. dnl dnl The last ACK from the 3-WAY handshake will go via upcall after going dnl through conntrack because +est traffic is handled differently from +new. dnl The sender though will proceed sending data. Since connection is very dnl short, it is possible that this one ACK will be delivered after the dnl connection is already closed, i.e. after all the data is sent and the dnl connection termination sequence (FIN-ACK-FIN-ACK) is done. Delivery dnl in this case will trigger RST reply. And RST will transition TIME_WAIT dnl into CLOSE, hence the need to look for both states below. NS_CHECK_EXEC([at_ns1], [echo "foobar" |nc $NC_EOF_OPT 10.1.1.1 1234]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-conntrack \ | grep -E '(TIME_WAIT|CLOSING)' | FORMAT_CT(10.1.1.2)], [tcp,dnl orig=(src=10.1.1.2,dst=10.1.1.1,sport=,dport=),dnl reply=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),dnl protoinfo=(state=)]) dnl Check that the full session ends as expected (i.e. TIME_WAIT/CLOSING). dnl Otherwise it means the datapath didn't process the ct_clear action. dnl Ending in SYN_RECV (OVS maps to ESTABLISHED) means the initial frame dnl was committed, but not a second time after the FIP translation (because dnl ct_clear didn't occur). dnl dnl Same considerations about packet reordering apply, hence looking for dnl both states. NS_CHECK_EXEC([at_ns1], [echo "foobar" |nc $NC_EOF_OPT 10.254.254.1 1234]) OVS_WAIT_UNTIL_EQUAL([ovs-appctl dpctl/dump-conntrack \ | grep -E '(TIME_WAIT|CLOSING)' | FORMAT_CT(10.254.254.2)], [tcp,dnl orig=(src=10.254.254.2,dst=10.1.1.1,sport=,dport=),dnl reply=(src=10.1.1.1,dst=10.254.254.2,sport=,dport=),dnl protoinfo=(state=)]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - negative test for recirculation optimization]) dnl This test will fail if 'conn' caching is being used, because the tuple dnl has been changed outside of conntrack. AT_SKIP_IF([test $HAVE_NC = no]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_CT_CLEAR() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") dnl FIP 10.254.254.1 ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") dnl FIP 10.254.254.2 dnl Static ARPs NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.2 lladdr f0:00:00:01:01:02 dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.1 lladdr f0:00:00:01:01:01 dev p1]) dnl Static ARP and route entries for the FIP "gateway" NS_CHECK_EXEC([at_ns0], [ip neigh add 10.1.1.254 lladdr f0:00:00:01:01:FE dev p0]) NS_CHECK_EXEC([at_ns1], [ip neigh add 10.1.1.254 lladdr f0:00:00:01:01:FE dev p1]) NS_CHECK_EXEC([at_ns0], [ip route add default nexthop via 10.1.1.254]) NS_CHECK_EXEC([at_ns1], [ip route add default nexthop via 10.1.1.254]) NETNS_DAEMONIZE([at_ns0], [nc -l -k 1234 > /dev/null], [nc0.pid]) AT_DATA([flows.txt], [dnl table=0,priority=10 ip action=ct(table=1) dnl dst FIP table=1,priority=20 ip,ct_state=+trk+est,nw_dst=10.254.254.0/24 action=goto_table:2 table=1,priority=20 ip,ct_state=+trk+new,nw_dst=10.254.254.0/24 action=ct(commit,exec(set_field:1->ct_mark),table=2) dnl dnl FIP translation (dst FIP, src local) --> (dst local, src FIP) table=2 ip,nw_dst=10.254.254.1 action=set_field:10.1.1.1->nw_dst,goto_table:3 table=2 ip,nw_dst=10.254.254.2 action=set_field:10.1.1.2->nw_dst,goto_table:3 table=3 ip,nw_src=10.1.1.1 action=set_field:10.254.254.1->nw_src,goto_table:4 table=3 ip,nw_src=10.1.1.2 action=set_field:10.254.254.2->nw_src,goto_table:4 table=4 ip,nw_dst=10.1.1.1 action=set_field:f0:00:00:01:01:01->eth_dst,goto_table:5 table=4 ip,nw_dst=10.1.1.2 action=set_field:f0:00:00:01:01:02->eth_dst,goto_table:5 table=5 ip,nw_src=10.254.254.0/24 action=set_field:f0:00:00:01:01:FE->eth_src,goto_table:6 dnl dnl Tuple has been changed outside of conntrack table=6,priority=10 ip action=ct(table=7) dnl table=7 ip,ct_state=+trk+est action=goto_table:8 table=7 ip,ct_mark=0x0,ct_state=+trk+new action=ct(commit,exec(set_field:2->ct_mark),table=8) dnl table=8 ip,nw_dst=10.1.1.1 action=output:ovs-p0 table=8 ip,nw_dst=10.1.1.2 action=output:ovs-p1 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) NS_CHECK_EXEC([at_ns1], [echo "foobar" |nc $NC_EOF_OPT 10.254.254.1 1234]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.254.254)], [0], [dnl tcp,orig=(src=10.1.1.2,dst=10.254.254.1,sport=,dport=),reply=(src=10.254.254.1,dst=10.1.1.2,sport=,dport=),mark=1,protoinfo=(state=) tcp,orig=(src=10.254.254.2,dst=10.1.1.1,sport=,dport=),reply=(src=10.1.1.1,dst=10.254.254.2,sport=,dport=),mark=2,protoinfo=(state=) ]) ovs-appctl dpif/dump-flows br0 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - Multiple ICMP traverse]) dnl This tracks sending ICMP packets via conntrack multiple times for the dnl same packet CHECK_NO_DPDK_OFFLOAD() CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_CT_CLEAR() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") dnl setup ct flows AT_DATA([flows.txt], [dnl table=0,priority=10 ip,icmp,ct_state=-trk action=ct(zone=1,table=1) table=0,priority=0 action=drop table=1,priority=10 ct_state=-est+trk+new,ip,ct_zone=1,in_port=1 action=ct(commit,table=2) table=1,priority=10 ct_state=+est-new+trk,ct_zone=1,in_port=1 action=resubmit(,2) table=1,priority=0 action=drop table=2,priority=10 ct_state=+trk+new,in_port=1 action=drop table=2,priority=10 ct_state=+trk+est action=drop ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) m4_define([ICMP_PKT], [m4_join([,], [eth_src=f0:00:00:01:01:01,eth_dst=f0:00:00:01:01:02,eth_type=0x0800], [nw_src=10.1.1.1,nw_dst=10.1.1.2], [nw_proto=1,nw_ttl=64,nw_frag=no], [icmp_type=8,icmp_code=0])]) # Sending ICMP packets, first and second. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT' '')], [0], [ignore]) NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'ICMP_PKT' '')], [0], [ignore]) sleep 1 dnl ensure CT picked up the packet AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0) ]) AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE | sed 's/n_bytes=70,/n_bytes=84,/'], [0], [dnl cookie=0x0, duration=, table=2, n_packets=2, n_bytes=84, idle_age=, priority=10,ct_state=+new+trk,in_port=1 actions=drop cookie=0x0, duration=, table=2, n_packets=0, n_bytes=0, idle_age=, priority=10,ct_state=+est+trk actions=drop ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - can match and clear ct_state from outside OVS]) CHECK_EXTERNAL_CT() CHECK_CONNTRACK_LOCAL_STACK() OVS_CHECK_GENEVE() OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-underlay], [set bridge br-underlay other-config:hwaddr=\"f0:00:00:01:01:02\"]) AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=100,ct_state=+trk,actions=ct_clear,resubmit(,0)"]) AT_CHECK([ovs-ofctl add-flow br-underlay "priority=10,actions=normal"]) ADD_EXTERNAL_CT([br0]) ADD_NAMESPACES(at_ns0) dnl Set up underlay link from host into the namespace using veth pair. ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24", "f0:00:00:01:01:01") AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) AT_CHECK([ip link set dev br-underlay up]) dnl Set up tunnel endpoints on OVS outside the namespace and with a native dnl linux device inside the namespace. ADD_OVS_TUNNEL([geneve], [br0], [at_gnv0], [172.31.1.1], [10.1.1.100/24]) ADD_NATIVE_TUNNEL([geneve], [ns_gnv0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [vni 0]) dnl First, check the underlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 172.31.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Okay, now check the overlay NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.1.1.100 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl Confirm that the ct_state and ct_clear action found its way to the dp AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep ct_clear | sort | dnl grep 'eth(src=f0:00:00:01:01:02,dst=f0:00:00:01:01:01)' | dnl strip_stats | strip_used | dnl sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], [0], [dnl recirc_id(0),in_port(br-underlay),ct_state(+trk),eth(src=f0:00:00:01:01:02,dst=f0:00:00:01:01:01),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct_clear,ovs-p0 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - ICMP from different source related with NAT]) AT_SKIP_IF([test $HAVE_NC = no]) AT_SKIP_IF([test $HAVE_TCPDUMP = no]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(client, server) ADD_VETH(client, client, br0, "192.168.20.10/24", "00:00:00:00:20:10") ADD_VETH(server, server, br0, "192.168.10.20/24", "00:00:00:00:10:20") dnl Send traffic from client to CT, do DNAT if the traffic is new otherwise send it to server AT_DATA([flows.txt], [dnl table=0,ip,actions=ct(table=1,zone=42,nat) table=1,in_port=ovs-client,ip,ct_state=+trk+new,actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20) table=1,icmp,ct_state=+trk+rel-rpl,actions=ct(commit,table=2,zone=42,nat) table=1,ip,actions=resubmit(,2) table=2,in_port=ovs-client,ip,ct_state=+trk+new,actions=output:ovs-server table=2,in_port=ovs-client,icmp,ct_state=+trk+rel,actions=output:ovs-server table=2,in_port=ovs-server,icmp,ct_state=+trk+rel,actions=output:ovs-client table=2,in_port=ovs-server,ip,ct_state=+trk+rpl,actions=output:ovs-client ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) rm server.pcap NETNS_DAEMONIZE([server], [tcpdump -n -l -U -i server -w server.pcap 2>tcpdump0_err], [tcpdump0.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump0_err]) dnl Send UDP client->server AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ packet=00000000102000000000201008004500001C000040000A11C762C0A8140AC0A814140001000200080000,actions=resubmit(,0)"]) dnl Send UDP response server->client AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-server,\ packet=00000000201000000000102008004500001C000040000A11D162C0A80A14C0A8140A0002000100080000,actions=resubmit(,0)"]) dnl Fake router sending ICMP need frag router->server AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ packet=000000001020000000002000080045000038011F0000FF011140C0A81401C0A814140304F778000005784500001C000040000A11C762C0A81414C0A8140A0002000100080000,\ actions=resubmit(,0)" ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort ], [0], [dnl n_packets=3, n_bytes=154, reset_counts ip actions=ct(table=1,zone=42,nat) table=1, n_packets=1, n_bytes=42, reset_counts ct_state=+new+trk,ip,in_port=1 actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20)) table=1, n_packets=1, n_bytes=42, reset_counts ip actions=resubmit(,2) table=1, n_packets=1, n_bytes=70, reset_counts ct_state=+rel-rpl+trk,icmp actions=ct(commit,table=2,zone=42,nat) table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+new+trk,ip,in_port=1 actions=output:2 table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+rpl+trk,ip,in_port=2 actions=output:1 table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=1 actions=output:2 table=2, reset_counts ct_state=+rel+trk,icmp,in_port=2 actions=output:1 OFPST_FLOW reply (OF1.5): ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "192.168.20.10"], [0], [dnl udp,orig=(src=192.168.20.10,dst=192.168.20.20,sport=1,dport=2),reply=(src=192.168.10.20,dst=192.168.20.10,sport=2,dport=1),zone=42 ]) OVS_WAIT_UNTIL([ovs-pcap server.pcap | grep 000000001020000000002000]) AT_CHECK([ovs-pcap server.pcap | grep 000000001020000000002000], [0], [dnl 000000001020000000002000080045000038011f0000ff011b40c0a81401c0a80a140304f778000005784500001c000040000a11d162c0a80a14c0a8140a0002000100080000 ]) dnl Check the ICMP error in reply direction AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=42]) rm client.pcap NETNS_DAEMONIZE([client], [tcpdump -n -l -U -i client -w client.pcap 2>tcpdump1_err], [tcpdump1.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump1_err]) dnl Send UDP client->server AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ packet=00000000102000000000201008004500001C000040000A11C762C0A8140AC0A814140001000200080000,actions=resubmit(,0)"]) dnl Fake router sending ICMP need frag router->client AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-server,\ packet=000000002010000000002000080045000038011F0000FF01114AC0A81401C0A8140A0304F778000005784500001C000040000A11D162C0A8140AC0A80A140001000200080000,\ actions=resubmit(,0)" ]) AT_CHECK([ovs-appctl revalidator/purge], [0]) AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort ], [0], [dnl n_packets=5, n_bytes=266, reset_counts ip actions=ct(table=1,zone=42,nat) table=1, n_packets=1, n_bytes=70, reset_counts ct_state=+rel-rpl+trk,icmp actions=ct(commit,table=2,zone=42,nat) table=1, n_packets=2, n_bytes=112, reset_counts ip actions=resubmit(,2) table=1, n_packets=2, n_bytes=84, reset_counts ct_state=+new+trk,ip,in_port=1 actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20)) table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+rpl+trk,ip,in_port=2 actions=output:1 table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=1 actions=output:2 table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=2 actions=output:1 table=2, n_packets=2, n_bytes=84, reset_counts ct_state=+new+trk,ip,in_port=1 actions=output:2 OFPST_FLOW reply (OF1.5): ]) AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "192.168.20.10"], [0], [dnl udp,orig=(src=192.168.20.10,dst=192.168.20.20,sport=1,dport=2),reply=(src=192.168.10.20,dst=192.168.20.10,sport=2,dport=1),zone=42 ]) OVS_WAIT_UNTIL([ovs-pcap client.pcap | grep 000000002010000000002000]) AT_CHECK([ovs-pcap client.pcap | grep 000000002010000000002000], [0], [dnl 000000002010000000002000080045000038011f0000ff011137c0a81414c0a8140a0304f778000005784500001c000040000a11c762c0a8140ac0a814140001000200080000 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([conntrack - Flush many conntrack entries by port]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_DATA([flows.txt], [dnl priority=100,in_port=1,udp,action=ct(zone=1,commit),2 ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) dnl 20 packets from port 1 and 1 packet from port 2. flow_l3="\ eth_src=50:54:00:00:00:09,eth_dst=50:54:00:00:00:0a,dl_type=0x0800,\ nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_proto=17,nw_ttl=64,nw_frag=no" for i in $(seq 1 20); do frame=$(ovs-ofctl compose-packet --bare "$flow_l3, udp_src=1,udp_dst=$i") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=$frame actions=resubmit(,0)"]) done frame=$(ovs-ofctl compose-packet --bare "$flow_l3, udp_src=2,udp_dst=1") AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=$frame actions=resubmit(,0)"]) : > conntrack for i in $(seq 1 20); do echo "udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=${i}),reply=(src=10.1.1.2,dst=10.1.1.1,sport=${i},dport=1),zone=1" >> conntrack done echo "udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=2,dport=1),reply=(src=10.1.1.2,dst=10.1.1.1,sport=1,dport=2),zone=1" >> conntrack sort conntrack > expout AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=1 | grep -F "src=10.1.1.1," | sort ], [0], [expout]) dnl Check that flushing conntrack by port 1 flush all ct for port 1 but keeps ct for port 2. AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=1 'ct_nw_proto=17,ct_tp_src=1']) AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=1 | grep -F "src=10.1.1.1," | sort ], [0], [dnl udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=2,dport=1),reply=(src=10.1.1.2,dst=10.1.1.1,sport=1,dport=2),zone=1 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([IGMP]) AT_SETUP([IGMP - flood under normal action]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") AT_CHECK([ovs-ofctl add-flow br0 "actions=NORMAL"]) NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 01 00 5e 01 01 03 dnl f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl 00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl 00 00 00 00 > /dev/null]) AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep -e .*ipv4 | sort | dnl strip_stats | strip_used | strip_recirc | dnl sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'], [0], [dnl recirc_id(),in_port(ovs-p1),eth(src=f0:00:00:01:01:01,dst=01:00:5e:01:01:03),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:br0,ovs-p2 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([IGMP - forward with ICMP]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01") ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02") AT_DATA([flows.txt], [dnl table=0, arp actions=NORMAL table=0, ip,in_port=1 actions=ct(table=1,zone=64000) table=0, in_port=2 actions=output:1 table=1, ip,ct_state=+trk+inv actions=drop table=1 ip,in_port=1,icmp,ct_state=+trk+new actions=output:2 table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) table=1, in_port=1,ip,ct_state=+trk+est actions=output:2 ]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl 00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl 00 00 00 00 > /dev/null]) NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl f0 00 00 01 01 01 08 00 45 00 00 1c 00 01 00 00 40 01 64 dc 0a 01 01 01 0a dnl 01 01 02 08 00 f7 ff ff ff ff ff > /dev/null]) sleep 1 dnl Prefer the OpenFlow rules, because different datapaths will behave slightly dnl differently with respect to the exact dp rules. dnl dnl This is also why we clear n_bytes / n_packets - some kernels with ipv6 dnl enabled will bump some of these counters non-deterministically AT_CHECK([ovs-ofctl dump-flows br0 | grep -v NXST | dnl strip_duration | grep -v arp | grep -v n_packets=0 | dnl grep -v 'in_port=2 actions=output:1' | dnl sed 's/n_bytes=[[0-9]]*/n_bytes=0/ s/idle_age=[[0-9]]*/idle_age=0/ s/n_packets=[[1-9]]/n_packets=0/' | sort], [0], [dnl cookie=0x0, table=0, n_packets=0, n_bytes=0, idle_age=0, ip,in_port=1 actions=ct(table=1,zone=64000) cookie=0x0, table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,icmp,in_port=1 actions=output:2 cookie=0x0, table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,ip,in_port=1 actions=controller(userdata=00.de.ad.be.ef.ca.fe.01) ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([802.1ad]) AT_SETUP([802.1ad - vlan_limit]) OVS_CHECK_GITHUB_ACTION() OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_SVLAN(p0, at_ns0, 4094, "10.255.2.1/24") ADD_SVLAN(p1, at_ns1, 4094, "10.255.2.2/24") ADD_CVLAN(p0.4094, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p1.4094, at_ns1, 100, "10.2.2.2/24") AT_CHECK([ovs-ofctl add-flow br0 "priority=1 action=normal"]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) dnl CVLAN traffic should match the flow and drop AT_CHECK([ovs-appctl revalidator/purge]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:vlan-limit=1]) AT_CHECK([ovs-ofctl add-flow br0 "priority=100 dl_type=0x8100 action=drop"]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -W 3 10.2.2.2], [1], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([802.1ad - push/pop outer 802.1ad]) OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_BR([br1]) ADD_BR([br2]) ADD_NAMESPACES(at_ns0, at_ns1) AT_CHECK([ip link add ovs-p0 type veth peer name ovs-p1]) AT_CHECK([ip link set dev ovs-p0 up]) AT_CHECK([ip link set dev ovs-p1 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p0]) AT_CHECK([ovs-vsctl add-port br1 ovs-p1]) on_exit 'ip link del ovs-p0' AT_CHECK([ip link add ovs-p2 type veth peer name ovs-p3]) AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev ovs-p3 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2]) AT_CHECK([ovs-vsctl add-port br2 ovs-p3]) on_exit 'ip link del ovs-p2' ADD_VETH(p4, at_ns0, br1, "10.1.1.1/24") ADD_VETH(p5, at_ns1, br2, "10.1.1.2/24") ADD_CVLAN(p4, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p5, at_ns1, 100, "10.2.2.2/24") AT_DATA([flows-br0.txt], [dnl priority=1 action=drop priority=100 in_port=1 action=push_vlan:0x88a8,mod_vlan_vid=4094,output:2 priority=100 in_port=2 action=push_vlan:0x88a8,mod_vlan_vid=4094,output:1 ]) AT_DATA([flows-customer-br.txt], [dnl priority=1 action=normal priority=100 in_port=1 vlan_tci=0x1000/0x1000 action=pop_vlan,normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows-br0.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br1 flows-customer-br.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br2 flows-customer-br.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([802.1ad - push/pop outer 802.1q]) OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_BR([br1]) ADD_BR([br2]) ADD_NAMESPACES(at_ns0, at_ns1) AT_CHECK([ip link add ovs-p0 type veth peer name ovs-p1]) AT_CHECK([ip link set dev ovs-p0 up]) AT_CHECK([ip link set dev ovs-p1 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p0]) AT_CHECK([ovs-vsctl add-port br1 ovs-p1]) on_exit 'ip link del ovs-p0' AT_CHECK([ip link add ovs-p2 type veth peer name ovs-p3]) AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev ovs-p3 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2]) AT_CHECK([ovs-vsctl add-port br2 ovs-p3]) on_exit 'ip link del ovs-p2' ADD_VETH(p4, at_ns0, br1, "10.1.1.1/24") ADD_VETH(p5, at_ns1, br2, "10.1.1.2/24") ADD_CVLAN(p4, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p5, at_ns1, 100, "10.2.2.2/24") AT_DATA([flows-br0.txt], [dnl priority=1 action=drop priority=100 in_port=1 action=push_vlan:0x8100,mod_vlan_vid=4094,output:2 priority=100 in_port=2 action=push_vlan:0x8100,mod_vlan_vid=4094,output:1 ]) AT_DATA([flows-customer-br.txt], [dnl priority=1 action=normal priority=100 in_port=1 vlan_tci=0x1000/0x1000 action=pop_vlan,normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows-br0.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br1 flows-customer-br.txt]) AT_CHECK([ovs-ofctl --bundle add-flows br2 flows-customer-br.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([802.1ad - 802.1q tunnel]) OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_BR([br1]) ADD_BR([br2]) ADD_NAMESPACES(at_ns0, at_ns1) AT_CHECK([ip link add ovs-p0 type veth peer name ovs-p1]) AT_CHECK([ip link set dev ovs-p0 up]) AT_CHECK([ip link set dev ovs-p1 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p0]) AT_CHECK([ovs-vsctl add-port br1 ovs-p1]) on_exit 'ip link del ovs-p0' AT_CHECK([ip link add ovs-p2 type veth peer name ovs-p3]) AT_CHECK([ip link set dev ovs-p2 up]) AT_CHECK([ip link set dev ovs-p3 up]) AT_CHECK([ovs-vsctl add-port br0 ovs-p2]) AT_CHECK([ovs-vsctl add-port br2 ovs-p3]) on_exit 'ip link del ovs-p2' ADD_VETH(p4, at_ns0, br1, "10.1.1.1/24") ADD_VETH(p5, at_ns1, br2, "10.1.1.2/24") ADD_CVLAN(p4, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p5, at_ns1, 100, "10.2.2.2/24") ADD_CVLAN(p4, at_ns0, 200, "10.3.2.1/24") ADD_CVLAN(p5, at_ns1, 200, "10.3.2.2/24") ADD_CVLAN(p4, at_ns0, 300, "10.4.2.1/24") ADD_CVLAN(p5, at_ns1, 300, "10.4.2.2/24") AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-ofctl add-flow br2 action=normal]) AT_CHECK([ovs-vsctl set port ovs-p0 vlan_mode=dot1q-tunnel tag=4094 cvlans=100,200]) AT_CHECK([ovs-vsctl set port ovs-p2 vlan_mode=dot1q-tunnel tag=4094 cvlans=100,200]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.3.2.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.3.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.3.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) dnl CVLAN 300 is not permitted by dot1q-tunnel NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -W 3 10.4.2.2], [1], [ignore]) OVS_TRAFFIC_VSWITCHD_STOP(["/dropping VLAN \(0\|300\) packet received on dot1q-tunnel port/d"]) AT_CLEANUP AT_SETUP([802.1ad - double vlan match]) OVS_TRAFFIC_VSWITCHD_START([set Open_vSwitch . other_config:vlan-limit=0]) OVS_CHECK_8021AD() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ADD_SVLAN(p0, at_ns0, 4094, "10.255.2.1/24") ADD_SVLAN(p1, at_ns1, 4094, "10.255.2.2/24") ADD_CVLAN(p0.4094, at_ns0, 100, "10.2.2.1/24") ADD_CVLAN(p1.4094, at_ns1, 100, "10.2.2.2/24") AT_DATA([flows-br0.txt], [dnl table=0,priority=1 action=drop table=0,priority=100 dl_vlan=4094 action=pop_vlan,goto_table:1 table=1,priority=100 dl_vlan=100 action=push_vlan:0x88a8,mod_vlan_vid:4094,normal ]) AT_CHECK([ovs-ofctl --bundle add-flows br0 flows-br0.txt]) OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.2.2.2]) NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -W 2 10.2.2.2 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([nsh-datapath]) AT_SETUP([nsh - encap header]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "0.0.0.0") ADD_VETH(p1, at_ns1, br0, "0.0.0.0") dnl The flow will encap a nsh header to the TCP syn packet dnl eth/ip/tcp --> OVS --> eth/nsh/eth/ip/tcp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,in_port=ovs-p0,ip,actions=encap(nsh(md_type=1)),set_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1,encap(ethernet),set_field:f2:ff:00:00:00:02->dl_dst,set_field:f2:ff:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([TCP_SYN_PKT], [m4_join([,], [eth_src=f2:00:00:00:00:01,eth_dst=f2:00:00:00:00:02,eth_type=0x0800], [nw_src=192.168.0.10,nw_dst=10.0.0.10], [nw_proto=6,nw_ttl=64,nw_frag=no], [tcp_src=1024,tcp_dst=2048,tcp_flags=syn])]) dnl Send the TCP SYN packet from p0(at_ns0) interface directed to dnl p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ $(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')], [0], [ignore]) m4_define([NSH_HEADER], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=63,nsh_np=3,nsh_spi=0x1234,nsh_si=255], [nsh_mdtype=1,nsh_c1=0x11223344])]) OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'NSH_HEADER'), $(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([nsh - decap header]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "0.0.0.0") ADD_VETH(p1, at_ns1, br0, "0.0.0.0") dnl The flow will decap a nsh header which in turn carries a TCP syn packet dnl eth/nsh/eth/ip/tcp --> OVS --> eth/ip/tcp AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,in_port=ovs-p0,dl_type=0x894f, actions=decap(),decap(), ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([TCP_SYN_PKT], [m4_join([,], [eth_src=f2:00:00:00:00:01,eth_dst=f2:00:00:00:00:02,eth_type=0x0800], [nw_src=192.168.0.10,nw_dst=10.0.0.10], [nw_proto=6,nw_ttl=64,nw_frag=no], [tcp_src=1024,tcp_dst=2048,tcp_flags=syn])]) m4_define([NSH_HEADER], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=63,nsh_np=3,nsh_spi=0x1234,nsh_si=255], [nsh_mdtype=1,nsh_c1=0x11223344])]) dnl Send the NSH packet with TCP SYN payload from p0(at_ns0) interface directed dnl to p1(at_ns1) interface. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ "$(ovs-ofctl compose-packet --bare 'NSH_HEADER')" \ "$(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')"], [0], [ignore]) dnl Check the expected de-capsulated TCP packet on the egress interface OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q \ "^$(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')0*\$"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([nsh - replace header]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "0.0.0.0") ADD_VETH(p1, at_ns1, br0, "0.0.0.0") dnl The flow will decap a nsh header and encap a new nsh header dnl eth/nsh-X/eth/ip/tcp --> OVS --> eth/nsh-Y/eth/ip/tcp dnl The flow will add another NSH header with nsh_spi=0x101, nsh_si=4, dnl nsh_ttl=7 and change the md1 context AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,in_port=ovs-p0,dl_type=0x894f,nsh_spi=0x100,nsh_si=0x03,actions=decap(),decap(),encap(nsh(md_type=1)),set_field:0x07->nsh_ttl,set_field:0x0101->nsh_spi,set_field:0x04->nsh_si,set_field:0x100f0e0d->nsh_c1,set_field:0x0c0b0a09->nsh_c2,set_field:0x08070605->nsh_c3,set_field:0x04030201->nsh_c4,encap(ethernet),set_field:f2:ff:00:00:00:02->dl_dst,set_field:f2:ff:00:00:00:01->dl_src,ovs-p1"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) m4_define([TCP_SYN_PKT], [m4_join([,], [eth_src=f2:00:00:00:00:01,eth_dst=f2:00:00:00:00:02,eth_type=0x0800], [nw_src=192.168.0.10,nw_dst=10.0.0.10], [nw_proto=6,nw_ttl=64,nw_frag=no], [tcp_src=1024,tcp_dst=2048,tcp_flags=syn])]) m4_define([NSH_HEADER_1], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=8,nsh_np=3,nsh_spi=0x100,nsh_si=3,nsh_mdtype=1], [nsh_c1=0x01020304,nsh_c2=0x05060708,nsh_c3=0x090a0b0c,nsh_c4=0x0d0e0f10])]) dnl Send the NSH packet with TCP SYN payload from p0(at_ns0) interface directed dnl to p1(at_ns1) interface. dnl The nsh_ttl is 8, nsh_spi is 0x100 and nsh_si is 3. NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ "$(ovs-ofctl compose-packet --bare 'NSH_HEADER_1')" \ "$(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')"], [0], [ignore]) m4_define([NSH_HEADER_2], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=7,nsh_np=3,nsh_spi=0x101,nsh_si=4,nsh_mdtype=1], [nsh_c1=0x100f0e0d,nsh_c2=0x0c0b0a09,nsh_c3=0x08070605,nsh_c4=0x04030201])]) dnl Check the expected NSH packet with new fields in the header. OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'NSH_HEADER_2'), $(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([nsh - forward]) OVS_TRAFFIC_VSWITCHD_START() ADD_NAMESPACES(at_ns0, at_ns1, at_ns2) ADD_VETH(p0, at_ns0, br0, "0.0.0.0") ADD_VETH(p1, at_ns1, br0, "0.0.0.0") ADD_VETH(p2, at_ns2, br0, "0.0.0.0") dnl Push two flows to OVS. #1 will check on SPI=0X100, SI=2 and send the dnl packet to at_ns1. #2 will check on SPI=0X100, SI=1 and send the dnl packet to to at_ns2. AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x894f,nsh_spi=0x100,nsh_si=0x02,actions=ovs-p1"]) AT_CHECK([ovs-ofctl -Oopenflow13 add-flow br0 "table=0,priority=100,dl_type=0x894f,nsh_spi=0x100,nsh_si=0x01,actions=ovs-p2"]) NETNS_DAEMONIZE([at_ns1], [tcpdump -l -n -xx -U -i p1 -w p1.pcap 2>tcpdump_err], [tcpdump.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) NETNS_DAEMONIZE([at_ns2], [tcpdump -l -n -xx -U -i p2 -w p2.pcap 2>tcpdump2_err], [tcpdump2.pid]) OVS_WAIT_UNTIL([grep "listening" tcpdump2_err]) m4_define([TCP_SYN_PKT], [m4_join([,], [eth_src=f2:00:00:00:00:01,eth_dst=f2:00:00:00:00:02,eth_type=0x0800], [nw_src=192.168.0.10,nw_dst=10.0.0.10], [nw_proto=6,nw_ttl=64,nw_frag=no], [tcp_src=1024,tcp_dst=2048,tcp_flags=syn])]) dnl First send packet from at_ns0 --> OVS with SPI=0x100 and SI=2. m4_define([NSH_HEADER_1], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=8,nsh_np=3,nsh_spi=0x100,nsh_si=2,nsh_mdtype=1], [nsh_c1=0x01020304,nsh_c2=0x05060708,nsh_c3=0x090a0b0c,nsh_c4=0x0d0e0f10])]) NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 \ "$(ovs-ofctl compose-packet --bare 'NSH_HEADER_1')" \ "$(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')"], [0], [ignore]) dnl Check for the above packet on p1 interface. OVS_WAIT_UNTIL([ovs-pcap p1.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'NSH_HEADER_1'), $(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT'), [\$])"]) dnl Send the second packet from at_ns1 --> OVS with SPI=0x100 and SI=1. m4_define([NSH_HEADER_2], [m4_join([,], [eth_src=f2:ff:00:00:00:01,eth_dst=f2:ff:00:00:00:02,eth_type=0x894f], [nsh_ttl=8,nsh_np=3,nsh_spi=0x100,nsh_si=1,nsh_mdtype=1], [nsh_c1=0x01020304,nsh_c2=0x05060708,nsh_c3=0x090a0b0c,nsh_c4=0x0d0e0f10])]) NS_CHECK_EXEC([at_ns1], [$PYTHON3 $srcdir/sendpkt.py p1 \ "$(ovs-ofctl compose-packet --bare 'NSH_HEADER_2')" \ "$(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT')"], [0], [ignore]) dnl Check for the above packet on p2 interface. OVS_WAIT_UNTIL([ovs-pcap p2.pcap | grep -q "m4_join([], [^], $(ovs-ofctl compose-packet --bare 'NSH_HEADER_2'), $(ovs-ofctl compose-packet --bare 'TCP_SYN_PKT'), [\$])"]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_BANNER([local-sampling]) m4_define([SAMPLE_ACTION], [sample(probability=65535,collector_set_id=$1,obs_domain_id=$2,obs_point_id=$3)]dnl ) AT_SETUP([psample - sanity check]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ local-group-id=12], [0], [ignore]) AT_DATA([flows.txt], [dnl arp actions=NORMAL in_port=ovs-p0,ip actions=SAMPLE_ACTION(1, 2853183536, 2856341600),ovs-p1 in_port=ovs-p1,ip actions=SAMPLE_ACTION(2, 3138396208, 3141554272),ovs-p0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) AT_CHECK([ovs-appctl dpctl/dump-flows -m --names], [0], [stdout]) AT_CHECK([grep -q 'actions:psample(group=10,cookie=0xaa102030aa405060),ovs-p1' stdout]) AT_CHECK([grep -q 'actions:psample(group=12,cookie=0xbb102030bb405060),ovs-p0' stdout]) m4_define([SAMPLE1], [m4_join([ ], [group_id=0xa,prob=4294967295], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) m4_define([SAMPLE2], [m4_join([ ], [group_id=0xc,prob=4294967295], [obs_domain=0xbb102030,obs_point=0xbb405060], [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE1' psample.out]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE2' psample.out]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([psample - sanity check IPv6]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "fc00::1/96") ADD_VETH(p1, at_ns1, br0, "fc00::2/96") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ local-group-id=12], [0], [ignore]) AT_DATA([flows.txt], [dnl priority=100,in_port=ovs-p0,ip6,icmp6,icmpv6_type=128 actions=SAMPLE_ACTION(1, 2853183536, 2856341600),ovs-p1 priority=100,in_port=ovs-p1,ip6,icmp6,icmpv6_type=129 actions=SAMPLE_ACTION(2, 3138396208, 3141554272),ovs-p0 priority=0 actions=NORMAL ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) OVS_WAIT_UNTIL_EQUAL([ip netns exec at_ns0 ping6 -I fc00::1 -q -W 2 -c 1 fc00::2 | FORMAT_PING], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms]) AT_CHECK([ovs-appctl dpctl/dump-flows -m --names], [0], [stdout]) AT_CHECK([grep -q 'actions:psample(group=10,cookie=0xaa102030aa405060),ovs-p1' stdout]) AT_CHECK([grep -q 'actions:psample(group=12,cookie=0xbb102030bb405060),ovs-p0' stdout]) m4_define([SAMPLE1], [m4_join([ ], [group_id=0xa,prob=4294967295], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp6.*ipv6_src=fc00::1,ipv6_dst=fc00::2])]) m4_define([SAMPLE2], [m4_join([ ], [group_id=0xc,prob=4294967295], [obs_domain=0xbb102030,obs_point=0xbb405060], [.*icmp6.*ipv6_src=fc00::2,ipv6_dst=fc00::1])]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE1' psample.out]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE2' psample.out]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([psample - slow]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ local-group-id=12], [0], [ignore]) AT_DATA([flows.txt], [dnl arp actions=NORMAL in_port=ovs-p0,ip actions=SAMPLE_ACTION(1, 2853183536, 2856341600),output(port=ovs-p1,max_len=200) in_port=ovs-p1,ip actions=SAMPLE_ACTION(2, 3138396208, 3141554272),output(port=ovs-p0,max_len=200) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Disable datapath truncate support to force actions to run in slow path. AT_CHECK([ovs-appctl dpif/set-dp-features br0 trunc false], [0]) AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=ovs-p0,dl_src=e4:11:22:33:44:55,dl_dst=e4:11:22:33:44:66,dl_type=0x0800,nw_src=10.1.1.1,nw_dst=10.1.1.12'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [dnl Datapath actions: psample(group=10,cookie=0xaa102030aa405060),trunc(200),3 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) m4_define([SAMPLE1], [m4_join([ ], [group_id=0xa,prob=4294967295], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) m4_define([SAMPLE2], [m4_join([ ], [group_id=0xc,prob=4294967295], [obs_domain=0xbb102030,obs_point=0xbb405060], [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) AT_CHECK([grep -qE 'SAMPLE1' psample.out]) AT_CHECK([grep -qE 'SAMPLE2' psample.out]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([psample - slow with probability]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ local-group-id=10], [0], [ignore]) dnl A probability != 100% but still pretty high (99.99847%). This ensures that dnl the outer sample action is not optimized out. m4_define([PROBABLE_SAMPLE_ACTION], [sample(probability=65534,collector_set_id=$1,obs_domain_id=$2,obs_point_id=$3)]dnl ) AT_DATA([flows.txt], [dnl arp actions=NORMAL in_port=ovs-p0,ip actions=PROBABLE_SAMPLE_ACTION(1, 2853183536, 2856341600),output(port=ovs-p1,max_len=200) in_port=ovs-p1,ip actions=PROBABLE_SAMPLE_ACTION(1, 2853183536, 2856341600),output(port=ovs-p0,max_len=200) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Disable datapath truncate support to force actions to run in slow path. AT_CHECK([ovs-appctl dpif/set-dp-features br0 trunc false], [0]) AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=ovs-p0,dl_src=e4:11:22:33:44:55,dl_dst=e4:11:22:33:44:66,dl_type=0x0800,nw_src=10.1.1.1,nw_dst=10.1.1.12'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [dnl Datapath actions: sample(sample=100.0%,actions(psample(group=10,cookie=0xaa102030aa405060))),trunc(200),3 This flow is handled by the userspace slow path because it: - Uses action(s) not supported by datapath. ]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) dnl Sending 10 packets to decrease even more the odds of not sampling a packet. NS_CHECK_EXEC([at_ns0], [ping -q -i 0.1 -c 10 10.1.1.2 | FORMAT_PING], [0], [dnl 10 packets transmitted, 10 received, 0% packet loss, time 0ms ]) m4_define([SAMPLE], [m4_join([ ], [group_id=0xa,prob=4294901758], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) AT_CHECK([grep -qE 'SAMPLE' psample.out]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP AT_SETUP([psample - with IPFIX]) OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@i create IPFIX targets=\"127.0.0.1:4739\" \ -- create Flow_Sample_Collector_Set id=1 ipfix=@i \ bridge=@br0 local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 ipfix=@i \ bridge=@br0 local-group-id=12], [0], [ignore]) AT_DATA([flows.txt], [dnl arp actions=NORMAL in_port=ovs-p0,ip actions=SAMPLE_ACTION(1, 2853183536, 2856341600),ovs-p1 in_port=ovs-p1,ip actions=SAMPLE_ACTION(2, 3138396208, 3141554272),ovs-p0 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 \ 'in_port=ovs-p0,dl_src=e4:11:22:33:44:55,dl_dst=e4:11:22:33:44:66,dl_type=0x0800,nw_src=10.1.1.1,nw_dst=10.1.1.12'], [0], [stdout]) m4_define([ACTIONS], [m4_join([], [psample(group=10,cookie=0xaa102030aa405060),], [userspace(pid=4294967295,], [flow_sample(probability=65535,], [collector_set_id=1,], [obs_domain_id=2853183536,], [obs_point_id=2856341600,], [output_port=4294967295)),], [3])]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: ACTIONS ]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) m4_define([SAMPLE1], [m4_join([ ], [group_id=0xa,prob=4294967295], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) m4_define([SAMPLE2], [m4_join([ ], [group_id=0xc,prob=4294967295], [obs_domain=0xbb102030,obs_point=0xbb405060], [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE1' psample.out]) OVS_WAIT_UNTIL([grep -qE 'SAMPLE2' psample.out]) dnl Check IPFIX samples have been received. dnl Entries can be unsorted and IFPIX packets might not have been sent (or dnl at least tried to be sent) yet. OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-ipfix-flow br0 | \ sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | \ sed 's/tx errs=[[0-9]]*/tx errs=0/' | \ sed 's/id [[1-2]]:/id ?:/'], [dnl NXST_IPFIX_FLOW reply (xid=0x2): 2 ids id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0 id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0]) dnl OVS will fail to send IPFIX packets because the target is localhost dnl and the port is closed. Ignore the message it generates. OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP AT_SETUP([psample - from ct label]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() OVS_CHECK_PSAMPLE() ADD_NAMESPACES(at_ns0, at_ns1) NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "e4:11:22:33:44:55") ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24", "e4:11:22:33:44:66") AT_CHECK([ovs-vsctl -- --id=@br0 get Bridge br0 \ -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4739\" \ -- create Flow_Sample_Collector_Set id=1 bridge=@br0 \ ipfix=@ipfix, local-group-id=10 \ -- create Flow_Sample_Collector_Set id=2 bridge=@br0 \ ipfix=@ipfix, local-group-id=12], [0], [ignore]) m4_define([CT_STORE_ACT], [ct(zone=5,commit,exec(load:0x0bb102030->NXM_NX_CT_LABEL[[0..31]],load:0xbb405060->NXM_NX_CT_LABEL[[32..63]]))]) AT_DATA([flows.txt], [dnl priority=100,ip actions=ct(zone=5, table=10) priority=0 actions=NORMAL table=10,priority=100,ip,ct_state=+trk+new action=SAMPLE_ACTION(1, 2853183536, 2856341600),CT_STORE_ACT,NORMAL table=10,priority=100,ip,ct_state=+trk-new action=SAMPLE_ACTION(2, NXM_NX_CT_LABEL[[[0..31]]], NXM_NX_CT_LABEL[[[32..63]]]),NORMAL table=10, priority=50, ip, actions=DROP ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) OVS_DAEMONIZE([ovstest test-psample > psample.out], [psample1.pid]) OVS_WAIT_UNTIL([grep -q "Listening for psample events" psample.out]) NS_CHECK_EXEC([at_ns0], [ping -q -c 1 10.1.1.2 | FORMAT_PING], [0], [dnl 1 packets transmitted, 1 received, 0% packet loss, time 0ms ]) m4_define([SAMPLE1], [m4_join([ ], [group_id=0xa,prob=4294967295], [obs_domain=0xaa102030,obs_point=0xaa405060], [.*icmp.*nw_src=10.1.1.1,nw_dst=10.1.1.2])]) m4_define([SAMPLE2], [m4_join([ ], [group_id=0xc,prob=4294967295], [obs_domain=0xbb102030,obs_point=0xbb405060], [.*icmp.*nw_src=10.1.1.2,nw_dst=10.1.1.1])]) AT_CHECK([grep -qE 'SAMPLE1' psample.out]) AT_CHECK([grep -qE 'SAMPLE2' psample.out]) m4_define([FLOW_MATCH], [m4_join([], [ct_label(0xbb405060bb102030/0xffffffffffffffff).*actions:], [actions:psample(group=12,cookie=0xbb102030bb405060),], [userspace(pid=[[0-9]]+,flow_sample(.*obs_domain_id=3138396208,obs_point_id=3141554272.*))] )]) AT_CHECK([ovs-appctl dpctl/dump-flows --names filter=in_port=ovs-p1 \ | grep -qE 'FLOW_MATCH' ], [0], []) dnl Check IPFIX samples have been received. dnl Entries can be unsorted and IFPIX packets might not have been sent (or dnl at least tried to be sent) yet. OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-ipfix-flow br0 | \ sed 's/tx pkts=[[0-9]]*/tx pkts=24/' | \ sed 's/tx errs=[[0-9]]*/tx errs=0/' | \ sed 's/id [[1-2]]:/id ?:/'], [dnl NXST_IPFIX_FLOW reply (xid=0x2): 2 ids id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0 id ?: flows=1, current flows=0, sampled pkts=1, ipv4 ok=1, ipv6 ok=0, tx pkts=24 pkts errs=0, ipv4 errs=0, ipv6 errs=0, tx errs=0]) dnl OVS will fail to send IPFIX packets because the target is localhost dnl and the port is closed. Ignore the message it generates. OVS_TRAFFIC_VSWITCHD_STOP(["/sending to collector failed/d"]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-tso-macros.at000066400000000000000000000030651514270232600240650ustar00rootroot00000000000000# _ADD_BR([name]) # # Expands into the proper ovs-vsctl commands to create a bridge with the # appropriate type and properties m4_define([_ADD_BR], [[add-br $1 -- set Bridge $1 datapath_type="netdev" protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15 fail-mode=secure ]]) # OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args]) # # Creates a database and starts ovsdb-server, starts ovs-vswitchd # connected to that database, calls ovs-vsctl to create a bridge named # br0 with predictable settings, passing 'vsctl-args' as additional # commands to ovs-vsctl. If 'vsctl-args' causes ovs-vsctl to provide # output (e.g. because it includes "create" commands) then 'vsctl-output' # specifies the expected output after filtering through uuidfilt. # 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init' # before starting ovs-vswitchd. m4_define([OVS_TRAFFIC_VSWITCHD_START], [ OVS_WAIT_WHILE([ip link show ovs-netdev]) _OVS_VSWITCHD_START([--disable-system], [$3]) dnl Add bridges, ports, etc. OVS_WAIT_WHILE([ip link show br0]) AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:userspace-tso-enable=true]) AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) ]) # CONFIGURE_VETH_OFFLOADS([VETH]) # # Enable TCP segmentation offload and scatter-gather for veths. m4_define([CONFIGURE_VETH_OFFLOADS], [AT_CHECK([ethtool -K $1 sg on], [0], [ignore], [ignore])] [AT_CHECK([ethtool -K $1 tso on], [0], [ignore], [ignore])] ) m4_define([CHECK_SYSTEM_TSO], []) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-tso-testsuite.at000066400000000000000000000016571514270232600246370ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2020 VMware, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-userspace-macros.at]) m4_include([tests/system-tso-macros.at]) m4_include([tests/system-tap.at]) m4_include([tests/system-traffic.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-userspace-macros.at000066400000000000000000000302431514270232600252500ustar00rootroot00000000000000# _ADD_BR([name]) # # Expands into the proper ovs-vsctl commands to create a bridge with the # appropriate type and properties m4_define([_ADD_BR], [[add-br $1 -- set Bridge $1 datapath_type="netdev" protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15 fail-mode=secure ]]) # OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [dbinit-aux-args]) # # Creates a database and starts ovsdb-server, starts ovs-vswitchd # connected to that database, calls ovs-vsctl to create a bridge named # br0 with predictable settings, passing 'vsctl-args' as additional # commands to ovs-vsctl. If 'vsctl-args' causes ovs-vsctl to provide # output (e.g. because it includes "create" commands) then 'vsctl-output' # specifies the expected output after filtering through uuidfilt. # 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init' # before starting ovs-vswitchd. m4_define([OVS_TRAFFIC_VSWITCHD_START], [ OVS_WAIT_WHILE([ip link show ovs-netdev]) _OVS_VSWITCHD_START([--disable-system], [$3]) dnl Add bridges, ports, etc. OVS_WAIT_WHILE([ip link show br0]) AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2]) ]) # OVS_TRAFFIC_VSWITCHD_STOP([ALLOWLIST], [extra_cmds]) # # Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files # for messages with severity WARN or higher and signaling an error if any # is present. The optional ALLOWLIST may contain shell-quoted "sed" # commands to delete any warnings that are actually expected, e.g.: # # OVS_TRAFFIC_VSWITCHD_STOP(["/expected error/d"]) # # 'extra_cmds' are shell commands to be executed after OVS_VSWITCHD_STOP() is # invoked. They can be used to perform additional cleanups such as name space # removal. m4_define([OVS_TRAFFIC_VSWITCHD_STOP], [OVS_VSWITCHD_STOP([dnl $1";/netdev_linux.*obtaining netdev stats via vport failed/d /dpif_netlink.*Generic Netlink family 'ovs_datapath' does not exist. The Open vSwitch kernel module is probably not loaded./d /dpif_netdev(revalidator.*)|ERR|internal error parsing flow key.*proto=2.*/d /dpif(revalidator.*)|WARN|netdev@ovs-netdev: failed to.*proto=2.*/d"]) AT_CHECK([:; $2]) ]) # CONFIGURE_VETH_OFFLOADS([VETH]) # # Disable TX offloads for veths. The userspace datapath uses the AF_PACKET # socket to receive packets for veths. Unfortunately, the AF_PACKET socket # doesn't play well with offloads: # 1. GSO packets are received without segmentation and therefore discarded. # 2. Packets with offloaded partial checksum are received with the wrong # checksum, therefore discarded by the receiver. # # By disabling tx offloads in the non-OVS side of the veth peer we make sure # that the AF_PACKET socket will not receive bad packets. # # This is a workaround, and should be removed when offloads are properly # supported in netdev-linux. m4_define([CONFIGURE_VETH_OFFLOADS], [AT_CHECK([ethtool -K $1 tx off], [0], [ignore], [ignore])] ) # CHECK_CONNTRACK() # # Perform requirements checks for running conntrack tests. # m4_define([CHECK_CONNTRACK], []) # CHECK_CONNTRACK_ALG() # # Perform requirements checks for running conntrack ALG tests. The userspace # supports FTP and TFTP. # m4_define([CHECK_CONNTRACK_ALG]) # CHECK_CONNTRACK_LOCAL_STACK() # # Perform requirements checks for running conntrack tests with local stack. # While the kernel connection tracker automatically passes all the connection # tracking state from an internal port to the OpenvSwitch kernel module, there # is simply no way of doing that with the userspace, so skip the tests. m4_define([CHECK_CONNTRACK_LOCAL_STACK], [ AT_SKIP_IF([:]) ]) # CHECK_CONNTRACK_FRAG_OVERLAP() # # The userspace datapath supports fragment overlap check. m4_define([CHECK_CONNTRACK_FRAG_OVERLAP]) # CHECK_CONNTRACK_NAT() # # Perform requirements checks for running conntrack NAT tests. The userspace # datapath supports NAT. # m4_define([CHECK_CONNTRACK_NAT]) # CHECK_CONNTRACK_ZEROIP_SNAT() # # Perform requirements checks for running conntrack all-zero IP SNAT tests. # The userspace datapath always supports all-zero IP SNAT, so no check is # needed. # m4_define([CHECK_CONNTRACK_ZEROIP_SNAT]) # CHECK_CONNTRACK_SCTP() # # Perform requirements checks for running conntrack SCTP. The userspace # datapath has no dependency, so no check is required. # m4_define([CHECK_CONNTRACK_SCTP]) # CHECK_CONNTRACK_TIMEOUT() # # Perform requirements checks for running conntrack customized timeout tests. # m4_define([CHECK_CONNTRACK_TIMEOUT]) # CHECK_CONNTRACK_DUMP_EXPECTATIONS() # # Perform requirements checks for dumping conntrack expectations. # m4_define([CHECK_CONNTRACK_DUMP_EXPECTATIONS]) # CHECK_CT_DPIF_SET_GET_MAXCONNS() # # Perform requirements checks for running ovs-dpctl ct-set-maxconns or # ovs-dpctl ct-get-maxconns. The userspace datapath does support this feature. m4_define([CHECK_CT_DPIF_SET_GET_MAXCONNS]) # CHECK_CT_DPIF_GET_NCONNS() # # Perform requirements checks for running ovs-dpctl ct-get-nconns. The # userspace datapath does support this feature. m4_define([CHECK_CT_DPIF_GET_NCONNS]) # DPCTL_SET_MIN_FRAG_SIZE() # # The userspace datapath supports this command. m4_define([DPCTL_SET_MIN_FRAG_SIZE], [ AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 400], [], [dnl setting minimum fragment size successful ]) AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v6 400], [], [dnl setting minimum fragment size successful ]) ]) # DPCTL_MODIFY_FRAGMENTATION() # # The userspace datapath supports this command. m4_define([DPCTL_MODIFY_FRAGMENTATION], [ AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 1000], [], [dnl setting minimum fragment size successful ]) AT_CHECK([ovs-appctl dpctl/ipf-set-max-nfrags 500], [], [dnl setting maximum fragments successful ]) AT_CHECK([ovs-appctl dpctl/ipf-get-status], [], [dnl Fragmentation Module Status --------------------------- v4 enabled: 1 v6 enabled: 1 max num frags (v4/v6): 500 num frag: 0 min v4 frag size: 1000 v4 frags accepted: 0 v4 frags completed: 0 v4 frags expired: 0 v4 frags too small: 0 v4 frags overlapped: 0 v4 frags purged: 0 min v6 frag size: 1280 v6 frags accepted: 0 v6 frags completed: 0 v6 frags expired: 0 v6 frags too small: 0 v6 frags overlapped: 0 v6 frags purged: 0 ]) ]) # DPCTL_CHECK_FRAGMENTATION_PASS() # # Used to check fragmentation counters for some fragmentation tests using # the userspace datapath. m4_define([DPCTL_CHECK_FRAGMENTATION_PASS], [ AT_CHECK([ovs-appctl dpctl/ipf-get-status --more], [], [dnl Fragmentation Module Status --------------------------- v4 enabled: 1 v6 enabled: 1 max num frags (v4/v6): 500 num frag: 0 min v4 frag size: 1000 v4 frags accepted: 30 v4 frags completed: 30 v4 frags expired: 0 v4 frags too small: 0 v4 frags overlapped: 0 v4 frags purged: 0 min v6 frag size: 1280 v6 frags accepted: 0 v6 frags completed: 0 v6 frags expired: 0 v6 frags too small: 0 v6 frags overlapped: 0 v6 frags purged: 0 Fragment Lists: ]) ]) # DPCTL_CHECK_V6_FRAGMENTATION_PASS() # # Used to check fragmentation counters for some fragmentation tests using # the userspace datapath. m4_define([DPCTL_CHECK_V6_FRAGMENTATION_PASS], [ AT_CHECK([ovs-appctl dpctl/ipf-get-status --more], [], [dnl Fragmentation Module Status --------------------------- v4 enabled: 1 v6 enabled: 1 max num frags (v4/v6): 1000 num frag: 0 min v4 frag size: 1200 v4 frags accepted: 0 v4 frags completed: 0 v4 frags expired: 0 v4 frags too small: 0 v4 frags overlapped: 0 v4 frags purged: 0 min v6 frag size: 1280 v6 frags accepted: 30 v6 frags completed: 30 v6 frags expired: 0 v6 frags too small: 0 v6 frags overlapped: 0 v6 frags purged: 0 Fragment Lists: ]) ]) # FORMAT_FRAG_LIST([]) # # Strip content from the piped input which can differ from test to test; recirc_id # and ip_id fields in an ipf_list vary from test to test and hence are cleared. m4_define([FORMAT_FRAG_LIST], [[sed -e 's/ip_id=[0-9]*/ip_id=/g' -e 's/recirc_id=[0-9]*/recirc_id=/g']]) # DPCTL_CHECK_FRAGMENTATION_FAIL() # # Used to check fragmentation counters for some fragmentation tests using # the userspace datapath, when failure to transmit fragments is expected. m4_define([DPCTL_CHECK_FRAGMENTATION_FAIL], [ AT_CHECK([ovs-appctl dpctl/ipf-get-status -m | FORMAT_FRAG_LIST()], [], [dnl Fragmentation Module Status --------------------------- v4 enabled: 1 v6 enabled: 1 max num frags (v4/v6): 500 num frag: 7 min v4 frag size: 1000 v4 frags accepted: 7 v4 frags completed: 0 v4 frags expired: 0 v4 frags too small: 0 v4 frags overlapped: 0 v4 frags purged: 0 min v6 frag size: 1280 v6 frags accepted: 0 v6 frags completed: 0 v6 frags expired: 0 v6 frags too small: 0 v6 frags overlapped: 0 v6 frags purged: 0 Fragment Lists: (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) (src=10.1.1.1,dst=10.1.1.2,recirc_id=,ip_id=,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag) ]) ]) # OVS_CHECK_FRAG_LARGE() # # The userspace needs to check that ipf larger fragments have occurred. m4_define([OVS_CHECK_FRAG_LARGE], [ OVS_WAIT_UNTIL([grep -Eq 'Unsupported big reassembled (v4|v6) packet' ovs-vswitchd.log]) ]) # OVS_CHECK_MIN_KERNEL([minversion], [maxversion]) # # The userspace skips all tests that check kernel version. m4_define([OVS_CHECK_MIN_KERNEL], [ AT_SKIP_IF([:]) ]) # OVS_CHECK_KERNEL_EXCL([minversion], [maxversion], [minsublevel], [maxsublevel]) # # The userspace skips all tests that check kernel version. m4_define([OVS_CHECK_KERNEL_EXCL], [ AT_SKIP_IF([:]) ]) # OVS_CHECK_SRV6() m4_define([OVS_CHECK_SRV6], [AT_SKIP_IF([! ip -6 route add fc00::1/96 encap seg6 mode encap dev lo 2>&1 >/dev/null]) AT_CHECK([ip -6 route del fc00::1/96 2>&1 >/dev/null]) OVS_CHECK_FIREWALL()]) # CHECK_LATER_IPV6_FRAGMENTS() # # Userspace is parsing later IPv6 fragments correctly. m4_define([CHECK_LATER_IPV6_FRAGMENTS], []) # VSCTL_ADD_DATAPATH_TABLE() # # Create datapath table "netdev" for userspace tests in ovsdb m4_define([VSCTL_ADD_DATAPATH_TABLE], [ AT_CHECK([ovs-vsctl -- --id=@m create Datapath datapath_version=0 -- set Open_vSwitch . datapaths:"netdev"=@m], [0], [stdout]) DP_TYPE=$(echo "netdev") ]) # CHECK_L3L4_CONNTRACK_REASM() # # Only allow this test to run on the kernel datapath - it is not useful # or necessary for the userspace datapath as it is checking for a kernel # specific regression. m4_define([CHECK_L3L4_CONNTRACK_REASM], [ AT_SKIP_IF([:]) ]) # CHECK_NO_DPDK_OFFLOAD # # Plain userspace tests do not use dpdk's rte_flow offload. m4_define([CHECK_NO_DPDK_OFFLOAD]) # CHECK_NO_TC_OFFLOAD # # Userspace tests do not use TC offload. m4_define([CHECK_NO_TC_OFFLOAD]) # OVS_CHECK_BAREUDP() # # The userspace datapath does not support bareudp tunnels. m4_define([OVS_CHECK_BAREUDP], [ AT_SKIP_IF([:]) ]) # CHECK_EXTERNAL_CT() # # The userspace datapath does not support external ct. m4_define([CHECK_EXTERNAL_CT], [ AT_SKIP_IF([:]) ]) # ADD_EXTERNAL_CT() # # The userspace datapath does not support external ct. m4_define([ADD_EXTERNAL_CT], [ AT_SKIP_IF([:]) ]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-userspace-packet-type-aware.at000066400000000000000000000413611514270232600273120ustar00rootroot00000000000000AT_BANNER([packet-type-aware pipeline]) AT_SETUP([ptap - triangle bridge setup with L2 and L3 GRE tunnels]) ######################## # GRE tunneling test setup for PTAP bridge # # 192.168.10.10 192.168.10.20 192.168.10.30 # n1 n2 n3 # |ovs-n1 |ovs-n2 |ovs-n3 # +------o------+ +------o------+ +------o------+ # | br-in1 | | br-in2 | | br-in3 | # | | | (PTAP) | | | # +------o------+ +------o------+ +------o------+ # gre gre gre # 10.0.0.1 (10.0.0.2) (10.0.0.3) # (20.0.0.1) 20.0.0.2 (20.0.0.3) # (30.0.0.1) LOCAL (30.0.0.2) LOCAL 30.0.0.3 LOCAL # +-----------o-+ +-----------o-+ +-----------o-+ # | br-p1 | | br-p2 | | br-p3 | # +------o------+ +------o------+ +------o------+ # p1-0 | | p2-0 | p3-0 # p0-1 | | p0-2 | p0-3 # +--o------------------------o-------------------------o--+ # | br0 | # +--------------------------------------------------------+ #" # GRE tunnel ports: # No Bridge Name Packet-type Remote bridge & ports # ----------------------------------------------------------------------- # 1020 br-in1 gre-12 l2 br-in2 2010 (ptap) # 1021 br-in1 gre-12_l3 l3 same # 1030 br-in1 gre-13 l2 br-in3 3010 (l2) # 2010 br-in2 gre-21 ptap br-in1 1020 (l2), 1021 (l3) # 2030 br-in2 gre-23 ptap br-in3 3020 (l2), 3021 (l3) # 3010 br-in3 gre-31 l2 br-in1 1030 (l2) # 3020 br-in3 gre-32 l2 br-in2 2010 (ptap) # 3021 br-in3 gre-32_l3 l3 same AT_SKIP_IF([test $HAVE_NC = no]) OVS_TRAFFIC_VSWITCHD_START() HWADDR_BRP1=aa:55:00:00:00:01 HWADDR_BRP2=aa:55:00:00:00:02 HWADDR_BRP3=aa:55:00:00:00:03 dnl Create veth ports to connect br0 with br-p1, br-p2 and br-p3 AT_CHECK([ip link add p1-0 type veth peer name p0-1]) AT_CHECK([ip link set p1-0 up]) AT_CHECK([ip link set p0-1 up]) AT_CHECK([ip link set dev p1-0 mtu 3300]) AT_CHECK([ip link set dev p0-1 mtu 3300]) on_exit 'ip link del p0-1' AT_CHECK([ip link add p2-0 type veth peer name p0-2]) AT_CHECK([ip link set p2-0 up]) AT_CHECK([ip link set p0-2 up]) AT_CHECK([ip link set dev p2-0 mtu 3300]) AT_CHECK([ip link set dev p0-2 mtu 3300]) on_exit 'ip link del p0-2' AT_CHECK([ip link add p3-0 type veth peer name p0-3]) AT_CHECK([ip link set p3-0 up]) AT_CHECK([ip link set p0-3 up]) AT_CHECK([ip link set dev p3-0 mtu 3300]) AT_CHECK([ip link set dev p0-3 mtu 3300]) on_exit 'ip link del p0-3' # Setup bridge infrastructure AT_CHECK([ ovs-vsctl add-br br-in1 -- \ set bridge br-in1 datapath_type=netdev fail-mode=standalone ovs-vsctl add-br br-in2 -- \ set bridge br-in2 datapath_type=netdev fail-mode=standalone ovs-vsctl add-br br-in3 -- \ set bridge br-in3 datapath_type=netdev fail-mode=standalone ovs-vsctl add-br br-p1 -- \ set bridge br-p1 datapath_type=netdev fail-mode=standalone other-config:hwaddr=$HWADDR_BRP1 ovs-vsctl add-br br-p2 -- \ set bridge br-p2 datapath_type=netdev fail-mode=standalone other-config:hwaddr=$HWADDR_BRP2 ovs-vsctl add-br br-p3 -- \ set bridge br-p3 datapath_type=netdev fail-mode=standalone other-config:hwaddr=$HWADDR_BRP3 ovs-vsctl add-port br-p1 p1-0 -- set interface p1-0 ofport_request=2 ovs-vsctl add-port br-p2 p2-0 -- set interface p2-0 ofport_request=2 ovs-vsctl add-port br-p3 p3-0 -- set interface p3-0 ofport_request=2 ovs-vsctl add-port br0 p0-1 -- set interface p0-1 ofport_request=10 ovs-vsctl add-port br0 p0-2 -- set interface p0-2 ofport_request=20 ovs-vsctl add-port br0 p0-3 -- set interface p0-3 ofport_request=30 # Populate the MAC table of br0 ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP1,actions=10 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP2,actions=20 ovs-ofctl add-flow br0 dl_dst=$HWADDR_BRP3,actions=30 ovs-ofctl del-flows br-in1 ovs-ofctl del-flows br-in2 ovs-ofctl del-flows br-in3 ovs-ofctl del-flows br-p1 ovs-ofctl del-flows br-p2 ovs-ofctl del-flows br-p3 ], [0]) ### Setup GRE tunnels AT_CHECK([ ovs-vsctl add-port br-in1 gre12 -- \ set interface gre12 type=gre options:remote_ip=10.0.0.2 ofport_request=1020 ovs-vsctl add-port br-in1 gre12_l3 -- \ set interface gre12_l3 type=gre options:remote_ip=10.0.0.2 ofport_request=1021 options:packet_type=legacy_l3 ovs-vsctl add-port br-in1 gre13 -- \ set interface gre13 type=gre options:remote_ip=10.0.0.3 ofport_request=1030 ovs-vsctl add-port br-in2 gre21 -- \ set interface gre21 type=gre options:remote_ip=20.0.0.1 ofport_request=2010 options:packet_type=ptap ovs-vsctl add-port br-in2 gre23 -- \ set interface gre23 type=gre options:remote_ip=20.0.0.3 ofport_request=2030 options:packet_type=ptap ovs-vsctl add-port br-in3 gre31 -- \ set interface gre31 type=gre options:remote_ip=30.0.0.1 ofport_request=3010 ovs-vsctl add-port br-in3 gre32 -- \ set interface gre32 type=gre options:remote_ip=30.0.0.2 ofport_request=3020 ovs-vsctl add-port br-in3 gre32_l3 -- \ set interface gre32_l3 type=gre options:remote_ip=30.0.0.2 ofport_request=3021 options:packet_type=legacy_l3 ], [0], [stdout]) AT_CHECK([ ip addr add 10.0.0.1/24 dev br-p1 ip link set br-p1 up ], [0], [stdout]) OVS_WAIT_UNTIL([ovs-appctl ovs/route/show | grep -q br-p1]) AT_CHECK([ ovs-appctl ovs/route/add 10.0.0.0/24 br-p1 ovs-appctl tnl/arp/set br-p1 10.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p1 10.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p1 10.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ip addr add 20.0.0.2/24 dev br-p2 ip link set br-p2 up ], [0], [stdout]) OVS_WAIT_UNTIL([ovs-appctl ovs/route/show | grep -q br-p2]) AT_CHECK([ ovs-appctl ovs/route/add 20.0.0.0/24 br-p2 ovs-appctl tnl/arp/set br-p2 20.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p2 20.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p2 20.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ip addr add 30.0.0.3/24 dev br-p3 ip link set br-p3 up ], [0], [stdout]) OVS_WAIT_UNTIL([ovs-appctl ovs/route/show | grep -q br-p3]) AT_CHECK([ ovs-appctl ovs/route/add 30.0.0.0/24 br-p3 ovs-appctl tnl/arp/set br-p3 30.0.0.1 $HWADDR_BRP1 ovs-appctl tnl/arp/set br-p3 30.0.0.2 $HWADDR_BRP2 ovs-appctl tnl/arp/set br-p3 30.0.0.3 $HWADDR_BRP3 ], [0], [stdout]) AT_CHECK([ ovs-appctl ovs/route/show | grep User: ], [0], [dnl User: 10.0.0.0/24 dev br-p1 SRC 10.0.0.1 User: 20.0.0.0/24 dev br-p2 SRC 20.0.0.2 User: 30.0.0.0/24 dev br-p3 SRC 30.0.0.3 ]) AT_CHECK([ ovs-appctl tnl/neigh/show | grep br-p | sort ], [0], [stdout]) ### Flows in br-pto twist TEP IP addresses in tunnel IP headers AT_CHECK([ ovs-ofctl add-flow br-p1 in_port:LOCAL,ip,actions=2 ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:20.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ovs-ofctl add-flow br-p1 in_port:2,ip,nw_dst:30.0.0.1,actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL ovs-ofctl add-flow br-p2 in_port:LOCAL,ip,actions=2 ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:10.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ovs-ofctl add-flow br-p2 in_port:2,ip,nw_dst:30.0.0.2,actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL ovs-ofctl add-flow br-p3 in_port:LOCAL,ip,actions=2 ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:10.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ovs-ofctl add-flow br-p3 in_port:2,ip,nw_dst:20.0.0.3,actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ], [0]) # Strips 'n_packets=...' from ovs-ofctl output. strip_n_packets () { sed 's/n_packets=[[0-9]]*, //' } # Strips 'n_bytes=...' from ovs-ofctl output. strip_n_bytes () { sed 's/n_bytes=[[0-9]]*, //' } AT_CHECK([ ovs-ofctl dump-flows br-p1 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ovs-ofctl dump-flows br-p2 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ovs-ofctl dump-flows br-p3 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ], [0], [dnl ip,in_port=2,nw_dst=20.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.2,LOCAL ip,in_port=2,nw_dst=30.0.0.1 actions=mod_nw_dst:10.0.0.1,mod_nw_src:10.0.0.3,LOCAL ip,in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.1,LOCAL ip,in_port=2,nw_dst=30.0.0.2 actions=mod_nw_dst:20.0.0.2,mod_nw_src:20.0.0.3,LOCAL ip,in_port=LOCAL actions=output:2 ip,in_port=2,nw_dst=10.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.1,LOCAL ip,in_port=2,nw_dst=20.0.0.3 actions=mod_nw_dst:30.0.0.3,mod_nw_src:30.0.0.2,LOCAL ip,in_port=LOCAL actions=output:2 ]) ### Setup test ports for traffic injection N1_IP=192.168.10.10 N2_IP=192.168.10.20 N3_IP=192.168.10.30 N1_MAC=aa:55:aa:55:00:01 N2_MAC=aa:55:aa:55:00:02 N3_MAC=aa:55:aa:55:00:03 N1_OFPORT=10 N2_OFPORT=20 N3_OFPORT=30 ADD_NAMESPACES(ns1, ns2, ns3) ADD_VETH(n1, ns1, br-in1, "$N1_IP/24", $N1_MAC) ADD_VETH(n2, ns2, br-in2, "$N2_IP/24", $N2_MAC) ADD_VETH(n3, ns3, br-in3, "$N3_IP/24", $N3_MAC) NS_EXEC([ns1], [arp -s $N2_IP $N2_MAC]) NS_EXEC([ns1], [arp -s $N3_IP $N3_MAC]) NS_EXEC([ns2], [arp -s $N1_IP $N1_MAC]) NS_EXEC([ns2], [arp -s $N3_IP $N3_MAC]) NS_EXEC([ns3], [arp -s $N2_IP $N2_MAC]) NS_EXEC([ns3], [arp -s $N1_IP $N1_MAC]) AT_CHECK([ ovs-vsctl set interface ovs-n1 ofport_request=$N1_OFPORT ovs-vsctl set interface ovs-n2 ofport_request=$N2_OFPORT ovs-vsctl set interface ovs-n3 ofport_request=$N3_OFPORT ], [0]) #N1_DPPORT=$(ovs-appctl dpif/show | grep "n1 10" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N2_DPPORT=$(ovs-appctl dpif/show | grep "n2 20" | sed 's|.*/\([[0-9]]*\):.*|\1|') #N3_DPPORT=$(ovs-appctl dpif/show | grep "n3 30" | sed 's|.*/\([[0-9]]*\):.*|\1|') ### Verify datapath configuration AT_CHECK([ ovs-appctl dpif/show | grep -v hit ], [0], [dnl br-in1: br-in1 65534/2: (tap) gre12 1020/14: (gre: remote_ip=10.0.0.2) gre12_l3 1021/14: (gre: packet_type=legacy_l3, remote_ip=10.0.0.2) gre13 1030/14: (gre: remote_ip=10.0.0.3) ovs-n1 10/15: (system) br-in2: br-in2 65534/3: (tap) gre21 2010/14: (gre: packet_type=ptap, remote_ip=20.0.0.1) gre23 2030/14: (gre: packet_type=ptap, remote_ip=20.0.0.3) ovs-n2 20/16: (system) br-in3: br-in3 65534/4: (tap) gre31 3010/14: (gre: remote_ip=30.0.0.1) gre32 3020/14: (gre: remote_ip=30.0.0.2) gre32_l3 3021/14: (gre: packet_type=legacy_l3, remote_ip=30.0.0.2) ovs-n3 30/17: (system) br-p1: br-p1 65534/5: (tap) p1-0 2/8: (system) br-p2: br-p2 65534/6: (tap) p2-0 2/9: (system) br-p3: br-p3 65534/7: (tap) p3-0 2/10: (system) br0: br0 65534/1: (tap) p0-1 10/11: (system) p0-2 20/12: (system) p0-3 30/13: (system) ]) ### Test L3 forwarding flows AT_CHECK([ ovs-ofctl add-flow br-in1 ip,nw_dst=$N1_IP,actions=mod_dl_dst:$N1_MAC,$N1_OFPORT # Local route to N1 ovs-ofctl add-flow br-in1 ip,nw_dst=$N2_IP,actions=1020 # Route to N2 via the L2 tunnel to br-in2 ovs-ofctl add-flow br-in1 ip,nw_dst=$N3_IP,actions=1030 # Route to N3 direct through L2 tunnel ovs-ofctl add-flow br-in2 ip,nw_dst=$N2_IP,actions=mod_dl_dst:$N2_MAC,$N2_OFPORT # Local route to N2 for ethernet packets ovs-ofctl add-flow br-in2 ip,nw_dst=$N1_IP,actions=2010 # Route to N1 for ethernet packet ovs-ofctl add-flow br-in2 packet_type=\(1,0x800\),nw_dst=$N1_IP,actions=2010 # Route to N1 for IP packets ovs-ofctl add-flow br-in2 ip,nw_dst=$N3_IP,actions=2010 # Indirect route to N3 via br-in1 for ethernet packet ovs-ofctl add-flow br-in2 packet_type=\(1,0x800\),nw_dst=$N3_IP,actions=2030 # Direct route to N3 for IP packets ovs-ofctl add-flow br-in3 ip,nw_dst=$N3_IP,actions=mod_dl_dst:$N3_MAC,$N3_OFPORT # Local route to N1 ovs-ofctl add-flow br-in3 ip,nw_dst=$N2_IP,actions=3020 # Route to N2 via the L2 tunnel ovs-ofctl add-flow br-in3 ip,nw_dst=$N1_IP,actions=3021 # Route to N1 via br-in2 through L3 tunnel ], [0]) AT_CHECK([ ovs-ofctl dump-flows br-in1 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=mod_dl_dst:aa:55:aa:55:00:01,output:10 ip,nw_dst=192.168.10.20 actions=output:1020 ip,nw_dst=192.168.10.30 actions=output:1030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:2010 ip,nw_dst=192.168.10.20 actions=mod_dl_dst:aa:55:aa:55:00:02,output:20 ip,nw_dst=192.168.10.30 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 packet_type=(1,0x800),nw_dst=192.168.10.30 actions=output:2030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | strip_n_packets | strip_n_bytes | sort | grep actions ], [0], [dnl ip,nw_dst=192.168.10.10 actions=output:3021 ip,nw_dst=192.168.10.20 actions=output:3020 ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) # Ping between N1 and N3, via the L2 GRE tunnel between br-in1 and br-in3 NS_CHECK_EXEC([ns1], [ping -q -c 3 -i 0.3 -W 2 $N3_IP | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) sleep 1 AT_CHECK([ ovs-ofctl dump-flows br-in1 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=252, ip,nw_dst=192.168.10.10 actions=mod_dl_dst:aa:55:aa:55:00:01,output:10 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.30 actions=output:1030 ]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=252, packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.10 actions=output:3021 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) # Ping between N1 and N2, via the L2 GRE tunnel between br-in1 and br-in2 NS_CHECK_EXEC([ns1], [ping -q -c 3 -i 0.3 -W 2 $N2_IP | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) sleep 1 AT_CHECK([ ovs-ofctl dump-flows br-in1 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.20 actions=output:1020 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.30 actions=output:1030 n_packets=6, n_bytes=546, ip,nw_dst=192.168.10.10 actions=mod_dl_dst:aa:55:aa:55:00:01,output:10 ]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=252, packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.10 actions=output:2010 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.20 actions=mod_dl_dst:aa:55:aa:55:00:02,output:20 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.10 actions=output:3021 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) # Ping between N3 and N2, via the L3 GRE tunnel between br-in3 and br-in2 NS_CHECK_EXEC([ns3], [ping -q -c 3 -i 0.3 -W 2 $N1_IP | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) sleep 1 AT_CHECK([ ovs-ofctl dump-flows br-in1 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.20 actions=output:1020 n_packets=6, n_bytes=588, ip,nw_dst=192.168.10.30 actions=output:1030 n_packets=9, n_bytes=798, ip,nw_dst=192.168.10.10 actions=mod_dl_dst:aa:55:aa:55:00:01,output:10 ]) AT_CHECK([ ovs-ofctl dump-flows br-in2 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.10 actions=output:2010 n_packets=3, n_bytes=294, ip,nw_dst=192.168.10.20 actions=mod_dl_dst:aa:55:aa:55:00:02,output:20 n_packets=6, n_bytes=504, packet_type=(1,0x800),nw_dst=192.168.10.10 actions=output:2010 ]) AT_CHECK([ ovs-ofctl dump-flows br-in3 | ofctl_strip | sort | grep n_packets ], [0], [dnl n_packets=6, n_bytes=588, ip,nw_dst=192.168.10.10 actions=output:3021 n_packets=6, n_bytes=588, ip,nw_dst=192.168.10.30 actions=mod_dl_dst:aa:55:aa:55:00:03,output:30 ]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/system-userspace-testsuite.at000066400000000000000000000020271514270232600260140ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2015 Nicira, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/system-userspace-macros.at]) m4_include([tests/system-common-macros.at]) m4_include([tests/system-traffic.at]) m4_include([tests/system-layer3-tunnels.at]) m4_include([tests/system-interface.at]) m4_include([tests/system-userspace-packet-type-aware.at]) m4_include([tests/system-route.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-aa.c000066400000000000000000000233151514270232600216300ustar00rootroot00000000000000/* * Copyright (c) 2015 Avaya, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #undef NDEBUG #include #include #include #include #include "ovs-lldp.h" #include "ovstest.h" #define ETH_TYPE_LLDP 0x88cc /* Dummy MAC addresses */ static const struct eth_addr chassis_mac = ETH_ADDR_C(5e,10,8e,e7,84,ad); static const struct eth_addr eth_src = ETH_ADDR_C(5e,10,8e,e7,84,ad); /* LLDP multicast address */ static const struct eth_addr eth_addr_lldp = ETH_ADDR_C(01,80,c2,00,00,0e); /* Count of tests run */ static int num_tests = 0; /* * Helper function to validate port info */ static void check_received_port(struct lldpd_port *sport, struct lldpd_port *rport) { assert(rport->p_id_subtype == sport->p_id_subtype); assert(rport->p_id_len == sport->p_id_len); assert(strncmp(rport->p_id, sport->p_id, sport->p_id_len) == 0); assert(strcmp(rport->p_descr, sport->p_descr) == 0); } /* * Helper function to validate chassis info */ static void check_received_chassis(struct lldpd_chassis *schassis, struct lldpd_chassis *rchassis) { assert(rchassis->c_id_subtype == schassis->c_id_subtype); assert(rchassis->c_id_len == schassis->c_id_len); assert(memcmp(rchassis->c_id, schassis->c_id, schassis->c_id_len) == 0); assert(strcmp(rchassis->c_name, schassis->c_name) == 0); assert(strcmp(rchassis->c_descr, schassis->c_descr) == 0); assert(rchassis->c_cap_available == schassis->c_cap_available); assert(rchassis->c_cap_enabled == schassis->c_cap_enabled); } /* * Helper function to validate auto-attach info */ static void check_received_aa(struct lldpd_port *sport, struct lldpd_port *rport, struct lldpd_aa_isid_vlan_maps_tlv *smap) { struct lldpd_aa_isid_vlan_maps_tlv *received_map; int i = 0; assert(rport->p_element.type == sport->p_element.type); assert(rport->p_element.mgmt_vlan == sport->p_element.mgmt_vlan); assert(eth_addr_equals(rport->p_element.system_id.system_mac, sport->p_element.system_id.system_mac)); assert(rport->p_element.system_id.conn_type == sport->p_element.system_id.conn_type); assert(rport->p_element.system_id.rsvd == sport->p_element.system_id.rsvd); assert(rport->p_element.system_id.rsvd2[0] == sport->p_element.system_id.rsvd2[0]); assert(rport->p_element.system_id.rsvd2[1] == sport->p_element.system_id.rsvd2[1]); /* Should receive 2 mappings */ assert(!ovs_list_is_empty(&rport->p_isid_vlan_maps)); /* For each received isid/vlan mapping */ LIST_FOR_EACH (received_map, m_entries, &rport->p_isid_vlan_maps) { /* Validate against mapping sent */ assert(smap[i].isid_vlan_data.status == received_map->isid_vlan_data.status); assert(smap[i].isid_vlan_data.vlan == received_map->isid_vlan_data.vlan); assert(smap[i].isid_vlan_data.isid == received_map->isid_vlan_data.isid); /* Next mapping sent */ i++; } assert(i == 2); } /* * Validate basic send/receive processing */ static int test_aa_send(void) { struct lldp *lldp; struct lldpd_hardware hardware; struct lldpd_chassis chassis; struct lldpd_chassis *nchassis = NULL; struct lldpd_port *nport = NULL; struct lldpd_hardware *hw = NULL; struct lldpd_chassis *ch = NULL; struct lldpd_aa_isid_vlan_maps_tlv map_init[2]; struct lldpd_aa_isid_vlan_maps_tlv map[2]; uint32_t stub[512 / 4]; struct dp_packet packet; int n; /* Prepare data used to construct and validate LLDPPDU */ hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; hardware.h_lport.p_id = "FastEthernet 1/5"; hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id); hardware.h_lport.p_descr = "Fake port description"; hardware.h_lport.p_mfs = 1516; /* Auto attach element discovery info */ hardware.h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_CLIENT_VIRTUAL_SWITCH; hardware.h_lport.p_element.mgmt_vlan = 0xCDC; eth_addr_from_uint64(0x010203040506ULL, &hardware.h_lport.p_element.system_id.system_mac); hardware.h_lport.p_element.system_id.conn_type = 0x5; hardware.h_lport.p_element.system_id.rsvd = 0x3CC; hardware.h_lport.p_element.system_id.rsvd2[0] = 0xB; hardware.h_lport.p_element.system_id.rsvd2[1] = 0xE; /* Local chassis info */ chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; chassis.c_id = CONST_CAST(uint8_t *, chassis_mac.ea); chassis.c_id_len = ETH_ADDR_LEN; chassis.c_name = "Dummy chassis"; chassis.c_descr = "Long dummy chassis description"; chassis.c_cap_available = LLDP_CAP_BRIDGE; chassis.c_cap_enabled = LLDP_CAP_BRIDGE; /* ISID/VLAN mappings */ map_init[0].isid_vlan_data.status = 0xC; map_init[0].isid_vlan_data.vlan = 0x64; map_init[0].isid_vlan_data.isid = 0x010203; map_init[1].isid_vlan_data.status = 0xD; map_init[1].isid_vlan_data.vlan = 0xF; map_init[1].isid_vlan_data.isid = 0x040506; /* Prepare an empty packet buffer */ dp_packet_use_stub(&packet, stub, sizeof stub); dp_packet_clear(&packet); /* Create a dummy lldp instance */ lldp = lldp_create_dummy(); if ((lldp == NULL) || (lldp->lldpd == NULL) || ovs_list_is_empty(&lldp->lldpd->g_hardware)) { printf("Error: unable to create dummy lldp instance"); lldp_destroy_dummy(lldp); return 1; } /* Populate instance with local chassis info */ hw = lldpd_first_hardware(lldp->lldpd); ch = hw->h_lport.p_chassis; ch->c_id_subtype = chassis.c_id_subtype; ch->c_id = chassis.c_id; ch->c_id_len = chassis.c_id_len; ch->c_name = chassis.c_name; ch->c_descr = chassis.c_descr; ch->c_cap_available = chassis.c_cap_available; ch->c_cap_enabled = chassis.c_cap_enabled; /* Populate instance with local port info */ hw->h_lport.p_id_subtype = hardware.h_lport.p_id_subtype; hw->h_lport.p_id = hardware.h_lport.p_id; hw->h_lport.p_id_len = strlen(hw->h_lport.p_id); hw->h_lport.p_descr = hardware.h_lport.p_descr; hw->h_lport.p_mfs = hardware.h_lport.p_mfs; /* Populate instance with auto attach element discovery info */ hw->h_lport.p_element.type = hardware.h_lport.p_element.type; hw->h_lport.p_element.mgmt_vlan = hardware.h_lport.p_element.mgmt_vlan; hw->h_lport.p_element.system_id.system_mac = hardware.h_lport.p_element.system_id.system_mac; hw->h_lport.p_element.system_id.conn_type = hardware.h_lport.p_element.system_id.conn_type; hw->h_lport.p_element.system_id.rsvd = hardware.h_lport.p_element.system_id.rsvd; hw->h_lport.p_element.system_id.rsvd2[0] = hardware.h_lport.p_element.system_id.rsvd2[0]; hw->h_lport.p_element.system_id.rsvd2[1] = hardware.h_lport.p_element.system_id.rsvd2[1]; /* Populate instance with two auto attach isid/vlan mappings */ map[0].isid_vlan_data.status = map_init[0].isid_vlan_data.status; map[0].isid_vlan_data.vlan = map_init[0].isid_vlan_data.vlan; map[0].isid_vlan_data.isid = map_init[0].isid_vlan_data.isid; map[1].isid_vlan_data.status = map_init[1].isid_vlan_data.status; map[1].isid_vlan_data.vlan = map_init[1].isid_vlan_data.vlan; map[1].isid_vlan_data.isid = map_init[1].isid_vlan_data.isid; ovs_list_init(&hw->h_lport.p_isid_vlan_maps); ovs_list_push_back(&hw->h_lport.p_isid_vlan_maps, &map[0].m_entries); ovs_list_push_back(&hw->h_lport.p_isid_vlan_maps, &map[1].m_entries); /* Construct LLDPPDU (including Ethernet header) */ eth_compose(&packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, 0); n = lldp_send(lldp->lldpd, hw, &packet); if (n == 0) { printf("Error: unable to build packet\n"); lldp_destroy_dummy(lldp); return 1; } /* Decode the constructed LLDPPDU */ assert(lldp_decode(NULL, dp_packet_data(&packet), dp_packet_size(&packet), hw, &nchassis, &nport) != -1); /* Expecting returned pointers to allocated structures */ if (!nchassis || !nport) { printf("Error: unable to decode packet"); return 1; } /* Verify chassis values */ check_received_chassis(&chassis, nchassis); /* Verify port values */ check_received_port(&hardware.h_lport, nport); /* Verify auto attach values */ check_received_aa(&hardware.h_lport, nport, map_init); lldpd_chassis_cleanup(nchassis, true); lldpd_port_cleanup(nport, true); free(nport); lldp_destroy_dummy(lldp); return 0; } static void test_aa_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { int num_errors = 0; /* Make sure we emit valid auto-attach LLDPPDUs */ num_tests++; num_errors += test_aa_send(); /* Add more tests here */ printf("executed %d tests, %d errors\n", num_tests, num_errors); exit(num_errors != 0); } OVSTEST_REGISTER("test-aa", test_aa_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-aes128.c000066400000000000000000000033741514270232600222550ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2013, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "aes128.h" #include #include "ovstest.h" #include "util.h" static void hex_to_uint8(const char *input, uint8_t *output, size_t n) { size_t i; if (strlen(input) != n * 2) { goto error; } for (i = 0; i < n; i++) { bool ok; output[i] = hexits_value(&input[i * 2], 2, &ok); if (!ok) { goto error; } } return; error: ovs_fatal(0, "\"%s\" is not exactly %"PRIuSIZE" hex digits", input, n * 2); } static void test_aes128_main(int argc, char *argv[]) { struct aes128 aes; uint8_t plaintext[16]; uint8_t ciphertext[16]; uint8_t key[16]; size_t i; if (argc != 3) { ovs_fatal(0, "usage: %s KEY PLAINTEXT, where KEY and PLAINTEXT each " "consist of 32 hex digits", argv[0]); } hex_to_uint8(argv[1], key, 16); hex_to_uint8(argv[2], plaintext, 16); aes128_schedule(&aes, key); aes128_encrypt(&aes, plaintext, ciphertext); for (i = 0; i < 16; i++) { printf("%02x", ciphertext[i]); } putchar('\n'); } OVSTEST_REGISTER("test-aes128", test_aes128_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-atomic.c000066400000000000000000000410111514270232600225140ustar00rootroot00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "fatal-signal.h" #include "ovs-atomic.h" #include "ovstest.h" #include "ovs-thread.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(test_atomic); #define TEST_ATOMIC_TYPE(ATOMIC_TYPE, BASE_TYPE) \ { \ ATOMIC_TYPE x = 1; \ BASE_TYPE value, orig; \ \ atomic_read(&x, &value); \ ovs_assert(value == 1); \ \ atomic_store(&x, 2); \ atomic_read(&x, &value); \ ovs_assert(value == 2); \ \ atomic_init(&x, 3); \ atomic_read(&x, &value); \ ovs_assert(value == 3); \ \ atomic_add(&x, 1, &orig); \ ovs_assert(orig == 3); \ atomic_read(&x, &value); \ ovs_assert(value == 4); \ \ atomic_sub(&x, 2, &orig); \ ovs_assert(orig == 4); \ atomic_read(&x, &value); \ ovs_assert(value == 2); \ \ atomic_or(&x, 6, &orig); \ ovs_assert(orig == 2); \ atomic_read(&x, &value); \ ovs_assert(value == 6); \ \ atomic_and(&x, 10, &orig); \ ovs_assert(orig == 6); \ atomic_read(&x, &value); \ ovs_assert(value == 2); \ \ atomic_xor(&x, 10, &orig); \ ovs_assert(orig == 2); \ atomic_read(&x, &value); \ ovs_assert(value == 8); \ } #define TEST_ATOMIC_TYPE_EXPLICIT(ATOMIC_TYPE, BASE_TYPE, \ ORDER_READ, ORDER_STORE, ORDER_RMW) \ { \ ATOMIC_TYPE x = 1; \ BASE_TYPE value, orig; \ \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 1); \ \ atomic_store_explicit(&x, 2, ORDER_STORE); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 2); \ \ atomic_init(&x, 3); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 3); \ \ atomic_add_explicit(&x, 1, &orig, ORDER_RMW); \ ovs_assert(orig == 3); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 4); \ \ atomic_sub_explicit(&x, 2, &orig, ORDER_RMW); \ ovs_assert(orig == 4); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 2); \ \ atomic_or_explicit(&x, 6, &orig, ORDER_RMW); \ ovs_assert(orig == 2); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 6); \ \ atomic_and_explicit(&x, 10, &orig, ORDER_RMW); \ ovs_assert(orig == 6); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 2); \ \ atomic_xor_explicit(&x, 10, &orig, ORDER_RMW); \ ovs_assert(orig == 2); \ atomic_read_explicit(&x, &value, ORDER_READ); \ ovs_assert(value == 8); \ } #define TEST_ATOMIC_ORDER(ORDER_READ, ORDER_STORE, ORDER_RMW) \ { \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_char, char, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uchar, unsigned char, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_schar, signed char, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_short, short, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_ushort, unsigned short, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_int, int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint, unsigned int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_long, long int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_ulong, unsigned long int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_llong, long long int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_ullong, unsigned long long int, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_size_t, size_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_ptrdiff_t, ptrdiff_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_intmax_t, intmax_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintmax_t, uintmax_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_intptr_t, intptr_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintptr_t, uintptr_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint8_t, uint8_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_int8_t, int8_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint16_t, uint16_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_int16_t, int16_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint32_t, uint32_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ TEST_ATOMIC_TYPE_EXPLICIT(atomic_int32_t, int32_t, \ ORDER_READ, ORDER_STORE, ORDER_RMW); \ } static void test_atomic_flag(void) { atomic_flag flag = ATOMIC_FLAG_INIT; ovs_assert(atomic_flag_test_and_set(&flag) == false); ovs_assert(atomic_flag_test_and_set(&flag) == true); atomic_flag_clear(&flag); ovs_assert(atomic_flag_test_and_set(&flag) == false); } static uint32_t a; struct atomic_aux { ATOMIC(uint64_t) count; uint32_t b; ATOMIC(uint32_t *) data; ATOMIC(uint64_t) data64; }; static ATOMIC(struct atomic_aux *) paux = NULL; static struct atomic_aux *auxes = NULL; #define ATOMIC_ITEM_COUNT 1000000 #define DURATION 5000 static void * atomic_consumer(void * arg1 OVS_UNUSED) { struct atomic_aux *old_aux = NULL; uint64_t count; long long int stop_time = time_msec() + DURATION; do { struct atomic_aux *aux; uint32_t b; /* Wait for a new item. We may not be fast enough to process every * item, but we are guaranteed to see the last one. */ do { atomic_read_explicit(&paux, &aux, memory_order_consume); } while (aux == old_aux); b = aux->b; atomic_read_explicit(&aux->count, &count, memory_order_relaxed); ovs_assert(b == count + 42); old_aux = aux; } while (count < ATOMIC_ITEM_COUNT - 1 && time_msec() < stop_time); if (time_msec() >= stop_time) { if (count < 10) { VLOG_WARN("atomic_consumer test stopped due to excessive runtime. " "Count = %"PRIu64, count); } } return NULL; } static void * atomic_producer(void * arg1 OVS_UNUSED) { size_t i; for (i = 0; i < ATOMIC_ITEM_COUNT; i++) { struct atomic_aux *aux = &auxes[i]; aux->count = i; aux->b = i + 42; /* Publish the new item. */ atomic_store_explicit(&paux, aux, memory_order_release); } return NULL; } static void test_cons_rel(void) { pthread_t reader, writer; atomic_init(&paux, NULL); auxes = xmalloc(sizeof *auxes * ATOMIC_ITEM_COUNT); reader = ovs_thread_create("consumer", atomic_consumer, NULL); writer = ovs_thread_create("producer", atomic_producer, NULL); xpthread_join(reader, NULL); xpthread_join(writer, NULL); free(auxes); } static void * atomic_reader(void *aux_) { struct atomic_aux *aux = aux_; uint64_t count; uint64_t data; long long int now = time_msec(); long long int stop_time = now + DURATION; do { /* Non-synchronized add. */ atomic_add_explicit(&aux->count, 1, &count, memory_order_relaxed); do { atomic_read_explicit(&aux->data64, &data, memory_order_acquire); } while (!data && (now = time_msec()) < stop_time); if (now >= stop_time) { if (count < 10) { VLOG_WARN("atomic_reader test stopped due to excessive " "runtime. Count = %"PRIu64, count); } break; } ovs_assert(data == a && data == aux->b && a == aux->b); atomic_read_explicit(&aux->count, &count, memory_order_relaxed); ovs_assert(count == 2 * a && count == 2 * aux->b && count == 2 * data); atomic_store_explicit(&aux->data64, UINT64_C(0), memory_order_release); } while (count < 2 * ATOMIC_ITEM_COUNT); return NULL; } static void * atomic_writer(void *aux_) { struct atomic_aux *aux = aux_; uint64_t old_count; uint64_t data; size_t i; long long int now = time_msec(); long long int stop_time = now + DURATION; for (i = 0; i < ATOMIC_ITEM_COUNT; i++) { /* Wait for the reader to be done with the data. */ do { atomic_read_explicit(&aux->data64, &data, memory_order_acquire); } while (data && (now = time_msec()) < stop_time); if (now >= stop_time) { if (i < 10) { VLOG_WARN("atomic_writer test stopped due to excessive " "runtime, Count = %"PRIuSIZE, i); } break; } a = i + 1; atomic_add_explicit(&aux->count, 1, &old_count, memory_order_relaxed); aux->b++; atomic_store_explicit(&aux->data64, (i & 1) ? (uint64_t)aux->b : a, memory_order_release); } return NULL; } static void test_acq_rel(void) { pthread_t reader, writer; struct atomic_aux *aux = xmalloc(sizeof *aux); a = 0; aux->b = 0; aux->count = 0; atomic_init(&aux->data, NULL); aux->data64 = 0; reader = ovs_thread_create("reader", atomic_reader, aux); writer = ovs_thread_create("writer", atomic_writer, aux); xpthread_join(reader, NULL); xpthread_join(writer, NULL); free(aux); } static void test_atomic_plain(void) { TEST_ATOMIC_TYPE(atomic_char, char); TEST_ATOMIC_TYPE(atomic_uchar, unsigned char); TEST_ATOMIC_TYPE(atomic_schar, signed char); TEST_ATOMIC_TYPE(atomic_short, short); TEST_ATOMIC_TYPE(atomic_ushort, unsigned short); TEST_ATOMIC_TYPE(atomic_int, int); TEST_ATOMIC_TYPE(atomic_uint, unsigned int); TEST_ATOMIC_TYPE(atomic_long, long int); TEST_ATOMIC_TYPE(atomic_ulong, unsigned long int); TEST_ATOMIC_TYPE(atomic_llong, long long int); TEST_ATOMIC_TYPE(atomic_ullong, unsigned long long int); TEST_ATOMIC_TYPE(atomic_size_t, size_t); TEST_ATOMIC_TYPE(atomic_ptrdiff_t, ptrdiff_t); TEST_ATOMIC_TYPE(atomic_intmax_t, intmax_t); TEST_ATOMIC_TYPE(atomic_uintmax_t, uintmax_t); TEST_ATOMIC_TYPE(atomic_intptr_t, intptr_t); TEST_ATOMIC_TYPE(atomic_uintptr_t, uintptr_t); TEST_ATOMIC_TYPE(atomic_uint8_t, uint8_t); TEST_ATOMIC_TYPE(atomic_int8_t, int8_t); TEST_ATOMIC_TYPE(atomic_uint16_t, uint16_t); TEST_ATOMIC_TYPE(atomic_int16_t, int16_t); TEST_ATOMIC_TYPE(atomic_uint32_t, uint32_t); TEST_ATOMIC_TYPE(atomic_int32_t, int32_t); TEST_ATOMIC_TYPE(atomic_uint64_t, uint64_t); TEST_ATOMIC_TYPE(atomic_int64_t, int64_t); } static void test_atomic_relaxed(void) { TEST_ATOMIC_ORDER(memory_order_relaxed, memory_order_relaxed, memory_order_relaxed); } static void test_atomic_consume(void) { TEST_ATOMIC_ORDER(memory_order_consume, memory_order_release, memory_order_release); } static void test_atomic_acquire(void) { TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release, memory_order_release); } static void test_atomic_acq_rel(void) { TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release, memory_order_acq_rel); } static void test_atomic_seq_cst(void) { TEST_ATOMIC_ORDER(memory_order_seq_cst, memory_order_seq_cst, memory_order_seq_cst); } static void test_atomic_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { fatal_signal_init(); test_atomic_plain(); test_atomic_relaxed(); test_atomic_consume(); test_atomic_acquire(); test_atomic_acq_rel(); test_atomic_seq_cst(); test_atomic_flag(); test_acq_rel(); test_cons_rel(); } OVSTEST_REGISTER("test-atomic", test_atomic_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-barrier.c000066400000000000000000000140711514270232600226740ustar00rootroot00000000000000/* * Copyright (c) 2021 NVIDIA Corporation. * * 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. */ #include #undef NDEBUG #include #include "ovstest.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "random.h" #include "util.h" #define DEFAULT_N_THREADS 4 #define NB_STEPS 4 static bool verbose; static struct ovs_barrier barrier; struct blocker_aux { unsigned int tid; bool leader; int step; }; static void * basic_blocker_main(void *aux_) { struct blocker_aux *aux = aux_; size_t i; aux->step = 0; for (i = 0; i < NB_STEPS; i++) { ovs_barrier_block(&barrier); aux->step++; ovs_barrier_block(&barrier); } return NULL; } static void basic_block_check(struct blocker_aux *aux, size_t n, int expected) { size_t i; for (i = 0; i < n; i++) { if (verbose) { printf("aux[%" PRIuSIZE "]=%d == %d", i, aux[i].step, expected); if (aux[i].step != expected) { printf(" <--- X"); } printf("\n"); } else { ovs_assert(aux[i].step == expected); } } ovs_barrier_block(&barrier); ovs_barrier_block(&barrier); } /* * Basic barrier test. * * N writers and 1 reader participate in the test. * Each thread goes through M steps (=NB_STEPS). * The main thread participates as the reader. * * A Step is divided in three parts: * 1. before * (barrier) * 2. during * (barrier) * 3. after * * Each writer updates a thread-local variable with the * current step number within part 2 and waits. * * The reader checks all variables during part 3, expecting * all variables to be equal. If any variable differs, it means * its thread was not properly blocked by the barrier. */ static void test_barrier_basic(size_t n_threads) { struct blocker_aux *aux; pthread_t *threads; size_t i; ovs_barrier_init(&barrier, n_threads + 1); aux = xcalloc(n_threads, sizeof *aux); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("ovs-barrier", basic_blocker_main, &aux[i]); } for (i = 0; i < NB_STEPS; i++) { basic_block_check(aux, n_threads, i); } ovs_barrier_destroy(&barrier); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } free(threads); free(aux); } static unsigned int *shared_mem; static void * lead_blocker_main(void *aux_) { struct blocker_aux *aux = aux_; size_t i; aux->step = 0; for (i = 0; i < NB_STEPS; i++) { if (aux->leader) { shared_mem = xmalloc(sizeof *shared_mem); if (verbose) { printf("*T1: allocated shmem\n"); } } xnanosleep(random_range(100) * 1000); ovs_barrier_block(&barrier); if (verbose) { printf("%cT%u: ENTER, writing\n", (aux->leader ? '*' : ' '), aux->tid); } shared_mem[0] = 42; ovs_barrier_block(&barrier); if (verbose) { printf("%cT%u: EXIT\n", (aux->leader ? '*' : ' '), aux->tid); } if (aux->leader) { free(shared_mem); if (verbose) { printf("*T1: freed shmem\n"); } } xnanosleep(random_range(100) * 1000); } return NULL; } /* * Leader barrier test. * * N threads participates, one of which is marked as * the leader (thread 0). The main thread does not * participate. * * The test is divided in M steps (=NB_STEPS). * A Step is divided in three parts: * 1. before * (barrier) * 2. during * (barrier) * 3. after * * Part 1, the leader allocates a block of shared memory. * Part 2, all threads write to the shared memory. * Part 3: the leader frees the shared memory. * * If any thread is improperly blocked by the barrier, * the shared memory accesses will trigger a segfault * or a use-after-free if ASAN is enabled. */ static void test_barrier_lead(size_t n_threads) { struct blocker_aux *aux; pthread_t *threads; size_t i; ovs_barrier_init(&barrier, n_threads); aux = xcalloc(n_threads, sizeof *aux); threads = xmalloc(n_threads * sizeof *threads); aux[0].leader = true; for (i = 0; i < n_threads; i++) { aux[i].tid = i + 1; threads[i] = ovs_thread_create("ovs-barrier", lead_blocker_main, &aux[i]); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } /* If the main thread does not participate in the barrier, * it must wait for all threads to join before destroying it. */ ovs_barrier_destroy(&barrier); free(threads); free(aux); } static void usage(char *test_name) { fprintf(stderr, "Usage: %s [n_threads=%d] [-v]\n", test_name, DEFAULT_N_THREADS); } static void test_barrier(int argc, char *argv[]) { size_t n_threads = DEFAULT_N_THREADS; char **args = argv + optind - 1; set_program_name(argv[0]); argc -= optind; if (argc > 2) { usage(args[0]); return; } while (argc-- > 0) { args++; if (!strcmp(args[0], "-v")) { verbose = true; } else { n_threads = strtol(args[0], NULL, 10); if (n_threads > 20) { n_threads = 20; } } } test_barrier_basic(n_threads); test_barrier_lead(n_threads); } OVSTEST_REGISTER("test-barrier", test_barrier); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-bitmap.c000066400000000000000000000115221514270232600225200ustar00rootroot00000000000000/* * Copyright (c) 2014 Kmindg * * 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. */ #include #undef NDEBUG #include "bitmap.h" #include #include "command-line.h" #include "ovstest.h" #include "timeval.h" enum { MAX_BITS = 20 * BITMAP_ULONG_BITS }; static int elapsed(const struct timeval *start) { struct timeval end; xgettimeofday(&end); return timeval_to_msec(&end) - timeval_to_msec(start); } /* Tests bitmap_equal. */ static void test_bitmap_equal(void) { unsigned long *a, *b; a = bitmap_allocate(MAX_BITS); b = bitmap_allocate(MAX_BITS); /* equal test */ assert(bitmap_equal(a, b, MAX_BITS)); assert(bitmap_equal(a, b, MAX_BITS - 1)); assert(bitmap_equal(a, b, MAX_BITS - (BITMAP_ULONG_BITS - 1))); bitmap_set_multiple(a, 10 * BITMAP_ULONG_BITS, BITMAP_ULONG_BITS, true); assert(bitmap_equal(a, b, 10 * BITMAP_ULONG_BITS)); /* non-equal test */ assert(!bitmap_equal(a, b, 11 * BITMAP_ULONG_BITS)); assert(!bitmap_equal(a, b, 11 * BITMAP_ULONG_BITS - 1)); assert(!bitmap_equal(a, b, 11 * BITMAP_ULONG_BITS - (BITMAP_ULONG_BITS - 1))); free(b); free(a); } /* Tests bitmap_scan. */ static void test_bitmap_scan(void) { unsigned long *a; a = bitmap_allocate(MAX_BITS); /* scan for 1 */ assert(bitmap_scan(a, true, 1, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, true, BITMAP_ULONG_BITS - 1, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, true, 0, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, true, 0, BITMAP_ULONG_BITS + 1) == BITMAP_ULONG_BITS + 1); assert(bitmap_scan(a, true, 0, 2 * BITMAP_ULONG_BITS - 1) == 2 * BITMAP_ULONG_BITS - 1); bitmap_set1(a, MAX_BITS - 1); assert(bitmap_scan(a, true, 0, MAX_BITS) == MAX_BITS - 1); bitmap_set1(a, MAX_BITS - BITMAP_ULONG_BITS + 1); assert(bitmap_scan(a, true, 3, MAX_BITS) == MAX_BITS - BITMAP_ULONG_BITS + 1); bitmap_set1(a, BITMAP_ULONG_BITS - 1); assert(bitmap_scan(a, true, 7, MAX_BITS - 1) == BITMAP_ULONG_BITS - 1); bitmap_set1(a, 0); assert(bitmap_scan(a, true, 0, MAX_BITS - 7) == 0); bitmap_set_multiple(a, 0, MAX_BITS, true); /* scan for 0 */ assert(bitmap_scan(a, false, 1, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, false, BITMAP_ULONG_BITS - 1, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, false, 0, BITMAP_ULONG_BITS) == BITMAP_ULONG_BITS); assert(bitmap_scan(a, false, 0, BITMAP_ULONG_BITS + 1) == BITMAP_ULONG_BITS + 1); assert(bitmap_scan(a, false, 0, 2 * BITMAP_ULONG_BITS - 1) == 2 * BITMAP_ULONG_BITS - 1); bitmap_set0(a, MAX_BITS - 1); assert(bitmap_scan(a, false, 0, MAX_BITS) == MAX_BITS - 1); bitmap_set0(a, MAX_BITS - BITMAP_ULONG_BITS + 1); assert(bitmap_scan(a, false, 3, MAX_BITS) == MAX_BITS - BITMAP_ULONG_BITS + 1); bitmap_set0(a, BITMAP_ULONG_BITS - 1); assert(bitmap_scan(a, false, 7, MAX_BITS - 1) == BITMAP_ULONG_BITS - 1); bitmap_set0(a, 0); assert(bitmap_scan(a, false, 0, MAX_BITS - 7) == 0); free(a); } static void run_test(void (*function)(void)) { function(); printf("."); } static void run_tests(struct ovs_cmdl_context *ctx OVS_UNUSED) { run_test(test_bitmap_equal); run_test(test_bitmap_scan); printf("\n"); } static void run_benchmarks(struct ovs_cmdl_context *ctx) { int n_iter = strtol(ctx->argv[1], NULL, 10); struct timeval start; xgettimeofday(&start); for (int i = 0; i < n_iter; i++) { test_bitmap_equal(); } printf("bitmap equal: %5d ms\n", elapsed(&start)); xgettimeofday(&start); for (int i = 0; i < n_iter; i++) { test_bitmap_scan(); } printf("bitmap scan: %5d ms\n", elapsed(&start)); printf("\n"); } static const struct ovs_cmdl_command commands[] = { {"check", NULL, 0, 0, run_tests, OVS_RO}, {"benchmark", NULL, 1, 1, run_benchmarks, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_bitmap_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-bitmap", test_bitmap_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-bundle.c000066400000000000000000000167071514270232600225270ustar00rootroot00000000000000/* Copyright (c) 2011, 2012, 2013, 2014, 2017 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include #include #include "bundle.h" #include "flow.h" #include "openvswitch/ofp-actions.h" #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "util.h" #define N_FLOWS 50000 #define MAX_MEMBERS 8 /* Maximum supported by this test framework. */ struct member { ofp_port_t member_id; bool enabled; size_t flow_count; }; struct member_group { size_t n_members; struct member members[MAX_MEMBERS]; }; static struct member * member_lookup(struct member_group *sg, ofp_port_t member_id) { size_t i; for (i = 0; i < sg->n_members; i++) { if (sg->members[i].member_id == member_id) { return &sg->members[i]; } } return NULL; } static bool member_enabled_cb(ofp_port_t member_id, void *aux) { struct member *member; member = member_lookup(aux, member_id); return member ? member->enabled : false; } static struct ofpact_bundle * parse_bundle_actions(char *actions) { struct ofpact_bundle *bundle; struct ofpbuf ofpacts; struct ofpact *action; char *error; ofpbuf_init(&ofpacts, 0); error = bundle_parse_load(actions, NULL, &ofpacts); if (error) { ovs_fatal(0, "%s", error); } action = ofpacts.data; bundle = ofpact_get_BUNDLE(xmemdup(action, action->len)); ofpbuf_uninit(&ofpacts); if (bundle->n_members > MAX_MEMBERS) { ovs_fatal(0, "At most %u members are supported", MAX_MEMBERS); } return bundle; } static const char * mask_str(uint8_t mask, size_t n_bits) { static char str[9]; size_t i; n_bits = MIN(n_bits, 8); for (i = 0; i < n_bits; i++) { str[i] = (1 << i) & mask ? '1' : '0'; } str[i] = '\0'; return str; } static void test_bundle_main(int argc, char *argv[]) { bool ok = true; struct ofpact_bundle *bundle; struct flow *flows; size_t i, n_permute, old_n_enabled; struct member_group sg; int old_active; set_program_name(argv[0]); if (argc != 2) { ovs_fatal(0, "usage: %s bundle_action", program_name); } bundle = parse_bundle_actions(argv[1]); /* Generate 'members' array. */ sg.n_members = 0; for (i = 0; i < bundle->n_members; i++) { ofp_port_t member_id = bundle->members[i]; if (member_lookup(&sg, member_id)) { ovs_fatal(0, "Redundant members are not supported. "); } sg.members[sg.n_members].member_id = member_id; sg.n_members++; } /* Generate flows. */ flows = xmalloc(N_FLOWS * sizeof *flows); for (i = 0; i < N_FLOWS; i++) { flow_random_hash_fields(&flows[i]); flows[i].regs[0] = ofp_to_u16(OFPP_NONE); } /* Cycles through each possible liveness permutation for the given * n_members. The initial state is equivalent to all members down, so we * skip it by starting at i = 1. We do one extra iteration to cover * transitioning from the final state back to the initial state. */ old_n_enabled = 0; old_active = -1; n_permute = 1 << sg.n_members; for (i = 1; i <= n_permute + 1; i++) { struct member *member; size_t j, n_enabled, changed; double disruption, perfect; uint8_t mask; int active; mask = i % n_permute; /* Gray coding ensures that in each iteration exactly one member * changes its liveness. This makes the expected disruption a bit * easier to calculate, and is likely similar to how failures will be * experienced in the wild. */ mask = mask ^ (mask >> 1); /* Initialize members. */ n_enabled = 0; for (j = 0; j < sg.n_members; j++) { member = &sg.members[j]; member->flow_count = 0; member->enabled = ((1 << j) & mask) != 0; if (member->enabled) { n_enabled++; } } active = -1; for (j = 0; j < sg.n_members; j++) { if (sg.members[j].enabled) { active = j; break; } } changed = 0; for (j = 0; j < N_FLOWS; j++) { struct flow *flow = &flows[j]; ofp_port_t old_member_id, ofp_port; struct flow_wildcards wc; old_member_id = u16_to_ofp(flow->regs[0]); ofp_port = bundle_execute(bundle, flow, &wc, member_enabled_cb, &sg); flow->regs[0] = ofp_to_u16(ofp_port); if (ofp_port != OFPP_NONE) { member_lookup(&sg, ofp_port)->flow_count++; } if (old_member_id != ofp_port) { changed++; } } if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { perfect = active == old_active ? 0.0 : 1.0; } else { if (old_n_enabled || n_enabled) { perfect = 1.0 / MAX(old_n_enabled, n_enabled); } else { /* This will happen when 'sg.n_members' is 0. */ perfect = 0; } } disruption = changed / (double)N_FLOWS; printf("%s: disruption=%.2f (perfect=%.2f)", mask_str(mask, sg.n_members), disruption, perfect); for (j = 0 ; j < sg.n_members; j++) { member = &sg.members[j]; double flow_percent; flow_percent = member->flow_count / (double)N_FLOWS; printf( " %.2f", flow_percent); if (member->enabled) { double perfect_fp; if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { perfect_fp = j == active ? 1.0 : 0.0; } else { perfect_fp = 1.0 / n_enabled; } if (fabs(flow_percent - perfect_fp) >= .01) { fprintf(stderr, "%s: member %d: flow_percentage=%.5f for" " differs from perfect=%.5f by more than .01\n", mask_str(mask, sg.n_members), member->member_id, flow_percent, perfect_fp); ok = false; } } else if (member->flow_count) { fprintf(stderr, "%s: member %d: disabled member received" " flows.\n", mask_str(mask, sg.n_members), member->member_id); ok = false; } } printf("\n"); if (fabs(disruption - perfect) >= .01) { fprintf(stderr, "%s: disruption=%.5f differs from perfect=%.5f by" " more than .01\n", mask_str(mask, sg.n_members), disruption, perfect); ok = false; } old_active = active; old_n_enabled = n_enabled; } free(bundle); free(flows); exit(ok ? 0 : 1); } OVSTEST_REGISTER("test-bundle", test_bundle_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-byte-order.c000066400000000000000000000034151514270232600233220ustar00rootroot00000000000000/* * Copyright (c) 2010, 2011 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "byte-order.h" #include #include #include "ovstest.h" static void test_byte_order_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { #ifndef __CHECKER__ /* I picked some random numbers. */ const uint16_t s = 0xc9bd; const uint32_t l = 0xffe56ae8; const uint64_t ll = UINT64_C(0xb6fe878a9117ecdb); assert(htons(ntohs(s)) == s); assert(ntohs(htons(s)) == s); assert(CONSTANT_HTONS(ntohs(s)) == s); assert(ntohs(CONSTANT_HTONS(s)) == s); assert(ntohs(CONSTANT_HTONS(l)) == (uint16_t) l); assert(ntohs(CONSTANT_HTONS(ll)) == (uint16_t) ll); assert(htonl(ntohl(l)) == l); assert(ntohl(htonl(l)) == l); assert(CONSTANT_HTONL(ntohl(l)) == l); assert(ntohl(CONSTANT_HTONL(l)) == l); assert(ntohl(CONSTANT_HTONL(ll)) == (uint32_t) ll); assert(htonll(ntohll(ll)) == ll); assert(ntohll(htonll(ll)) == ll); assert(CONSTANT_HTONLL(ntohll(ll)) == ll); assert(ntohll(CONSTANT_HTONLL(ll))); #else /* __CHECKER__ */ /* Making sparse happy with this code makes it unreadable, so don't bother. */ #endif } OVSTEST_REGISTER("test-byte-order", test_byte_order_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-byteq.c000066400000000000000000000113211514270232600223650ustar00rootroot00000000000000/* * Copyright (C) 2023 Hewlett Packard Enterprise Development LP * * 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. */ #include #undef NDEBUG #include #include #include #include #include #include "byteq.h" #include "ovstest.h" #include "util.h" static void test_byteq_main(int argc, char *argv[]); static void test_byteq_put_get(void); static void test_byteq_putn_get(void); static void test_byteq_put_string(void); static void test_byteq_fast_forward(void); static void test_byteq_write_read(void); #define SIZE 256 static void test_byteq_put_get(void) { struct byteq bq; uint8_t buffer[SIZE]; const char *input = "Open vSwitch"; const int input_len = strlen(input); byteq_init(&bq, buffer, SIZE); for (int i = 0; i < input_len; i++) { byteq_put(&bq, input[i]); } for (int i = 0; i < input_len; i++) { ovs_assert(byteq_get(&bq) == input[i]); } } static void test_byteq_putn_get(void) { struct byteq bq; uint8_t buffer[SIZE]; const char *input = "Open vSwitch"; const int input_len = strlen(input); byteq_init(&bq, buffer, SIZE); byteq_putn(&bq, input, input_len); for (int i = 0; i < input_len; i++) { ovs_assert(byteq_get(&bq) == input[i]); } } static void test_byteq_put_string(void) { struct byteq bq; uint8_t buffer[SIZE]; const char *input = "Open vSwitch"; const int input_len = strlen(input); byteq_init(&bq, buffer, SIZE); byteq_put_string(&bq, input); for (int i = 0; i < input_len; i++) { ovs_assert(byteq_get(&bq) == input[i]); } } static void test_byteq_fast_forward(void) { struct byteq bq; uint8_t buffer[SIZE]; unsigned int head; unsigned int tail; const char *input = "Open vSwitch"; const int input_len = strlen(input); byteq_init(&bq, buffer, SIZE); byteq_putn(&bq, input, input_len); for (int i = 0; i < input_len; i++) { ovs_assert(byteq_get(&bq) == input[i]); } ovs_assert(byteq_is_empty(&bq)); ovs_assert(byteq_headroom(&bq) < SIZE); head = bq.head; tail = bq.tail; byteq_fast_forward(&bq); ovs_assert(byteq_headroom(&bq) == SIZE); ovs_assert(bq.head > head); ovs_assert(bq.tail > tail); ovs_assert(bq.head == bq.tail); } static void test_byteq_write_read(void) { #ifndef _WIN32 int fd[2]; pid_t childpid; int rc; struct byteq bq; uint8_t buffer[SIZE]; const char *input = "Open vSwitch"; const int input_len = strlen(input); byteq_init(&bq, buffer, SIZE); byteq_put_string(&bq, input); rc = pipe(fd); ovs_assert(rc == 0); /* Flush stdout */ fflush(stdout); childpid = fork(); ovs_assert(childpid != -1); if (childpid == 0) { /* Child process closes stdout */ close(STDOUT_FILENO); /* Child process closes up input side of pipe */ close(fd[0]); rc = byteq_write(&bq, fd[1]); ovs_assert(rc == 0); exit(0); } else { /* Parent process closes up output side of pipe */ close(fd[1]); rc = byteq_read(&bq, fd[0]); ovs_assert(rc == EOF); for (int i = 0; i < input_len; i++) { ovs_assert(byteq_get(&bq) == input[i]); } } #endif /* _WIN32 */ } static void run_test(void (*function)(void)) { function(); printf("."); } static void test_byteq_main(int argc, char *argv[]) { if (argc != 2) { ovs_fatal(0, "exactly one argument required\n" "the argument must be one of the following:\n" "\tbasic\n" "\twrite_read\n"); } if (strcmp(argv[1], "write_read") == 0) { run_test(test_byteq_write_read); printf("\n"); } else if (strcmp(argv[1], "basic") == 0) { run_test(test_byteq_put_get); run_test(test_byteq_putn_get); run_test(test_byteq_put_string); run_test(test_byteq_fast_forward); printf("\n"); } else { ovs_fatal(0, "invalid argument\n" "the argument must be one of the following:\n" "\tbasic\n" "\twrite_read\n"); } } OVSTEST_REGISTER("test-byteq", test_byteq_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-ccmap.c000066400000000000000000000157501514270232600223360ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2013, 2014, 2016 Nicira, Inc. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * ccmap.h. */ #include #undef NDEBUG #include "ccmap.h" #include #include #include #include "bitmap.h" #include "command-line.h" #include "fat-rwlock.h" #include "hash.h" #include "openvswitch/hmap.h" #include "ovstest.h" #include "ovs-thread.h" #include "random.h" #include "timeval.h" #include "util.h" typedef size_t hash_func(int value); static int compare_uint32s(const void *a_, const void *b_) { const uint32_t *a = a_; const uint32_t *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'ccmap' contains exactly the 'n' values in 'values'. */ static void check_ccmap(struct ccmap *ccmap, const int values[], size_t n, hash_func *hash) { uint32_t *hashes = xmalloc(sizeof *hashes * n); int i; for (i = 0; i < n; i++) { hashes[i] = hash(values[i]); } qsort(hashes, n, sizeof *hashes, compare_uint32s); /* Check that all the values are there in lookup. */ for (i = 0; i < n; i++) { uint32_t h = hashes[i]; size_t count = ccmap_find(ccmap, h); assert(count); /* Must have at least one. */ assert(i + count <= n); /* May not have too many. */ /* Skip colliding hash values and assert they were in the count. */ while (--count) { i++; assert(hashes[i] == h); } /* Make sure next hash is different. */ if (i + 1 < n) { assert(hashes[i + 1] != h); } } /* Check counters. */ assert(ccmap_is_empty(ccmap) == !n); assert(ccmap_count(ccmap) == n); free(hashes); } static void shuffle(int *p, size_t n) { for (; n > 1; n--, p++) { int *q = &p[random_range(n)]; int tmp = *p; *p = *q; *q = tmp; } } static size_t identity_hash(int value) { return value; } static size_t good_hash(int value) { return hash_int(value, 0x1234abcd); } static size_t constant_hash(int value OVS_UNUSED) { return 123; } /* Tests basic ccmap increment and decrement. */ static void test_ccmap_inc_dec(hash_func *hash) { enum { N_ELEMS = 1000 }; int values[N_ELEMS]; struct ccmap ccmap; size_t i; ccmap_init(&ccmap); for (i = 0; i < N_ELEMS; i++) { ccmap_inc(&ccmap, hash(i)); values[i] = i; check_ccmap(&ccmap, values, i + 1, hash); } shuffle(values, N_ELEMS); for (i = 0; i < (N_ELEMS - 1); i++) { ccmap_dec(&ccmap, hash(values[i])); check_ccmap(&ccmap, values + (i + 1), N_ELEMS - (i + 1), hash); } ccmap_destroy(&ccmap); } static void run_test(void (*function)(hash_func *)) { hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash }; for (size_t i = 0; i < ARRAY_SIZE(hash_funcs); i++) { function(hash_funcs[i]); printf("."); fflush(stdout); } } static void run_tests(struct ovs_cmdl_context *ctx) { int n = ctx->argc >= 2 ? atoi(ctx->argv[1]) : 100; for (int i = 0; i < n; i++) { run_test(test_ccmap_inc_dec); } printf("\n"); } static int n_elems; /* Number of elements to insert. */ static int n_threads; /* Number of threads to search and mutate. */ static uint32_t mutation_frac; /* % mutations, as fraction of UINT32_MAX. */ static void benchmark_ccmap(void); static int elapsed(const struct timeval *start) { struct timeval end; xgettimeofday(&end); return timeval_to_msec(&end) - timeval_to_msec(start); } static void run_benchmarks(struct ovs_cmdl_context *ctx) { n_elems = strtol(ctx->argv[1], NULL, 10); n_threads = strtol(ctx->argv[2], NULL, 10); mutation_frac = strtod(ctx->argv[3], NULL) / 100.0 * UINT32_MAX; printf("Benchmarking with n=%d, %d threads, %.2f%% mutations\n", n_elems, n_threads, (double) mutation_frac / UINT32_MAX * 100.); benchmark_ccmap(); } /* ccmap benchmark. */ struct ccmap_aux { struct ovs_mutex mutex; struct ccmap *ccmap; }; static void * search_ccmap(void *aux_) { struct ccmap_aux *aux = aux_; size_t i; if (mutation_frac) { for (i = 0; i < n_elems; i++) { uint32_t hash = hash_int(i, 0); if (random_uint32() < mutation_frac) { ovs_mutex_lock(&aux->mutex); uint32_t count = ccmap_find(aux->ccmap, hash); if (count) { ccmap_dec(aux->ccmap, hash); } ovs_mutex_unlock(&aux->mutex); } else { ovs_ignore(ccmap_find(aux->ccmap, hash)); } } } else { for (i = 0; i < n_elems; i++) { ovs_ignore(ccmap_find(aux->ccmap, hash_int(i, 0))); } } return NULL; } static void benchmark_ccmap(void) { struct ccmap ccmap; struct timeval start; pthread_t *threads; struct ccmap_aux aux; size_t i; /* Insertions. */ xgettimeofday(&start); ccmap_init(&ccmap); for (i = 0; i < n_elems; i++) { ccmap_inc(&ccmap, hash_int(i, 0)); } printf("ccmap insert: %5d ms\n", elapsed(&start)); /* Search and mutation. */ xgettimeofday(&start); aux.ccmap = &ccmap; ovs_mutex_init(&aux.mutex); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("search", search_ccmap, &aux); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } free(threads); printf("ccmap search: %5d ms\n", elapsed(&start)); /* Destruction. */ xgettimeofday(&start); for (i = 0; i < n_elems; i++) { uint32_t hash = hash_int(i, 0); if (ccmap_find(&ccmap, hash)) { /* Also remove any colliding hashes. */ while (ccmap_dec(&ccmap, hash)) { ; } } } ccmap_destroy(&ccmap); printf("ccmap destroy: %5d ms\n", elapsed(&start)); } static const struct ovs_cmdl_command commands[] = { {"check", NULL, 0, 1, run_tests, OVS_RO}, {"benchmark", NULL, 3, 3, run_benchmarks, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_ccmap_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - optind, .argv = argv + optind, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-ccmap", test_ccmap_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-classifier.c000066400000000000000000001744341514270232600234040ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * 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. */ /* Tests for classifier, written with knowledge of and to advantage of the * classifier's internal structure. * * With very few exceptions, these tests obtain complete coverage of every * basic block and every branch in the classifier implementation, e.g. a clean * report from "gcov -b". (Covering the exceptions would require finding * collisions in the hash function used for flow data, etc.) * * This test should receive a clean report from "valgrind --leak-check=full": * it frees every heap block that it allocates. */ #include #undef NDEBUG #include "classifier.h" #include #include #include #include "byte-order.h" #include "classifier-private.h" #include "command-line.h" #include "fatal-signal.h" #include "flow.h" #include "openvswitch/vlog.h" #include "ovstest.h" #include "ovs-atomic.h" #include "ovs-thread.h" #include "packets.h" #include "random.h" #include "timeval.h" #include "unaligned.h" #include "util.h" static bool versioned = false; /* Fields in a rule. */ #define CLS_FIELDS \ /* struct flow all-caps */ \ /* member name name */ \ /* ----------- -------- */ \ CLS_FIELD(tunnel.tun_id, TUN_ID) \ CLS_FIELD(metadata, METADATA) \ CLS_FIELD(nw_src, NW_SRC) \ CLS_FIELD(nw_dst, NW_DST) \ CLS_FIELD(in_port.ofp_port, IN_PORT) \ CLS_FIELD(vlans[0].tci, VLAN_TCI) \ CLS_FIELD(dl_type, DL_TYPE) \ CLS_FIELD(tp_src, TP_SRC) \ CLS_FIELD(tp_dst, TP_DST) \ CLS_FIELD(dl_src, DL_SRC) \ CLS_FIELD(dl_dst, DL_DST) \ CLS_FIELD(nw_proto, NW_PROTO) \ CLS_FIELD(nw_tos, NW_DSCP) /* Field indexes. * * (These are also indexed into struct classifier's 'tables' array.) */ enum { #define CLS_FIELD(MEMBER, NAME) CLS_F_IDX_##NAME, CLS_FIELDS #undef CLS_FIELD CLS_N_FIELDS }; /* Field information. */ struct cls_field { int ofs; /* Offset in struct flow. */ int len; /* Length in bytes. */ const char *name; /* Name (for debugging). */ }; static const struct cls_field cls_fields[CLS_N_FIELDS] = { #define CLS_FIELD(MEMBER, NAME) \ { offsetof(struct flow, MEMBER), \ sizeof ((struct flow *)0)->MEMBER, \ #NAME }, CLS_FIELDS #undef CLS_FIELD }; struct test_rule { struct ovs_list list_node; int aux; /* Auxiliary data. */ struct cls_rule cls_rule; /* Classifier rule data. */ }; static struct test_rule * test_rule_from_cls_rule(const struct cls_rule *rule) { return rule ? CONTAINER_OF(rule, struct test_rule, cls_rule) : NULL; } static void test_rule_destroy(struct test_rule *rule) { if (rule) { cls_rule_destroy(&rule->cls_rule); free(rule); } } static struct test_rule *make_rule(int wc_fields, int priority, int value_pat); static void free_rule(struct test_rule *); static struct test_rule *clone_rule(const struct test_rule *); /* Trivial (linear) classifier. */ struct tcls { size_t n_rules; size_t allocated_rules; struct test_rule **rules; }; static void tcls_init(struct tcls *tcls) { tcls->n_rules = 0; tcls->allocated_rules = 0; tcls->rules = NULL; } static void tcls_destroy(struct tcls *tcls) { if (tcls) { size_t i; for (i = 0; i < tcls->n_rules; i++) { test_rule_destroy(tcls->rules[i]); } free(tcls->rules); } } static bool tcls_is_empty(const struct tcls *tcls) { return tcls->n_rules == 0; } static struct test_rule * tcls_insert(struct tcls *tcls, const struct test_rule *rule) { size_t i; for (i = 0; i < tcls->n_rules; i++) { const struct cls_rule *pos = &tcls->rules[i]->cls_rule; if (cls_rule_equal(pos, &rule->cls_rule)) { /* Exact match. */ ovsrcu_postpone(free_rule, tcls->rules[i]); tcls->rules[i] = clone_rule(rule); return tcls->rules[i]; } else if (pos->priority < rule->cls_rule.priority) { break; } } if (tcls->n_rules >= tcls->allocated_rules) { tcls->rules = x2nrealloc(tcls->rules, &tcls->allocated_rules, sizeof *tcls->rules); } if (i != tcls->n_rules) { memmove(&tcls->rules[i + 1], &tcls->rules[i], sizeof *tcls->rules * (tcls->n_rules - i)); } tcls->rules[i] = clone_rule(rule); tcls->n_rules++; return tcls->rules[i]; } static void tcls_remove(struct tcls *cls, const struct test_rule *rule) { size_t i; for (i = 0; i < cls->n_rules; i++) { struct test_rule *pos = cls->rules[i]; if (pos == rule) { test_rule_destroy(pos); memmove(&cls->rules[i], &cls->rules[i + 1], sizeof *cls->rules * (cls->n_rules - i - 1)); cls->n_rules--; return; } } OVS_NOT_REACHED(); } static bool match(const struct cls_rule *wild_, const struct flow *fixed) { struct match wild; int f_idx; minimatch_expand(&wild_->match, &wild); for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) { bool eq; if (f_idx == CLS_F_IDX_NW_SRC) { eq = !((fixed->nw_src ^ wild.flow.nw_src) & wild.wc.masks.nw_src); } else if (f_idx == CLS_F_IDX_NW_DST) { eq = !((fixed->nw_dst ^ wild.flow.nw_dst) & wild.wc.masks.nw_dst); } else if (f_idx == CLS_F_IDX_TP_SRC) { eq = !((fixed->tp_src ^ wild.flow.tp_src) & wild.wc.masks.tp_src); } else if (f_idx == CLS_F_IDX_TP_DST) { eq = !((fixed->tp_dst ^ wild.flow.tp_dst) & wild.wc.masks.tp_dst); } else if (f_idx == CLS_F_IDX_DL_SRC) { eq = eth_addr_equal_except(fixed->dl_src, wild.flow.dl_src, wild.wc.masks.dl_src); } else if (f_idx == CLS_F_IDX_DL_DST) { eq = eth_addr_equal_except(fixed->dl_dst, wild.flow.dl_dst, wild.wc.masks.dl_dst); } else if (f_idx == CLS_F_IDX_VLAN_TCI) { eq = !((fixed->vlans[0].tci ^ wild.flow.vlans[0].tci) & wild.wc.masks.vlans[0].tci); } else if (f_idx == CLS_F_IDX_TUN_ID) { eq = !((fixed->tunnel.tun_id ^ wild.flow.tunnel.tun_id) & wild.wc.masks.tunnel.tun_id); } else if (f_idx == CLS_F_IDX_METADATA) { eq = !((fixed->metadata ^ wild.flow.metadata) & wild.wc.masks.metadata); } else if (f_idx == CLS_F_IDX_NW_DSCP) { eq = !((fixed->nw_tos ^ wild.flow.nw_tos) & (wild.wc.masks.nw_tos & IP_DSCP_MASK)); } else if (f_idx == CLS_F_IDX_NW_PROTO) { eq = !((fixed->nw_proto ^ wild.flow.nw_proto) & wild.wc.masks.nw_proto); } else if (f_idx == CLS_F_IDX_DL_TYPE) { eq = !((fixed->dl_type ^ wild.flow.dl_type) & wild.wc.masks.dl_type); } else if (f_idx == CLS_F_IDX_IN_PORT) { eq = !((fixed->in_port.ofp_port ^ wild.flow.in_port.ofp_port) & wild.wc.masks.in_port.ofp_port); } else { OVS_NOT_REACHED(); } if (!eq) { return false; } } return true; } static struct cls_rule * tcls_lookup(const struct tcls *cls, const struct flow *flow) { size_t i; for (i = 0; i < cls->n_rules; i++) { struct test_rule *pos = cls->rules[i]; if (match(&pos->cls_rule, flow)) { return &pos->cls_rule; } } return NULL; } static void tcls_delete_matches(struct tcls *cls, const struct cls_rule *target) { size_t i; for (i = 0; i < cls->n_rules; ) { struct test_rule *pos = cls->rules[i]; if (!minimask_has_extra(pos->cls_rule.match.mask, target->match.mask)) { struct flow flow; miniflow_expand(pos->cls_rule.match.flow, &flow); if (match(target, &flow)) { tcls_remove(cls, pos); continue; } } i++; } } static ovs_be32 nw_src_values[] = { CONSTANT_HTONL(0xc0a80001), CONSTANT_HTONL(0xc0a04455) }; static ovs_be32 nw_dst_values[] = { CONSTANT_HTONL(0xc0a80002), CONSTANT_HTONL(0xc0a04455) }; static ovs_be64 tun_id_values[] = { 0, CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) }; static ovs_be64 metadata_values[] = { 0, CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) }; static ofp_port_t in_port_values[] = { OFP_PORT_C(1), OFPP_LOCAL }; static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) }; static ovs_be16 dl_type_values[] = { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) }; static ovs_be16 tp_src_values[] = { CONSTANT_HTONS(49362), CONSTANT_HTONS(80) }; static ovs_be16 tp_dst_values[] = { CONSTANT_HTONS(6667), CONSTANT_HTONS(22) }; static struct eth_addr dl_src_values[] = { ETH_ADDR_C(00,02,e3,0f,80,a4), ETH_ADDR_C(5e,33,7f,5f,1e,99) }; static struct eth_addr dl_dst_values[] = { ETH_ADDR_C(4a,27,71,ae,64,c1), ETH_ADDR_C(ff,ff,ff,ff,ff,ff) }; static uint8_t nw_proto_values[] = { IPPROTO_TCP, IPPROTO_ICMP }; static uint8_t nw_dscp_values[] = { 48, 0 }; static void *values[CLS_N_FIELDS][2]; static void init_values(void) { values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0]; values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1]; values[CLS_F_IDX_METADATA][0] = &metadata_values[0]; values[CLS_F_IDX_METADATA][1] = &metadata_values[1]; values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0]; values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1]; values[CLS_F_IDX_VLAN_TCI][0] = &vlan_tci_values[0]; values[CLS_F_IDX_VLAN_TCI][1] = &vlan_tci_values[1]; values[CLS_F_IDX_DL_SRC][0] = &dl_src_values[0]; values[CLS_F_IDX_DL_SRC][1] = &dl_src_values[1]; values[CLS_F_IDX_DL_DST][0] = &dl_dst_values[0]; values[CLS_F_IDX_DL_DST][1] = &dl_dst_values[1]; values[CLS_F_IDX_DL_TYPE][0] = &dl_type_values[0]; values[CLS_F_IDX_DL_TYPE][1] = &dl_type_values[1]; values[CLS_F_IDX_NW_SRC][0] = &nw_src_values[0]; values[CLS_F_IDX_NW_SRC][1] = &nw_src_values[1]; values[CLS_F_IDX_NW_DST][0] = &nw_dst_values[0]; values[CLS_F_IDX_NW_DST][1] = &nw_dst_values[1]; values[CLS_F_IDX_NW_PROTO][0] = &nw_proto_values[0]; values[CLS_F_IDX_NW_PROTO][1] = &nw_proto_values[1]; values[CLS_F_IDX_NW_DSCP][0] = &nw_dscp_values[0]; values[CLS_F_IDX_NW_DSCP][1] = &nw_dscp_values[1]; values[CLS_F_IDX_TP_SRC][0] = &tp_src_values[0]; values[CLS_F_IDX_TP_SRC][1] = &tp_src_values[1]; values[CLS_F_IDX_TP_DST][0] = &tp_dst_values[0]; values[CLS_F_IDX_TP_DST][1] = &tp_dst_values[1]; } #define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values) #define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values) #define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values) #define N_METADATA_VALUES ARRAY_SIZE(metadata_values) #define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values) #define N_VLAN_TCI_VALUES ARRAY_SIZE(vlan_tci_values) #define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values) #define N_TP_SRC_VALUES ARRAY_SIZE(tp_src_values) #define N_TP_DST_VALUES ARRAY_SIZE(tp_dst_values) #define N_DL_SRC_VALUES ARRAY_SIZE(dl_src_values) #define N_DL_DST_VALUES ARRAY_SIZE(dl_dst_values) #define N_NW_PROTO_VALUES ARRAY_SIZE(nw_proto_values) #define N_NW_DSCP_VALUES ARRAY_SIZE(nw_dscp_values) #define N_FLOW_VALUES (N_NW_SRC_VALUES * \ N_NW_DST_VALUES * \ N_TUN_ID_VALUES * \ N_IN_PORT_VALUES * \ N_VLAN_TCI_VALUES * \ N_DL_TYPE_VALUES * \ N_TP_SRC_VALUES * \ N_TP_DST_VALUES * \ N_DL_SRC_VALUES * \ N_DL_DST_VALUES * \ N_NW_PROTO_VALUES * \ N_NW_DSCP_VALUES) static unsigned int get_value(unsigned int *x, unsigned n_values) { unsigned int rem = *x % n_values; *x /= n_values; return rem; } static void compare_classifiers(struct classifier *cls, size_t n_invisible_rules, ovs_version_t version, struct tcls *tcls) { static const int confidence = 500; unsigned int i; assert(classifier_count(cls) == tcls->n_rules + n_invisible_rules); for (i = 0; i < confidence; i++) { const struct cls_rule *cr0, *cr1, *cr2; struct flow flow; struct flow_wildcards wc; uint32_t n_tries; unsigned int x; flow_wildcards_init_catchall(&wc); x = random_range(N_FLOW_VALUES); memset(&flow, 0, sizeof flow); flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)]; flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)]; flow.tunnel.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)]; flow.metadata = metadata_values[get_value(&x, N_METADATA_VALUES)]; flow.in_port.ofp_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)]; flow.vlans[0].tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)]; flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)]; flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)]; flow.tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)]; flow.dl_src = dl_src_values[get_value(&x, N_DL_SRC_VALUES)]; flow.dl_dst = dl_dst_values[get_value(&x, N_DL_DST_VALUES)]; flow.nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)]; flow.nw_tos = nw_dscp_values[get_value(&x, N_NW_DSCP_VALUES)]; /* This assertion is here to suppress a GCC 4.9 array-bounds warning */ atomic_read_relaxed(&cls->n_tries, &n_tries); ovs_assert(n_tries <= CLS_MAX_TRIES); cr0 = classifier_lookup(cls, version, &flow, &wc, NULL); cr1 = tcls_lookup(tcls, &flow); assert((cr0 == NULL) == (cr1 == NULL)); if (cr0 != NULL) { const struct test_rule *tr0 = test_rule_from_cls_rule(cr0); const struct test_rule *tr1 = test_rule_from_cls_rule(cr1); assert(cls_rule_equal(cr0, cr1)); assert(tr0->aux == tr1->aux); /* Make sure the rule should have been visible. */ assert(cls_rule_visible_in_version(cr0, version)); } cr2 = classifier_lookup(cls, version, &flow, NULL, NULL); assert(cr2 == cr0); } } static void destroy_classifier(struct classifier *cls) { struct test_rule *rule; classifier_defer(cls); CLS_FOR_EACH (rule, cls_rule, cls) { classifier_remove_assert(cls, &rule->cls_rule); ovsrcu_postpone(free_rule, rule); } classifier_destroy(cls); } static void pvector_verify(const struct pvector *pvec) { void *ptr OVS_UNUSED; int prev_priority = INT_MAX; PVECTOR_FOR_EACH (ptr, pvec) { int priority = cursor__.vector[cursor__.entry_idx].priority; if (priority > prev_priority) { ovs_abort(0, "Priority vector is out of order (%u > %u)", priority, prev_priority); } prev_priority = priority; } } static unsigned int trie_verify(const rcu_trie_ptr *trie, unsigned int ofs, unsigned int n_bits) { const struct trie_node *node = ovsrcu_get(struct trie_node *, trie); if (node) { assert(node->n_rules == 0 || node->n_bits > 0); ofs += node->n_bits; assert((ofs > 0 || (ofs == 0 && node->n_bits == 0)) && ofs <= n_bits); return node->n_rules + trie_verify(&node->edges[0], ofs, n_bits) + trie_verify(&node->edges[1], ofs, n_bits); } return 0; } static void verify_tries(struct classifier *cls) OVS_NO_THREAD_SAFETY_ANALYSIS { unsigned int n_rules; uint32_t i, n_tries; atomic_read_explicit(&cls->n_tries, &n_tries, memory_order_acquire); for (i = 0; i < n_tries; i++) { n_rules = trie_verify(&cls->tries[i].root, 0, cls->tries[i].field->n_bits); assert(n_rules <= cls->n_rules); } } static void check_tables(const struct classifier *cls, int n_tables, int n_rules, int n_dups, int n_invisible, ovs_version_t version) OVS_NO_THREAD_SAFETY_ANALYSIS { const struct cls_subtable *table; struct test_rule *test_rule; int found_tables = 0; int found_tables_with_visible_rules = 0; int found_rules = 0; int found_dups = 0; int found_invisible = 0; int found_visible_but_removable = 0; int found_rules2 = 0; pvector_verify(&cls->subtables); CMAP_FOR_EACH (table, cmap_node, &cls->subtables_map) { const struct cls_match *head; int max_priority = INT_MIN; unsigned int max_count = 0; bool found = false; bool found_visible_rules = false; const struct cls_subtable *iter; /* Locate the subtable from 'subtables'. */ PVECTOR_FOR_EACH (iter, &cls->subtables) { if (iter == table) { if (found) { ovs_abort(0, "Subtable %p duplicated in 'subtables'.", table); } found = true; } } if (!found) { ovs_abort(0, "Subtable %p not found from 'subtables'.", table); } assert(!cmap_is_empty(&table->rules)); assert(trie_verify(&table->ports_trie, 0, table->ports_mask_len) == (table->ports_mask_len ? cmap_count(&table->rules) : 0)); found_tables++; CMAP_FOR_EACH (head, cmap_node, &table->rules) { int prev_priority = INT_MAX; ovs_version_t prev_version = 0; const struct cls_match *rule, *prev; bool found_visible_rules_in_list = false; assert(head->priority <= table->max_priority); if (head->priority > max_priority) { max_priority = head->priority; max_count = 0; } FOR_EACH_RULE_IN_LIST_PROTECTED(rule, prev, head) { ovs_version_t rule_version; const struct cls_rule *found_rule; /* Priority may not increase. */ assert(rule->priority <= prev_priority); if (rule->priority == max_priority) { ++max_count; } /* Count invisible rules and visible duplicates. */ if (!cls_match_visible_in_version(rule, version)) { found_invisible++; } else { if (cls_match_is_eventually_invisible(rule)) { found_visible_but_removable++; } if (found_visible_rules_in_list) { found_dups++; } found_visible_rules_in_list = true; found_visible_rules = true; } /* Rule must be visible in the version it was inserted. */ rule_version = rule->versions.add_version; assert(cls_match_visible_in_version(rule, rule_version)); /* We should always find the latest version of the rule, * unless all rules have been marked for removal. * Later versions must always be later in the list. */ found_rule = classifier_find_rule_exactly(cls, rule->cls_rule, rule_version); if (found_rule && found_rule != rule->cls_rule) { struct cls_match *cls_match; cls_match = get_cls_match_protected(found_rule); assert(found_rule->priority == rule->priority); /* Found rule may not have a lower version. */ assert(cls_match->versions.add_version >= rule_version); /* This rule must not be visible in the found rule's * version. */ assert(!cls_match_visible_in_version( rule, cls_match->versions.add_version)); } if (rule->priority == prev_priority) { /* Exact duplicate rule may not have a lower version. */ assert(rule_version >= prev_version); /* Previous rule must not be visible in rule's version. */ assert(!cls_match_visible_in_version(prev, rule_version)); } prev_priority = rule->priority; prev_version = rule_version; found_rules++; } } if (found_visible_rules) { found_tables_with_visible_rules++; } assert(table->max_priority == max_priority); assert(table->max_count == max_count); } assert(found_tables == cmap_count(&cls->subtables_map)); assert(found_tables == pvector_count(&cls->subtables)); assert(n_tables == -1 || n_tables == found_tables_with_visible_rules); assert(n_rules == -1 || found_rules == n_rules + found_invisible); assert(n_dups == -1 || found_dups == n_dups); assert(found_invisible == n_invisible); CLS_FOR_EACH (test_rule, cls_rule, cls) { found_rules2++; } /* Iteration does not see removable rules. */ assert(found_rules == found_rules2 + found_visible_but_removable + found_invisible); } static struct test_rule * make_rule(int wc_fields, int priority, int value_pat) { const struct cls_field *f; struct test_rule *rule; struct match match; match_init_catchall(&match); for (f = &cls_fields[0]; f < &cls_fields[CLS_N_FIELDS]; f++) { int f_idx = f - cls_fields; int value_idx = (value_pat & (1u << f_idx)) != 0; memcpy((char *) &match.flow + f->ofs, values[f_idx][value_idx], f->len); if (f_idx == CLS_F_IDX_NW_SRC) { match.wc.masks.nw_src = OVS_BE32_MAX; } else if (f_idx == CLS_F_IDX_NW_DST) { match.wc.masks.nw_dst = OVS_BE32_MAX; } else if (f_idx == CLS_F_IDX_TP_SRC) { match.wc.masks.tp_src = OVS_BE16_MAX; } else if (f_idx == CLS_F_IDX_TP_DST) { match.wc.masks.tp_dst = OVS_BE16_MAX; } else if (f_idx == CLS_F_IDX_DL_SRC) { WC_MASK_FIELD(&match.wc, dl_src); } else if (f_idx == CLS_F_IDX_DL_DST) { WC_MASK_FIELD(&match.wc, dl_dst); } else if (f_idx == CLS_F_IDX_VLAN_TCI) { match.wc.masks.vlans[0].tci = OVS_BE16_MAX; } else if (f_idx == CLS_F_IDX_TUN_ID) { match.wc.masks.tunnel.tun_id = OVS_BE64_MAX; } else if (f_idx == CLS_F_IDX_METADATA) { match.wc.masks.metadata = OVS_BE64_MAX; } else if (f_idx == CLS_F_IDX_NW_DSCP) { match.wc.masks.nw_tos |= IP_DSCP_MASK; } else if (f_idx == CLS_F_IDX_NW_PROTO) { match.wc.masks.nw_proto = UINT8_MAX; } else if (f_idx == CLS_F_IDX_DL_TYPE) { match.wc.masks.dl_type = OVS_BE16_MAX; } else if (f_idx == CLS_F_IDX_IN_PORT) { match.wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX); } else { OVS_NOT_REACHED(); } } rule = xzalloc(sizeof *rule); cls_rule_init(&rule->cls_rule, &match, wc_fields ? (priority == INT_MIN ? priority + 1 : priority == INT_MAX ? priority - 1 : priority) : 0); return rule; } static struct test_rule * clone_rule(const struct test_rule *src) { struct test_rule *dst; dst = xmalloc(sizeof *dst); dst->aux = src->aux; cls_rule_clone(&dst->cls_rule, &src->cls_rule); return dst; } static void free_rule(struct test_rule *rule) { cls_rule_destroy(&rule->cls_rule); free(rule); } static void shuffle(int *p, size_t n) { for (; n > 1; n--, p++) { int *q = &p[random_range(n)]; int tmp = *p; *p = *q; *q = tmp; } } static void shuffle_u32s(uint32_t *p, size_t n) { for (; n > 1; n--, p++) { uint32_t *q = &p[random_range(n)]; uint32_t tmp = *p; *p = *q; *q = tmp; } } static void shuffle_fields(enum mf_field_id *p, size_t n) { for (; n > 1; n--, p++) { enum mf_field_id *q = &p[random_range(n)]; enum mf_field_id tmp = *p; *p = *q; *q = tmp; } } /* Classifier tests. */ static enum mf_field_id trie_fields[4] = { MFF_IPV4_DST, MFF_IPV4_SRC, MFF_IPV6_DST, MFF_IPV6_SRC, }; static void set_prefix_fields(struct classifier *cls) { verify_tries(cls); classifier_set_prefix_fields(cls, trie_fields, ARRAY_SIZE(trie_fields)); verify_tries(cls); } /* Tests an empty classifier. */ static void test_empty(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct classifier cls; struct tcls tcls; classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); assert(classifier_is_empty(&cls)); assert(tcls_is_empty(&tcls)); compare_classifiers(&cls, 0, OVS_VERSION_MIN, &tcls); classifier_destroy(&cls); tcls_destroy(&tcls); } /* Destroys a null classifier. */ static void test_destroy_null(struct ovs_cmdl_context *ctx OVS_UNUSED) { classifier_destroy(NULL); } /* Tests classification with one rule at a time. */ static void test_single_rule(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int wc_fields; /* Hilarious. */ for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) { struct classifier cls; struct test_rule *rule, *tcls_rule; struct tcls tcls; rule = make_rule(wc_fields, hash_bytes(&wc_fields, sizeof wc_fields, 0), 0); classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); tcls_rule = tcls_insert(&tcls, rule); classifier_insert(&cls, &rule->cls_rule, OVS_VERSION_MIN, NULL, 0); compare_classifiers(&cls, 0, OVS_VERSION_MIN, &tcls); check_tables(&cls, 1, 1, 0, 0, OVS_VERSION_MIN); classifier_remove_assert(&cls, &rule->cls_rule); tcls_remove(&tcls, tcls_rule); assert(classifier_is_empty(&cls)); assert(tcls_is_empty(&tcls)); compare_classifiers(&cls, 0, OVS_VERSION_MIN, &tcls); ovsrcu_postpone(free_rule, rule); classifier_destroy(&cls); tcls_destroy(&tcls); } } /* Tests replacing one rule by another. */ static void test_rule_replacement(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int wc_fields; for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) { struct classifier cls; struct test_rule *rule1; struct test_rule *rule2; struct tcls tcls; rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX); rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX); rule2->aux += 5; rule2->aux += 5; classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); tcls_insert(&tcls, rule1); classifier_insert(&cls, &rule1->cls_rule, OVS_VERSION_MIN, NULL, 0); compare_classifiers(&cls, 0, OVS_VERSION_MIN, &tcls); check_tables(&cls, 1, 1, 0, 0, OVS_VERSION_MIN); tcls_destroy(&tcls); tcls_init(&tcls); tcls_insert(&tcls, rule2); assert(test_rule_from_cls_rule( classifier_replace(&cls, &rule2->cls_rule, OVS_VERSION_MIN, NULL, 0)) == rule1); ovsrcu_postpone(free_rule, rule1); compare_classifiers(&cls, 0, OVS_VERSION_MIN, &tcls); check_tables(&cls, 1, 1, 0, 0, OVS_VERSION_MIN); classifier_defer(&cls); classifier_remove_assert(&cls, &rule2->cls_rule); tcls_destroy(&tcls); destroy_classifier(&cls); } } static int factorial(int n_items) { int n, i; n = 1; for (i = 2; i <= n_items; i++) { n *= i; } return n; } static void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } static void reverse(int *a, int n) { int i; for (i = 0; i < n / 2; i++) { int j = n - (i + 1); swap(&a[i], &a[j]); } } static bool next_permutation(int *a, int n) { int k; for (k = n - 2; k >= 0; k--) { if (a[k] < a[k + 1]) { int l; for (l = n - 1; ; l--) { if (a[l] > a[k]) { swap(&a[k], &a[l]); reverse(a + (k + 1), n - (k + 1)); return true; } } } } return false; } /* Tests classification with rules that have the same matching criteria. */ static void test_many_rules_in_one_list (struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_RULES = 3 }; int n_pris; for (n_pris = N_RULES; n_pris >= 1; n_pris--) { int ops[N_RULES * 2]; int pris[N_RULES]; int n_permutations; int i; pris[0] = 0; for (i = 1; i < N_RULES; i++) { pris[i] = pris[i - 1] + (n_pris > i); } for (i = 0; i < N_RULES * 2; i++) { ops[i] = i / 2; } n_permutations = 0; do { struct test_rule *rules[N_RULES]; struct test_rule *tcls_rules[N_RULES]; int pri_rules[N_RULES]; struct classifier cls; struct tcls tcls; ovs_version_t version = OVS_VERSION_MIN; size_t n_invisible_rules = 0; n_permutations++; for (i = 0; i < N_RULES; i++) { rules[i] = make_rule(456, pris[i], 0); tcls_rules[i] = NULL; pri_rules[i] = -1; } classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); for (i = 0; i < ARRAY_SIZE(ops); i++) { struct test_rule *displaced_rule = NULL; struct cls_rule *removable_rule = NULL; int j = ops[i]; int m, n; if (!tcls_rules[j]) { tcls_rules[j] = tcls_insert(&tcls, rules[j]); if (versioned) { /* Insert the new rule in the next version. */ ++version; displaced_rule = test_rule_from_cls_rule( classifier_find_rule_exactly(&cls, &rules[j]->cls_rule, version)); if (displaced_rule) { /* Mark the old rule for removal after the current * version. */ cls_rule_make_invisible_in_version( &displaced_rule->cls_rule, version); n_invisible_rules++; removable_rule = &displaced_rule->cls_rule; } classifier_insert(&cls, &rules[j]->cls_rule, version, NULL, 0); } else { displaced_rule = test_rule_from_cls_rule( classifier_replace(&cls, &rules[j]->cls_rule, version, NULL, 0)); } if (pri_rules[pris[j]] >= 0) { int k = pri_rules[pris[j]]; assert(displaced_rule != NULL); assert(displaced_rule != rules[j]); assert(pris[j] == displaced_rule->cls_rule.priority); tcls_rules[k] = NULL; } else { assert(displaced_rule == NULL); } pri_rules[pris[j]] = j; } else { if (versioned) { /* Mark the rule for removal after the current * version. */ ++version; cls_rule_make_invisible_in_version( &rules[j]->cls_rule, version); n_invisible_rules++; removable_rule = &rules[j]->cls_rule; } else { classifier_remove_assert(&cls, &rules[j]->cls_rule); } tcls_remove(&tcls, tcls_rules[j]); tcls_rules[j] = NULL; pri_rules[pris[j]] = -1; } compare_classifiers(&cls, n_invisible_rules, version, &tcls); n = 0; for (m = 0; m < N_RULES; m++) { n += tcls_rules[m] != NULL; } check_tables(&cls, n > 0, n, n - 1, n_invisible_rules, version); if (versioned && removable_rule) { struct cls_match *cls_match = get_cls_match_protected(removable_rule); /* Removable rule is no longer visible. */ assert(cls_match); assert(!cls_match_visible_in_version(cls_match, version)); classifier_remove_assert(&cls, removable_rule); n_invisible_rules--; } } classifier_defer(&cls); for (i = 0; i < N_RULES; i++) { if (classifier_remove(&cls, &rules[i]->cls_rule)) { ovsrcu_postpone(free_rule, rules[i]); } } classifier_destroy(&cls); tcls_destroy(&tcls); } while (next_permutation(ops, ARRAY_SIZE(ops))); assert(n_permutations == (factorial(N_RULES * 2) >> N_RULES)); } } static int count_ones(unsigned long int x) { int n = 0; while (x) { x = zero_rightmost_1bit(x); n++; } return n; } static bool array_contains(int *array, int n, int value) { int i; for (i = 0; i < n; i++) { if (array[i] == value) { return true; } } return false; } /* Tests classification with two rules at a time that fall into the same * table but different lists. */ static void test_many_rules_in_one_table(struct ovs_cmdl_context *ctx OVS_UNUSED) { int iteration; for (iteration = 0; iteration < 50; iteration++) { enum { N_RULES = 20 }; struct test_rule *rules[N_RULES]; struct test_rule *tcls_rules[N_RULES]; struct classifier cls; struct tcls tcls; ovs_version_t version = OVS_VERSION_MIN; size_t n_invisible_rules = 0; int value_pats[N_RULES]; int value_mask; int wcf; int i; do { wcf = random_uint32() & ((1u << CLS_N_FIELDS) - 1); value_mask = ~wcf & ((1u << CLS_N_FIELDS) - 1); } while ((1 << count_ones(value_mask)) < N_RULES); classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); for (i = 0; i < N_RULES; i++) { int priority = random_range(INT_MAX); do { value_pats[i] = random_uint32() & value_mask; } while (array_contains(value_pats, i, value_pats[i])); ++version; rules[i] = make_rule(wcf, priority, value_pats[i]); tcls_rules[i] = tcls_insert(&tcls, rules[i]); classifier_insert(&cls, &rules[i]->cls_rule, version, NULL, 0); compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, 1, i + 1, 0, n_invisible_rules, version); } for (i = 0; i < N_RULES; i++) { tcls_remove(&tcls, tcls_rules[i]); if (versioned) { /* Mark the rule for removal after the current version. */ ++version; cls_rule_make_invisible_in_version(&rules[i]->cls_rule, version); n_invisible_rules++; } else { classifier_remove_assert(&cls, &rules[i]->cls_rule); } compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, i < N_RULES - 1, N_RULES - (i + 1), 0, n_invisible_rules, version); if (!versioned) { ovsrcu_postpone(free_rule, rules[i]); } } if (versioned) { for (i = 0; i < N_RULES; i++) { classifier_remove_assert(&cls, &rules[i]->cls_rule); n_invisible_rules--; compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, 0, 0, 0, n_invisible_rules, version); ovsrcu_postpone(free_rule, rules[i]); } } classifier_destroy(&cls); tcls_destroy(&tcls); } } /* Tests classification with many rules at a time that fall into random lists * in 'n' tables. */ static void test_many_rules_in_n_tables(int n_tables) { enum { MAX_RULES = 50 }; int wcfs[10]; int iteration; int i; assert(n_tables < 10); for (i = 0; i < n_tables; i++) { do { wcfs[i] = random_uint32() & ((1u << CLS_N_FIELDS) - 1); } while (array_contains(wcfs, i, wcfs[i])); } for (iteration = 0; iteration < 30; iteration++) { int priorities[MAX_RULES]; struct classifier cls; struct tcls tcls; ovs_version_t version = OVS_VERSION_MIN; size_t n_invisible_rules = 0; struct ovs_list list = OVS_LIST_INITIALIZER(&list); random_set_seed(iteration + 1); for (i = 0; i < MAX_RULES; i++) { priorities[i] = (i * 129) & INT_MAX; } shuffle(priorities, ARRAY_SIZE(priorities)); classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); for (i = 0; i < MAX_RULES; i++) { struct test_rule *rule; int priority = priorities[i]; int wcf = wcfs[random_range(n_tables)]; int value_pat = random_uint32() & ((1u << CLS_N_FIELDS) - 1); rule = make_rule(wcf, priority, value_pat); tcls_insert(&tcls, rule); classifier_insert(&cls, &rule->cls_rule, version, NULL, 0); compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, -1, i + 1, -1, n_invisible_rules, version); } while (classifier_count(&cls) - n_invisible_rules > 0) { struct test_rule *target; struct test_rule *rule; size_t n_removable_rules = 0; target = clone_rule(tcls.rules[random_range(tcls.n_rules)]); CLS_FOR_EACH_TARGET (rule, cls_rule, &cls, &target->cls_rule, version) { if (versioned) { /* Mark the rule for removal after the current version. */ cls_rule_make_invisible_in_version(&rule->cls_rule, version + 1); n_removable_rules++; compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, -1, -1, -1, n_invisible_rules, version); ovs_list_push_back(&list, &rule->list_node); } else if (classifier_remove(&cls, &rule->cls_rule)) { ovsrcu_postpone(free_rule, rule); } } ++version; n_invisible_rules += n_removable_rules; tcls_delete_matches(&tcls, &target->cls_rule); free_rule(target); compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, -1, -1, -1, n_invisible_rules, version); } if (versioned) { struct test_rule *rule; /* Remove rules that are no longer visible. */ LIST_FOR_EACH_POP (rule, list_node, &list) { classifier_remove_assert(&cls, &rule->cls_rule); n_invisible_rules--; compare_classifiers(&cls, n_invisible_rules, version, &tcls); check_tables(&cls, -1, -1, -1, n_invisible_rules, version); } } destroy_classifier(&cls); tcls_destroy(&tcls); } } static void test_many_rules_in_two_tables(struct ovs_cmdl_context *ctx OVS_UNUSED) { test_many_rules_in_n_tables(2); } static void test_many_rules_in_five_tables(struct ovs_cmdl_context *ctx OVS_UNUSED) { test_many_rules_in_n_tables(5); } /* Classifier benchmarks. */ static int n_rules; /* Number of rules to insert. */ static int n_priorities; /* Number of priorities to use. */ static int n_tables; /* Number of subtables. */ static int n_threads; /* Number of threads to search and mutate. */ static int n_lookups; /* Number of lookups each thread performs. */ static void benchmark(bool use_wc, bool stress_prefixes); static int elapsed(const struct timeval *start) { struct timeval end; xgettimeofday(&end); return timeval_to_msec(&end) - timeval_to_msec(start); } static void run_benchmarks(struct ovs_cmdl_context *ctx) { if (ctx->argc < 5 || (ctx->argc > 1 && !strcmp(ctx->argv[1], "--help"))) { printf( "usage: ovstest %s benchmark \n" "\n" "where:\n" "\n" " - The number of rules to install for lookups. More rules\n" " makes misses less likely.\n" " - How many different priorities to use. Using only 1\n" " priority will force lookups to continue through all\n" " subtables.\n" " - Number of subtables to use. Normally a classifier has\n" " rules with different kinds of masks, resulting in\n" " multiple subtables (one per mask). However, in some\n" " special cases a table may consist of only one kind of\n" " rules, so there will be only one subtable.\n" " - How many lookup threads to use. Using one thread should\n" " give less variance accross runs, but classifier\n" " scaling can be tested with multiple threads.\n" " - How many lookups each thread should perform.\n" "\n", program_name); return; } n_rules = strtol(ctx->argv[1], NULL, 10); n_priorities = strtol(ctx->argv[2], NULL, 10); n_tables = strtol(ctx->argv[3], NULL, 10); n_threads = strtol(ctx->argv[4], NULL, 10); n_lookups = strtol(ctx->argv[5], NULL, 10); printf("\nBenchmarking with:\n" "%d rules with %d priorities in %d tables, " "%d threads doing %d lookups each\n", n_rules, n_priorities, n_tables, n_threads, n_lookups); puts("\nWithout wildcards: \n"); benchmark(false, false); puts("\nWith wildcards: \n"); benchmark(true, false); } static void run_prefix_stress(struct ovs_cmdl_context *ctx OVS_UNUSED) { vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_OFF); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); n_rules = 10000; n_priorities = 2; n_tables = 30; n_threads = 2; n_lookups = 2000000; printf("\nStress testing prefixes with:\n" "%d rules with %d priorities in %d tables, " "%d threads doing %d lookups each\n", n_rules, n_priorities, n_tables, n_threads, n_lookups); benchmark(true, true); } struct cls_aux { const struct classifier *cls; size_t n_lookup_flows; struct flow *lookup_flows; bool use_wc; bool quiesce; atomic_int hits; atomic_int misses; }; static void * lookup_classifier(void *aux_) { struct cls_aux *aux = aux_; ovs_version_t version = OVS_VERSION_MIN; int hits = 0, old_hits; int misses = 0, old_misses; size_t i; random_set_seed(1); for (i = 0; i < n_lookups; i++) { const struct cls_rule *cr; struct flow_wildcards wc; unsigned int x; x = random_range(aux->n_lookup_flows); if (aux->use_wc) { flow_wildcards_init_catchall(&wc); cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x], &wc, NULL); } else { cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x], NULL, NULL); } if (cr) { hits++; } else { misses++; } if (aux->quiesce) { ovsrcu_quiesce(); } } atomic_add(&aux->hits, hits, &old_hits); atomic_add(&aux->misses, misses, &old_misses); return NULL; } struct prefix_aux { struct classifier *cls; atomic_bool running; size_t n_updates; }; static void * update_prefixes(void *aux_) { struct prefix_aux *aux = aux_; size_t n, n_updates = 0; bool running = true; random_set_seed(1); while (running) { n_updates++; shuffle_fields(trie_fields, ARRAY_SIZE(trie_fields)); n = random_range(ARRAY_SIZE(trie_fields) + 1); classifier_set_prefix_fields(aux->cls, trie_fields, n); verify_tries(aux->cls); atomic_read_relaxed(&aux->running, &running); } aux->n_updates = n_updates; return NULL; } /* Benchmark classification. */ static void benchmark(bool use_wc, bool stress_prefixes) { struct classifier cls; ovs_version_t version = OVS_VERSION_MIN; struct cls_aux aux; int *wcfs = xmalloc(n_tables * sizeof *wcfs); int *priorities = xmalloc(n_priorities * sizeof *priorities); struct timeval start; pthread_t *threads; int i; fatal_signal_init(); random_set_seed(1); for (i = 0; i < n_tables; i++) { do { wcfs[i] = random_uint32() & ((1u << CLS_N_FIELDS) - 1); } while (array_contains(wcfs, i, wcfs[i])); } for (i = 0; i < n_priorities; i++) { priorities[i] = (i * 129) & INT_MAX; } shuffle(priorities, n_priorities); classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); /* Create lookup flows. */ aux.use_wc = use_wc; aux.quiesce = stress_prefixes; aux.cls = &cls; aux.n_lookup_flows = 2 * N_FLOW_VALUES; aux.lookup_flows = xzalloc(aux.n_lookup_flows * sizeof *aux.lookup_flows); for (i = 0; i < aux.n_lookup_flows; i++) { struct flow *flow = &aux.lookup_flows[i]; unsigned int x; x = random_range(N_FLOW_VALUES); flow->nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)]; flow->nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)]; flow->tunnel.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)]; flow->metadata = metadata_values[get_value(&x, N_METADATA_VALUES)]; flow->in_port.ofp_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)]; flow->vlans[0].tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)]; flow->dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)]; flow->tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)]; flow->tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)]; flow->dl_src = dl_src_values[get_value(&x, N_DL_SRC_VALUES)]; flow->dl_dst = dl_dst_values[get_value(&x, N_DL_DST_VALUES)]; flow->nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)]; flow->nw_tos = nw_dscp_values[get_value(&x, N_NW_DSCP_VALUES)]; } atomic_init(&aux.hits, 0); atomic_init(&aux.misses, 0); /* Rule insertion. */ for (i = 0; i < n_rules; i++) { struct test_rule *rule; const struct cls_rule *old_cr; int priority = priorities[random_range(n_priorities)]; int wcf = wcfs[random_range(n_tables)]; int value_pat = random_uint32() & ((1u << CLS_N_FIELDS) - 1); rule = make_rule(wcf, priority, value_pat); old_cr = classifier_find_rule_exactly(&cls, &rule->cls_rule, version); if (!old_cr) { classifier_insert(&cls, &rule->cls_rule, version, NULL, 0); } else { free_rule(rule); } } pthread_t prefix_thread; struct prefix_aux paux; if (stress_prefixes) { paux.cls = &cls; paux.n_updates = 0; atomic_init(&paux.running, true); prefix_thread = ovs_thread_create("prefixes", update_prefixes, &paux); ovsrcu_quiesce_start(); } /* Lookup. */ xgettimeofday(&start); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("lookups", lookup_classifier, &aux); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } int elapsed_msec = elapsed(&start); free(threads); if (stress_prefixes) { atomic_store_relaxed(&paux.running, false); xpthread_join(prefix_thread, NULL); printf("Prefixes updated %"PRIuSIZE" times.\n", paux.n_updates); ovsrcu_quiesce_end(); } int hits, misses; atomic_read(&aux.hits, &hits); atomic_read(&aux.misses, &misses); printf("hits: %d, misses: %d\n", hits, misses); printf("classifier lookups: %5d ms, %"PRId64" lookups/sec\n", elapsed_msec, (((uint64_t)hits + misses) * 1000) / elapsed_msec); destroy_classifier(&cls); free(aux.lookup_flows); free(priorities); free(wcfs); } /* Miniflow tests. */ static uint32_t random_value(void) { static const uint32_t values_[] = { 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x80000000, 0x00000001, 0xface0000, 0x00d00d1e, 0xdeadbeef }; return values_[random_range(ARRAY_SIZE(values_))]; } static bool choose(unsigned int n, unsigned int *idxp) { if (*idxp < n) { return true; } else { *idxp -= n; return false; } } #define FLOW_U32S (FLOW_U64S * 2) static bool init_consecutive_values(int n_consecutive, struct flow *flow, unsigned int *idxp) { uint32_t *flow_u32 = (uint32_t *) flow; if (choose(FLOW_U32S - n_consecutive + 1, idxp)) { int i; for (i = 0; i < n_consecutive; i++) { flow_u32[*idxp + i] = random_value(); } return true; } else { return false; } } static bool next_random_flow(struct flow *flow, unsigned int idx) { uint32_t *flow_u32 = (uint32_t *) flow; memset(flow, 0, sizeof *flow); /* Empty flow. */ if (choose(1, &idx)) { return true; } /* All flows with a small number of consecutive nonzero values. */ for (int i = 1; i <= 4; i++) { if (init_consecutive_values(i, flow, &idx)) { return true; } } /* All flows with a large number of consecutive nonzero values. */ for (int i = FLOW_U32S - 4; i <= FLOW_U32S; i++) { if (init_consecutive_values(i, flow, &idx)) { return true; } } /* All flows with exactly two nonconsecutive nonzero values. */ if (choose((FLOW_U32S - 1) * (FLOW_U32S - 2) / 2, &idx)) { int ofs1; for (ofs1 = 0; ofs1 < FLOW_U32S - 2; ofs1++) { int ofs2; for (ofs2 = ofs1 + 2; ofs2 < FLOW_U32S; ofs2++) { if (choose(1, &idx)) { flow_u32[ofs1] = random_value(); flow_u32[ofs2] = random_value(); return true; } } } OVS_NOT_REACHED(); } /* 16 randomly chosen flows with N >= 3 nonzero values. */ if (choose(16 * (FLOW_U32S - 4), &idx)) { int n = idx / 16 + 3; for (int i = 0; i < n; i++) { flow_u32[i] = random_value(); } shuffle_u32s(flow_u32, FLOW_U32S); return true; } return false; } static void any_random_flow(struct flow *flow) { static unsigned int max; if (!max) { while (next_random_flow(flow, max)) { max++; } } next_random_flow(flow, random_range(max)); } static void toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask) { const uint32_t *mask_u32 = (const uint32_t *) &mask->masks; uint32_t *flow_u32 = (uint32_t *) flow; int i; for (i = 0; i < FLOW_U32S; i++) { if (mask_u32[i] != 0) { uint32_t bit; do { bit = 1u << random_range(32); } while (!(bit & mask_u32[i])); flow_u32[i] ^= bit; } } } static void wildcard_extra_bits(struct flow_wildcards *mask) { uint32_t *mask_u32 = (uint32_t *) &mask->masks; int i; for (i = 0; i < FLOW_U32S; i++) { if (mask_u32[i] != 0) { uint32_t bit; do { bit = 1u << random_range(32); } while (!(bit & mask_u32[i])); mask_u32[i] &= ~bit; } } } /* Returns a copy of 'src'. The caller must eventually free the returned * miniflow with free(). */ static struct miniflow * miniflow_clone__(const struct miniflow *src) { struct miniflow *dst; size_t data_size; data_size = miniflow_alloc(&dst, 1, src); miniflow_clone(dst, src, data_size / sizeof(uint64_t)); return dst; } /* Returns a hash value for 'flow', given 'basis'. */ static inline uint32_t miniflow_hash__(const struct miniflow *flow, uint32_t basis) { const uint64_t *p = miniflow_get_values(flow); size_t n_values = miniflow_n_values(flow); struct flowmap hash_map = FLOWMAP_EMPTY_INITIALIZER; uint32_t hash = basis; size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, flow->map) { uint64_t value = *p++; if (value) { hash = hash_add64(hash, value); flowmap_set(&hash_map, idx, 1); } } map_t map; FLOWMAP_FOR_EACH_MAP (map, hash_map) { hash = hash_add64(hash, map); } return hash_finish(hash, n_values); } static void test_miniflow(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct flow flow; unsigned int idx; random_set_seed(0xb3faca38); for (idx = 0; next_random_flow(&flow, idx); idx++) { const uint64_t *flow_u64 = (const uint64_t *) &flow; struct miniflow *miniflow, *miniflow2, *miniflow3; struct flow flow2, flow3; struct flow_wildcards mask; struct minimask *minimask; int i; /* Convert flow to miniflow. */ miniflow = miniflow_create(&flow); /* Check that the flow equals its miniflow. */ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) { assert(miniflow_get_vid(miniflow, i) == vlan_tci_to_vid(flow.vlans[i].tci)); } for (i = 0; i < FLOW_U64S; i++) { assert(miniflow_get(miniflow, i) == flow_u64[i]); } /* Check that the miniflow equals itself. */ assert(miniflow_equal(miniflow, miniflow)); /* Convert miniflow back to flow and verify that it's the same. */ miniflow_expand(miniflow, &flow2); assert(flow_equal(&flow, &flow2)); /* Check that copying a miniflow works properly. */ miniflow2 = miniflow_clone__(miniflow); assert(miniflow_equal(miniflow, miniflow2)); assert(miniflow_hash__(miniflow, 0) == miniflow_hash__(miniflow2, 0)); miniflow_expand(miniflow2, &flow3); assert(flow_equal(&flow, &flow3)); /* Check that masked matches work as expected for identical flows and * miniflows. */ do { next_random_flow(&mask.masks, 1); } while (flow_wildcards_is_catchall(&mask)); minimask = minimask_create(&mask); assert(minimask_is_catchall(minimask) == flow_wildcards_is_catchall(&mask)); assert(miniflow_equal_in_minimask(miniflow, miniflow2, minimask)); assert(miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask)); assert(miniflow_hash_in_minimask(miniflow, minimask, 0x12345678) == flow_hash_in_minimask(&flow, minimask, 0x12345678)); assert(minimask_hash(minimask, 0) == miniflow_hash__(&minimask->masks, 0)); /* Check that masked matches work as expected for differing flows and * miniflows. */ toggle_masked_flow_bits(&flow2, &mask); assert(!miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask)); miniflow3 = miniflow_create(&flow2); assert(!miniflow_equal_in_minimask(miniflow, miniflow3, minimask)); /* Clean up. */ free(miniflow); free(miniflow2); free(miniflow3); free(minimask); } } static void test_minimask_has_extra(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct flow_wildcards catchall; struct minimask *minicatchall; struct flow flow; unsigned int idx; flow_wildcards_init_catchall(&catchall); minicatchall = minimask_create(&catchall); assert(minimask_is_catchall(minicatchall)); random_set_seed(0x2ec7905b); for (idx = 0; next_random_flow(&flow, idx); idx++) { struct flow_wildcards mask; struct minimask *minimask; mask.masks = flow; minimask = minimask_create(&mask); assert(!minimask_has_extra(minimask, minimask)); assert(minimask_has_extra(minicatchall, minimask) == !minimask_is_catchall(minimask)); if (!minimask_is_catchall(minimask)) { struct minimask *minimask2; wildcard_extra_bits(&mask); minimask2 = minimask_create(&mask); assert(minimask_has_extra(minimask2, minimask)); assert(!minimask_has_extra(minimask, minimask2)); free(minimask2); } free(minimask); } free(minicatchall); } static void test_minimask_combine(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct flow_wildcards catchall; struct minimask *minicatchall; struct flow flow; unsigned int idx; flow_wildcards_init_catchall(&catchall); minicatchall = minimask_create(&catchall); assert(minimask_is_catchall(minicatchall)); random_set_seed(0x181bf0cd); for (idx = 0; next_random_flow(&flow, idx); idx++) { struct minimask *minimask, *minimask2; struct flow_wildcards mask, mask2, combined, combined2; struct { struct minimask minicombined; uint64_t storage[FLOW_U64S]; } m; struct flow flow2; mask.masks = flow; minimask = minimask_create(&mask); minimask_combine(&m.minicombined, minimask, minicatchall, m.storage); assert(minimask_is_catchall(&m.minicombined)); any_random_flow(&flow2); mask2.masks = flow2; minimask2 = minimask_create(&mask2); minimask_combine(&m.minicombined, minimask, minimask2, m.storage); flow_wildcards_and(&combined, &mask, &mask2); minimask_expand(&m.minicombined, &combined2); assert(flow_wildcards_equal(&combined, &combined2)); free(minimask); free(minimask2); } free(minicatchall); } static void help(struct ovs_cmdl_context *ctx); static const struct ovs_cmdl_command commands[] = { /* Classifier tests. */ {"empty", NULL, 0, 0, test_empty, OVS_RO }, {"destroy-null", NULL, 0, 0, test_destroy_null, OVS_RO }, {"single-rule", NULL, 0, 0, test_single_rule, OVS_RO }, {"rule-replacement", NULL, 0, 0, test_rule_replacement, OVS_RO }, {"many-rules-in-one-list", NULL, 0, 1, test_many_rules_in_one_list, OVS_RO }, {"many-rules-in-one-table", NULL, 0, 1, test_many_rules_in_one_table, OVS_RO }, {"many-rules-in-two-tables", NULL, 0, 0, test_many_rules_in_two_tables, OVS_RO }, {"many-rules-in-five-tables", NULL, 0, 0, test_many_rules_in_five_tables, OVS_RO }, {"benchmark", NULL, 0, 5, run_benchmarks, OVS_RO }, {"stress-prefixes", NULL, 0, 0, run_prefix_stress, OVS_RO }, /* Miniflow and minimask tests. */ {"miniflow", NULL, 0, 0, test_miniflow, OVS_RO }, {"minimask_has_extra", NULL, 0, 0, test_minimask_has_extra, OVS_RO }, {"minimask_combine", NULL, 0, 0, test_minimask_combine, OVS_RO }, {"--help", NULL, 0, 0, help, OVS_RO }, {NULL, NULL, 0, 0, NULL, OVS_RO }, }; static void help(struct ovs_cmdl_context *ctx OVS_UNUSED) { const struct ovs_cmdl_command *p; struct ds test_names = DS_EMPTY_INITIALIZER; const int linesize = 80; printf("usage: ovstest %s TEST [TESTARGS]\n" "where TEST is one of the following:\n\n", program_name); for (p = commands; p->name != NULL; p++) { if (*p->name != '-') { /* Skip internal commands */ if (test_names.length > 1 && test_names.length + strlen(p->name) + 1 >= linesize) { test_names.length -= 1; printf ("%s\n", ds_cstr(&test_names)); ds_clear(&test_names); } ds_put_format(&test_names, "%s, ", p->name); } } if (test_names.length > 2) { test_names.length -= 2; printf("%s\n", ds_cstr(&test_names)); } ds_destroy(&test_names); } static void test_classifier_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); if (argc > 1 && !strcmp(argv[1], "--versioned")) { versioned = true; ctx.argc--; ctx.argv++; } init_values(); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-classifier", test_classifier_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-cmap.c000066400000000000000000000377561514270232600222050ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2013, 2014, 2016, 2017 Nicira, Inc. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * cmap.h. */ #include #undef NDEBUG #include "cmap.h" #include #include #include #include "bitmap.h" #include "command-line.h" #include "fat-rwlock.h" #include "hash.h" #include "openvswitch/hmap.h" #include "ovstest.h" #include "ovs-thread.h" #include "random.h" #include "timeval.h" #include "util.h" /* Sample cmap element. */ struct element { int value; struct cmap_node node; }; typedef size_t hash_func(int value); static int compare_ints(const void *a_, const void *b_) { const int *a = a_; const int *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'cmap' contains exactly the 'n' values in 'values'. */ static void check_cmap(struct cmap *cmap, const int values[], size_t n, hash_func *hash) { int *sort_values, *cmap_values, *cmap_values2; const struct element *e; size_t i, batch_size; struct cmap_position pos = { 0, 0, 0 }; struct cmap_node *node; /* Check that all the values are there in iteration. */ sort_values = xmalloc(sizeof *sort_values * n); cmap_values = xmalloc(sizeof *sort_values * n); cmap_values2 = xmalloc(sizeof *sort_values * n); /* Here we test cursor iteration */ i = 0; CMAP_FOR_EACH (e, node, cmap) { assert(i < n); cmap_values[i++] = e->value; } assert(i == n); assert(e == NULL); /* Here we test iteration with cmap_next_position() */ i = 0; while ((node = cmap_next_position(cmap, &pos))) { e = OBJECT_CONTAINING(node, e, node); assert(i < n); cmap_values2[i++] = e->value; } assert(i == n); memcpy(sort_values, values, sizeof *sort_values * n); qsort(sort_values, n, sizeof *sort_values, compare_ints); qsort(cmap_values, n, sizeof *cmap_values, compare_ints); qsort(cmap_values2, n, sizeof *cmap_values2, compare_ints); for (i = 0; i < n; i++) { assert(sort_values[i] == cmap_values[i]); assert(sort_values[i] == cmap_values2[i]); } free(cmap_values2); free(cmap_values); free(sort_values); /* Check that all the values are there in lookup. */ for (i = 0; i < n; i++) { size_t count = 0; CMAP_FOR_EACH_WITH_HASH (e, node, hash(values[i]), cmap) { count += e->value == values[i]; } assert(count == 1); assert(e == NULL); } /* Check that all the values are there in batched lookup. */ batch_size = n % BITMAP_ULONG_BITS + 1; for (i = 0; i < n; i += batch_size) { unsigned long map; uint32_t hashes[sizeof map * CHAR_BIT]; const struct cmap_node *nodes[sizeof map * CHAR_BIT]; size_t count = 0; int k, j; j = MIN(n - i, batch_size); /* Actual batch size. */ map = ~0UL >> (BITMAP_ULONG_BITS - j); for (k = 0; k < j; k++) { hashes[k] = hash(values[i + k]); } map = cmap_find_batch(cmap, map, hashes, nodes); ULLONG_FOR_EACH_1(k, map) { CMAP_NODE_FOR_EACH (e, node, nodes[k]) { count += e->value == values[i + k]; } assert(e == NULL); } assert(count == j); /* j elements in a batch. */ } /* Check that cmap_first() returns NULL only when cmap_is_empty(). */ assert(!cmap_first(cmap) == cmap_is_empty(cmap)); /* Check counters. */ assert(cmap_is_empty(cmap) == !n); assert(cmap_count(cmap) == n); } static void shuffle(int *p, size_t n) { for (; n > 1; n--, p++) { int *q = &p[random_range(n)]; int tmp = *p; *p = *q; *q = tmp; } } /* Prints the values in 'cmap', plus 'name' as a title. */ static void OVS_UNUSED print_cmap(const char *name, struct cmap *cmap) { struct cmap_cursor cursor; struct element *e; printf("%s:", name); CMAP_CURSOR_FOR_EACH (e, node, &cursor, cmap) { printf(" %d", e->value); } printf("\n"); } /* Prints the 'n' values in 'values', plus 'name' as a title. */ static void OVS_UNUSED print_ints(const char *name, const int *values, size_t n) { size_t i; printf("%s:", name); for (i = 0; i < n; i++) { printf(" %d", values[i]); } printf("\n"); } static size_t identity_hash(int value) { return value; } static size_t good_hash(int value) { return hash_int(value, 0x1234abcd); } static size_t constant_hash(int value OVS_UNUSED) { return 123; } /* Tests basic cmap insertion and deletion. */ static void test_cmap_insert_replace_delete(hash_func *hash) { enum { N_ELEMS = 1000 }; struct element elements[N_ELEMS]; struct element copies[N_ELEMS]; int values[N_ELEMS]; struct cmap cmap = CMAP_INITIALIZER; size_t i; for (i = 0; i < N_ELEMS; i++) { elements[i].value = i; cmap_insert(&cmap, &elements[i].node, hash(i)); values[i] = i; check_cmap(&cmap, values, i + 1, hash); } shuffle(values, N_ELEMS); for (i = 0; i < N_ELEMS; i++) { copies[values[i]].value = values[i]; cmap_replace(&cmap, &elements[values[i]].node, &copies[values[i]].node, hash(values[i])); check_cmap(&cmap, values, N_ELEMS, hash); } shuffle(values, N_ELEMS); for (i = 0; i < N_ELEMS; i++) { cmap_remove(&cmap, &copies[values[i]].node, hash(values[i])); check_cmap(&cmap, values + (i + 1), N_ELEMS - (i + 1), hash); } cmap_destroy(&cmap); } static void run_test(void (*function)(hash_func *)) { hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash }; size_t i; for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) { function(hash_funcs[i]); printf("."); fflush(stdout); } } static void run_tests(struct ovs_cmdl_context *ctx) { int n; int i; n = ctx->argc >= 2 ? atoi(ctx->argv[1]) : 100; for (i = 0; i < n; i++) { run_test(test_cmap_insert_replace_delete); } printf("\n"); } static int n_elems; /* Number of elements to insert. */ static int n_threads; /* Number of threads to search and mutate. */ static uint32_t mutation_frac; /* % mutations, as fraction of UINT32_MAX. */ static int n_batch; /* Number of elements in each batch. */ #define N_BATCH_MAX BITMAP_ULONG_BITS static void benchmark_cmap(void); static void benchmark_cmap_batched(void); static void benchmark_hmap(void); static int elapsed(const struct timeval *start) { struct timeval end; xgettimeofday(&end); return timeval_to_msec(&end) - timeval_to_msec(start); } static void run_benchmarks(struct ovs_cmdl_context *ctx) { n_elems = strtol(ctx->argv[1], NULL, 10); n_threads = strtol(ctx->argv[2], NULL, 10); mutation_frac = strtod(ctx->argv[3], NULL) / 100.0 * UINT32_MAX; n_batch = ctx->argc > 4 ? strtol(ctx->argv[4], NULL, 10) : 1; if (n_batch > N_BATCH_MAX) { n_batch = N_BATCH_MAX; } printf("Benchmarking with n=%d, %d threads, %.2f%% mutations, batch size %d:\n", n_elems, n_threads, (double) mutation_frac / UINT32_MAX * 100., n_batch); if (n_batch > 0) { benchmark_cmap_batched(); } putchar('\n'); benchmark_cmap(); putchar('\n'); benchmark_hmap(); } /* cmap benchmark. */ static struct element * find(const struct cmap *cmap, int value) { struct element *e; CMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), cmap) { if (e->value == value) { return e; } } return NULL; } struct cmap_aux { struct ovs_mutex mutex; struct cmap *cmap; }; static void * search_cmap(void *aux_) { struct cmap_aux *aux = aux_; size_t i; if (mutation_frac) { for (i = 0; i < n_elems; i++) { struct element *e; if (random_uint32() < mutation_frac) { ovs_mutex_lock(&aux->mutex); e = find(aux->cmap, i); if (e) { cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0)); } ovs_mutex_unlock(&aux->mutex); } else { ovs_ignore(find(aux->cmap, i)); } } } else { for (i = 0; i < n_elems; i++) { ovs_ignore(find(aux->cmap, i)); } } return NULL; } static void benchmark_cmap(void) { struct element *elements; struct cmap cmap; struct element *e; struct timeval start; pthread_t *threads; struct cmap_aux aux; size_t i; elements = xmalloc(n_elems * sizeof *elements); /* Insertions. */ xgettimeofday(&start); cmap_init(&cmap); for (i = 0; i < n_elems; i++) { elements[i].value = i; cmap_insert(&cmap, &elements[i].node, hash_int(i, 0)); } printf("cmap insert: %5d ms\n", elapsed(&start)); /* Iteration. */ xgettimeofday(&start); CMAP_FOR_EACH (e, node, &cmap) { ovs_ignore(e); } printf("cmap iterate: %5d ms\n", elapsed(&start)); /* Search and mutation. */ xgettimeofday(&start); aux.cmap = &cmap; ovs_mutex_init(&aux.mutex); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("search", search_cmap, &aux); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } free(threads); printf("cmap search: %5d ms\n", elapsed(&start)); /* Destruction. */ xgettimeofday(&start); CMAP_FOR_EACH (e, node, &cmap) { cmap_remove(&cmap, &e->node, hash_int(e->value, 0)); } cmap_destroy(&cmap); printf("cmap destroy: %5d ms\n", elapsed(&start)); free(elements); } static size_t find_batch(const struct cmap *cmap, const int value) { size_t i, ret; const size_t end = MIN(n_batch, n_elems - value); uint32_t hashes[N_BATCH_MAX]; const struct cmap_node *nodes[N_BATCH_MAX]; if (mutation_frac) { for (i = 0; i < end; i++) { if (random_uint32() < mutation_frac) { break; } hashes[i] = hash_int(value + i, 0); } } else { for (i = 0; i < end; i++) { hashes[i] = hash_int(value + i, 0); } } ret = i; unsigned long map = i ? ~0UL >> (BITMAP_ULONG_BITS - i) : 0; map = cmap_find_batch(cmap, map, hashes, nodes); ULLONG_FOR_EACH_1(i, map) { struct element *e; CMAP_NODE_FOR_EACH (e, node, nodes[i]) { if (OVS_LIKELY(e->value == value + i)) { ovs_ignore(e); /* Found result. */ break; } } } return ret; } static void * search_cmap_batched(void *aux_) { struct cmap_aux *aux = aux_; size_t i = 0, j; for (;;) { struct element *e; j = find_batch(aux->cmap, i); i += j; if (i >= n_elems) { break; } if (j < n_batch) { ovs_mutex_lock(&aux->mutex); e = find(aux->cmap, i); if (e) { cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0)); } ovs_mutex_unlock(&aux->mutex); } } return NULL; } static void benchmark_cmap_batched(void) { struct element *elements; struct cmap cmap; struct element *e; struct timeval start; pthread_t *threads; struct cmap_aux aux; size_t i; elements = xmalloc(n_elems * sizeof *elements); /* Insertions. */ xgettimeofday(&start); cmap_init(&cmap); for (i = 0; i < n_elems; i++) { elements[i].value = i; cmap_insert(&cmap, &elements[i].node, hash_int(i, 0)); } printf("cmap insert: %5d ms\n", elapsed(&start)); /* Iteration. */ xgettimeofday(&start); CMAP_FOR_EACH (e, node, &cmap) { ovs_ignore(e); } printf("cmap iterate: %5d ms\n", elapsed(&start)); /* Search and mutation. */ xgettimeofday(&start); aux.cmap = &cmap; ovs_mutex_init(&aux.mutex); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("search", search_cmap_batched, &aux); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } free(threads); printf("batch search: %5d ms\n", elapsed(&start)); /* Destruction. */ xgettimeofday(&start); CMAP_FOR_EACH (e, node, &cmap) { cmap_remove(&cmap, &e->node, hash_int(e->value, 0)); } cmap_destroy(&cmap); printf("cmap destroy: %5d ms\n", elapsed(&start)); free(elements); } /* hmap benchmark. */ struct helement { int value; struct hmap_node node; }; static struct helement * hfind(const struct hmap *hmap, int value) { struct helement *e; HMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), hmap) { if (e->value == value) { return e; } } return NULL; } struct hmap_aux { struct hmap *hmap; struct fat_rwlock fatlock; }; static void * search_hmap(void *aux_) { struct hmap_aux *aux = aux_; size_t i; if (mutation_frac) { for (i = 0; i < n_elems; i++) { if (random_uint32() < mutation_frac) { struct helement *e; fat_rwlock_wrlock(&aux->fatlock); e = hfind(aux->hmap, i); if (e) { hmap_remove(aux->hmap, &e->node); } fat_rwlock_unlock(&aux->fatlock); } else { fat_rwlock_rdlock(&aux->fatlock); ovs_ignore(hfind(aux->hmap, i)); fat_rwlock_unlock(&aux->fatlock); } } } else { for (i = 0; i < n_elems; i++) { ovs_ignore(hfind(aux->hmap, i)); } } return NULL; } static void benchmark_hmap(void) { struct helement *elements; struct hmap hmap; struct helement *e; struct timeval start; pthread_t *threads; struct hmap_aux aux; size_t i; elements = xmalloc(n_elems * sizeof *elements); xgettimeofday(&start); hmap_init(&hmap); for (i = 0; i < n_elems; i++) { elements[i].value = i; hmap_insert(&hmap, &elements[i].node, hash_int(i, 0)); } printf("hmap insert: %5d ms\n", elapsed(&start)); xgettimeofday(&start); HMAP_FOR_EACH (e, node, &hmap) { ovs_ignore(e); } printf("hmap iterate: %5d ms\n", elapsed(&start)); xgettimeofday(&start); aux.hmap = &hmap; fat_rwlock_init(&aux.fatlock); threads = xmalloc(n_threads * sizeof *threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("search", search_hmap, &aux); } for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } free(threads); printf("hmap search: %5d ms\n", elapsed(&start)); /* Destruction. */ xgettimeofday(&start); HMAP_FOR_EACH_SAFE (e, node, &hmap) { hmap_remove(&hmap, &e->node); } hmap_destroy(&hmap); printf("hmap destroy: %5d ms\n", elapsed(&start)); free(elements); } static const struct ovs_cmdl_command commands[] = { {"check", NULL, 0, 1, run_tests, OVS_RO}, {"benchmark", NULL, 3, 4, run_benchmarks, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_cmap_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - optind, .argv = argv + optind, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-cmap", test_cmap_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-conntrack.c000066400000000000000000000340071514270232600232310ustar00rootroot00000000000000/* * Copyright (c) 2015, 2017 Nicira, Inc. * * 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. */ #include #include "conntrack.h" #include "dp-packet.h" #include "fatal-signal.h" #include "flow.h" #include "netdev.h" #include "ovs-thread.h" #include "ovstest.h" #include "pcap-file.h" #include "timeval.h" #include "stopwatch.h" #define STOPWATCH_CT_EXECUTE_COMMIT "ct-execute-commit" #define STOPWATCH_CT_EXECUTE_NO_COMMIT "ct-execute-no-commit" #define STOPWATCH_FLUSH_FULL_ZONE "full-zone" #define STOPWATCH_FLUSH_EMPTY_ZONE "empty-zone" static const char payload[] = "50540000000a50540000000908004500001c0000000000" "11a4cd0a0101010a0101020001000200080000"; static struct dp_packet * build_packet(uint16_t udp_src, uint16_t udp_dst, ovs_be16 *dl_type) { struct udp_header *udp; struct flow flow; struct dp_packet *pkt = dp_packet_new(sizeof payload / 2); dp_packet_put_hex(pkt, payload, NULL); flow_extract(pkt, &flow); udp = dp_packet_l4(pkt); udp->udp_src = htons(udp_src); udp->udp_dst = htons(udp_dst); *dl_type = flow.dl_type; return pkt; } static struct dp_packet_batch * prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type) { struct dp_packet_batch *pkt_batch = xzalloc(sizeof *pkt_batch); size_t i; ovs_assert(n <= ARRAY_SIZE(pkt_batch->packets)); dp_packet_batch_init(pkt_batch); for (i = 0; i < n; i++) { uint16_t udp_dst = change ? 2+1 : 2; struct dp_packet *pkt = build_packet(1 + tid, udp_dst, dl_type); dp_packet_batch_add(pkt_batch, pkt); } return pkt_batch; } static void destroy_packets(struct dp_packet_batch *pkt_batch) { dp_packet_delete_batch(pkt_batch, true); free(pkt_batch); } struct thread_aux { pthread_t thread; unsigned tid; }; static struct conntrack *ct; static unsigned long n_threads, n_pkts, batch_size; static bool change_conn = false; static struct ovs_barrier barrier; static void * ct_thread_main(void *aux_) { struct thread_aux *aux = aux_; struct dp_packet_batch *pkt_batch; struct dp_packet *pkt; ovs_be16 dl_type; size_t i; long long now = time_msec(); pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type); ovs_barrier_block(&barrier); for (i = 0; i < n_pkts; i += batch_size) { conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL, NULL, NULL, now, 0); DP_PACKET_BATCH_FOR_EACH (j, pkt, pkt_batch) { pkt_metadata_init_conn(&pkt->md); } } ovs_barrier_block(&barrier); destroy_packets(pkt_batch); return NULL; } static void test_benchmark(struct ovs_cmdl_context *ctx) { struct thread_aux *threads; long long start; unsigned i; fatal_signal_init(); /* Parse arguments */ n_threads = strtoul(ctx->argv[1], NULL, 0); if (!n_threads) { ovs_fatal(0, "n_threads must be at least one"); } n_pkts = strtoul(ctx->argv[2], NULL, 0); batch_size = strtoul(ctx->argv[3], NULL, 0); if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) { ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)", NETDEV_MAX_BURST); } if (ctx->argc > 4) { change_conn = strtoul(ctx->argv[4], NULL, 0); } threads = xcalloc(n_threads, sizeof *threads); ovs_barrier_init(&barrier, n_threads + 1); ct = conntrack_init(); /* Create threads */ for (i = 0; i < n_threads; i++) { threads[i].tid = i; threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main, &threads[i]); } /* Starts the work inside the threads */ ovs_barrier_block(&barrier); start = time_msec(); /* Wait for the threads to finish the work */ ovs_barrier_block(&barrier); printf("conntrack: %5lld ms\n", time_msec() - start); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i].thread, NULL); } conntrack_destroy(ct); ovs_barrier_destroy(&barrier); free(threads); } static void test_benchmark_zones(struct ovs_cmdl_context *ctx) { unsigned long n_conns, n_zones, iterations; long long start; unsigned i, j; ovs_be16 dl_type; long long now = time_msec(); fatal_signal_init(); /* Parse arguments */ n_conns = strtoul(ctx->argv[1], NULL, 0); if (n_conns == 0 || n_conns >= UINT32_MAX) { ovs_fatal(0, "n_conns must be between 1 and 2^32"); } n_zones = strtoul(ctx->argv[2], NULL, 0); if (n_zones == 0 || n_zones >= UINT16_MAX) { ovs_fatal(0, "n_zones must be between 1 and 2^16"); } iterations = strtoul(ctx->argv[3], NULL, 0); if (iterations == 0) { ovs_fatal(0, "iterations must be greater than 0"); } ct = conntrack_init(); /* Create initial connection entries */ start = time_msec(); struct dp_packet_batch **pkt_batch = xzalloc(n_conns * sizeof *pkt_batch); for (i = 0; i < n_conns; i++) { pkt_batch[i] = xzalloc(sizeof(struct dp_packet_batch)); dp_packet_batch_init(pkt_batch[i]); uint16_t udp_src = (i & 0xFFFF0000) >> 16; if (udp_src == 0) { udp_src = UINT16_MAX; } uint16_t udp_dst = i & 0xFFFF; if (udp_dst == 0) { udp_dst = UINT16_MAX; } struct dp_packet *pkt = build_packet(udp_src, udp_dst, &dl_type); dp_packet_batch_add(pkt_batch[i], pkt); } printf("initial packet generation time: %lld ms\n", time_msec() - start); /* Put initial entries to each zone */ start = time_msec(); for (i = 0; i < n_zones; i++) { for (j = 0; j < n_conns; j++) { conntrack_execute(ct, pkt_batch[j], dl_type, false, true, i, NULL, NULL, NULL, NULL, now, 0); pkt_metadata_init_conn(&pkt_batch[j]->packets[0]->md); } } printf("initial insert time: %lld ms\n", time_msec() - start); /* Actually run the tests */ stopwatch_create(STOPWATCH_CT_EXECUTE_COMMIT, SW_US); stopwatch_create(STOPWATCH_CT_EXECUTE_NO_COMMIT, SW_US); stopwatch_create(STOPWATCH_FLUSH_FULL_ZONE, SW_US); stopwatch_create(STOPWATCH_FLUSH_EMPTY_ZONE, SW_US); start = time_msec(); for (i = 0; i < iterations; i++) { /* Testing flushing a full zone */ stopwatch_start(STOPWATCH_FLUSH_FULL_ZONE, time_usec()); uint16_t zone = 1; conntrack_flush(ct, &zone); stopwatch_stop(STOPWATCH_FLUSH_FULL_ZONE, time_usec()); /* Now fill the zone again */ stopwatch_start(STOPWATCH_CT_EXECUTE_COMMIT, time_usec()); for (j = 0; j < n_conns; j++) { conntrack_execute(ct, pkt_batch[j], dl_type, false, true, zone, NULL, NULL, NULL, NULL, now, 0); pkt_metadata_init_conn(&pkt_batch[j]->packets[0]->md); } stopwatch_stop(STOPWATCH_CT_EXECUTE_COMMIT, time_usec()); /* Running conntrack_execute on the now existing connections */ stopwatch_start(STOPWATCH_CT_EXECUTE_NO_COMMIT, time_usec()); for (j = 0; j < n_conns; j++) { conntrack_execute(ct, pkt_batch[j], dl_type, false, false, zone, NULL, NULL, NULL, NULL, now, 0); pkt_metadata_init_conn(&pkt_batch[j]->packets[0]->md); } stopwatch_stop(STOPWATCH_CT_EXECUTE_NO_COMMIT, time_usec()); /* Testing flushing an empty zone */ stopwatch_start(STOPWATCH_FLUSH_EMPTY_ZONE, time_usec()); zone = UINT16_MAX; conntrack_flush(ct, &zone); stopwatch_stop(STOPWATCH_FLUSH_EMPTY_ZONE, time_usec()); } printf("flush run time: %lld ms\n", time_msec() - start); stopwatch_sync(); struct stopwatch_stats stats_ct_execute_commit = { .unit = SW_US }; stopwatch_get_stats(STOPWATCH_CT_EXECUTE_COMMIT, &stats_ct_execute_commit); struct stopwatch_stats stats_ct_execute_nocommit = { .unit = SW_US }; stopwatch_get_stats(STOPWATCH_CT_EXECUTE_NO_COMMIT, &stats_ct_execute_nocommit); struct stopwatch_stats stats_flush_full = { .unit = SW_US }; stopwatch_get_stats(STOPWATCH_FLUSH_FULL_ZONE, &stats_flush_full); struct stopwatch_stats stats_flush_empty = { .unit = SW_US }; stopwatch_get_stats(STOPWATCH_FLUSH_EMPTY_ZONE, &stats_flush_empty); printf("results:\n"); printf(" | ct execute (commit) | ct execute (no commit) |" " flush full zone | flush empty zone |\n"); printf("+--------+---------------------+------------------------+" "-----------------+------------------+\n"); printf("| Min | %16llu us | %19llu us | %12llu us | %13llu us |\n", stats_ct_execute_commit.min, stats_ct_execute_nocommit.min, stats_flush_full.min, stats_flush_empty.min); printf("| Max | %16llu us | %19llu us | %12llu us | %13llu us |\n", stats_ct_execute_commit.max, stats_ct_execute_nocommit.max, stats_flush_full.max, stats_flush_empty.max); printf("| 95%%ile | %16.2f us | %19.2f us | %12.2f us | %13.2f us |\n", stats_ct_execute_commit.pctl_95, stats_ct_execute_nocommit.pctl_95, stats_flush_full.pctl_95, stats_flush_empty.pctl_95); printf("| Avg | %16.2f us | %19.2f us | %12.2f us | %13.2f us |\n", stats_ct_execute_commit.ewma_1, stats_ct_execute_nocommit.ewma_1, stats_flush_full.ewma_1, stats_flush_empty.ewma_1); conntrack_destroy(ct); for (i = 0; i < n_conns; i++) { dp_packet_delete_batch(pkt_batch[i], true); free(pkt_batch[i]); } free(pkt_batch); } static void pcap_batch_execute_conntrack(struct conntrack *ct_, struct dp_packet_batch *pkt_batch) { struct dp_packet_batch new_batch; ovs_be16 dl_type = htons(0); long long now = time_msec(); dp_packet_batch_init(&new_batch); /* pkt_batch contains packets with different 'dl_type'. We have to * call conntrack_execute() on packets with the same 'dl_type'. */ struct dp_packet *packet; DP_PACKET_BATCH_FOR_EACH (i, packet, pkt_batch) { struct flow flow; /* This also initializes the l3 and l4 pointers. */ flow_extract(packet, &flow); if (dp_packet_batch_is_empty(&new_batch)) { dl_type = flow.dl_type; } if (flow.dl_type != dl_type) { conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL, NULL, NULL, now, 0); dp_packet_batch_init(&new_batch); } dp_packet_batch_add(&new_batch, packet); } if (!dp_packet_batch_is_empty(&new_batch)) { conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL, NULL, NULL, now, 0); } } static void test_pcap(struct ovs_cmdl_context *ctx) { size_t total_count, batch_size_; struct pcap_file *pcap; int err = 0; pcap = ovs_pcap_open(ctx->argv[1], "rb"); if (!pcap) { return; } batch_size_ = 1; if (ctx->argc > 2) { batch_size_ = strtoul(ctx->argv[2], NULL, 0); if (batch_size_ == 0 || batch_size_ > NETDEV_MAX_BURST) { ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)", NETDEV_MAX_BURST); } } fatal_signal_init(); ct = conntrack_init(); total_count = 0; for (;;) { struct dp_packet *packet; struct dp_packet_batch pkt_batch_; struct dp_packet_batch *batch = &pkt_batch_; dp_packet_batch_init(batch); for (int i = 0; i < batch_size_; i++) { err = ovs_pcap_read(pcap, &packet, NULL); if (err) { break; } dp_packet_batch_add(batch, packet); } if (dp_packet_batch_is_empty(batch)) { break; } pcap_batch_execute_conntrack(ct, batch); DP_PACKET_BATCH_FOR_EACH (i, packet, batch) { struct ds ds = DS_EMPTY_INITIALIZER; total_count++; format_flags(&ds, ct_state_to_string, packet->md.ct_state, '|'); printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds)); ds_destroy(&ds); } dp_packet_delete_batch(batch, true); } conntrack_destroy(ct); ovs_pcap_close(pcap); } static const struct ovs_cmdl_command commands[] = { /* Connection tracker tests. */ /* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to * the connection tracker, 'batch_size' per call. If 'change_connection' * is '1', each packet in a batch will have a different source and * destination port */ {"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4, test_benchmark, OVS_RO}, /* Reads packets from 'file' and sends them to the connection tracker, * 'batch_size' (1 by default) per call, with the commit flag set. * Prints the ct_state of each packet. */ {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO}, /* Creates 'n_conns' connections in 'n_zones' zones each. * Afterwards triggers flush requests repeadeatly for the last filled zone * and an empty zone. */ {"benchmark-zones", "n_conns n_zones iterations", 3, 3, test_benchmark_zones, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_conntrack_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-conntrack", test_conntrack_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-cooperative-multitasking.c000066400000000000000000000226601514270232600263020ustar00rootroot00000000000000/* * Copyright (c) 2023 Canonical Ltd. * * 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. */ #include #undef NDEBUG #include "cooperative-multitasking.h" #include "cooperative-multitasking-private.h" #include "openvswitch/hmap.h" #include "ovstest.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" struct fixture_arg { bool called; }; static void fixture_run_wrap(void *arg); #define FIXTURE_RUN_NAME "fixture_run" static void fixture_run(struct fixture_arg *arg) { cooperative_multitasking_set(&fixture_run_wrap, (void *) arg, time_msec(), 0, FIXTURE_RUN_NAME); if (arg) { arg->called = true; } } static void fixture_run_wrap(void *arg) { struct fixture_arg *fixture_arg = (struct fixture_arg *) arg; fixture_run(fixture_arg); } static void fixture_other_run_wrap(void *arg); #define FIXTURE_OTHER_RUN_NAME "fixture_other_run" static void fixture_other_run(struct fixture_arg *arg) { cooperative_multitasking_set(&fixture_other_run_wrap, (void *) arg, time_msec(), 0, FIXTURE_OTHER_RUN_NAME); if (arg) { arg->called = true; } } static void fixture_other_run_wrap(void *arg) { struct fixture_arg *fixture_arg = (struct fixture_arg *) arg; fixture_other_run(fixture_arg); } static void test_cm_set_registration(void) { struct cm_entry *cm_entry; struct fixture_arg arg1 = { .called = false, }; struct fixture_arg arg2 = { .called = false, }; timeval_stop(); long long int now = time_msec(); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg1, 0, 1000, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg2, 0, 2000, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_other_run_wrap, NULL, 0, 3000, FIXTURE_OTHER_RUN_NAME); ovs_assert(hmap_count(&cooperative_multitasking_callbacks) == 3); HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) { if (cm_entry->arg == (void *) &arg1) { ovs_assert(cm_entry->cb == &fixture_run_wrap); ovs_assert(cm_entry->threshold == 1000); ovs_assert(cm_entry->last_run == now); } else if (cm_entry->arg == (void *) &arg2) { ovs_assert(cm_entry->cb == &fixture_run_wrap); ovs_assert(cm_entry->threshold == 2000); ovs_assert(cm_entry->last_run == now); } else if (cm_entry->cb == &fixture_other_run_wrap) { ovs_assert(cm_entry->arg == NULL); ovs_assert(cm_entry->threshold == 3000); ovs_assert(cm_entry->last_run == now); } else { OVS_NOT_REACHED(); } } cooperative_multitasking_remove(&fixture_other_run_wrap, NULL); ovs_assert(hmap_count(&cooperative_multitasking_callbacks) == 2); cooperative_multitasking_remove(&fixture_run_wrap, (void *) &arg2); ovs_assert(hmap_count(&cooperative_multitasking_callbacks) == 1); cooperative_multitasking_destroy(); } static void test_cm_set_update(void) { struct cm_entry *cm_entry; struct fixture_arg arg1 = { .called = false, }; struct fixture_arg arg2 = { .called = false, }; timeval_stop(); long long int now = time_msec(); /* First register a couple of callbacks. */ cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg1, 0, 0, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg2, 0, 0, FIXTURE_RUN_NAME); ovs_assert(hmap_count(&cooperative_multitasking_callbacks) == 2); HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) { if (cm_entry->arg == (void *) &arg1) { ovs_assert(cm_entry->threshold == 0); ovs_assert(cm_entry->last_run == now); } else if (cm_entry->arg == (void *) &arg2) { ovs_assert(cm_entry->threshold == 0); ovs_assert(cm_entry->last_run == now); } else { OVS_NOT_REACHED(); } } /* Update 'last_run' and 'threshold' for each callback and validate * that the correct entry was actually updated. */ cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg1, 1, 2, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg2, 3, 4, FIXTURE_RUN_NAME); HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) { if (cm_entry->arg == (void *) &arg1) { ovs_assert(cm_entry->threshold == 2); ovs_assert(cm_entry->last_run == 1); } else if (cm_entry->arg == (void *) &arg2) { ovs_assert(cm_entry->threshold == 4); ovs_assert(cm_entry->last_run == 3); } else { OVS_NOT_REACHED(); } } /* Confirm that providing 0 for 'last_run' or 'threshold' leaves the * existing value untouched. */ cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg1, 0, 5, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg2, 6, 0, FIXTURE_RUN_NAME); HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) { if (cm_entry->arg == (void *) &arg1) { ovs_assert(cm_entry->threshold == 5); ovs_assert(cm_entry->last_run == 1); } else if (cm_entry->arg == (void *) &arg2) { ovs_assert(cm_entry->threshold == 4); ovs_assert(cm_entry->last_run == 6); } else { OVS_NOT_REACHED(); } } cooperative_multitasking_destroy(); } static void test_cm_yield(void) { struct cm_entry *cm_entry; struct fixture_arg arg1 = { .called = false, }; struct fixture_arg arg2 = { .called = false, }; timeval_stop(); long long int now = time_msec(); /* First register a couple of callbacks. */ cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg1, 0, 1000, FIXTURE_RUN_NAME); cooperative_multitasking_set(&fixture_run_wrap, (void *) &arg2, 0, 2000, FIXTURE_RUN_NAME); ovs_assert(hmap_count(&cooperative_multitasking_callbacks) == 2); /* Call to yield should not execute callbacks until time threshold. */ cooperative_multitasking_yield(); ovs_assert(arg1.called == false); ovs_assert(arg2.called == false); HMAP_FOR_EACH (cm_entry, node, &cooperative_multitasking_callbacks) { ovs_assert(cm_entry->last_run == now); } /* Move clock forward and confirm the expected callbacks to be executed. */ timeval_warp(1000); timeval_stop(); cooperative_multitasking_yield(); ovs_assert(arg1.called == true); ovs_assert(arg2.called == false); /* Move clock forward and confirm the expected callbacks to be executed. */ arg1.called = arg2.called = false; timeval_warp(1000); timeval_stop(); cooperative_multitasking_yield(); ovs_assert(arg1.called == true); ovs_assert(arg2.called == true); cooperative_multitasking_destroy(); } static void fixture_buggy_run_wrap(void *arg); #define FIXTURE_BUGGY_RUN_NAME "fixture_buggy_run" static void fixture_buggy_run(struct fixture_arg *arg) { cooperative_multitasking_set(&fixture_buggy_run_wrap, (void *) arg, time_msec(), 0, FIXTURE_BUGGY_RUN_NAME); if (arg) { arg->called = true; } /* A real run function MUST NOT directly or indirectly call yield, this is * here to test the detection of such a programming error. */ cooperative_multitasking_yield(); } static void fixture_buggy_run_wrap(void *arg) { struct fixture_arg *fixture_arg = (struct fixture_arg *) arg; fixture_buggy_run(fixture_arg); } static void test_cooperative_multitasking_nested_yield(int argc OVS_UNUSED, char *argv[]) { struct fixture_arg arg1 = { .called = false, }; set_program_name(argv[0]); vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m"); vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF); time_msec(); /* Ensure timeval is initialized. */ cooperative_multitasking_set(&fixture_buggy_run_wrap, (void *) &arg1, 0, 1000, FIXTURE_BUGGY_RUN_NAME); timeval_warp(1000); cooperative_multitasking_yield(); cooperative_multitasking_destroy(); } static void test_cooperative_multitasking(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { time_msec(); /* Ensure timeval is initialized. */ test_cm_set_registration(); test_cm_set_update(); test_cm_yield(); } OVSTEST_REGISTER("test-cooperative-multitasking", test_cooperative_multitasking); OVSTEST_REGISTER("test-cooperative-multitasking-nested-yield", test_cooperative_multitasking_nested_yield); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-csum.c000066400000000000000000000222361514270232600222170ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "csum.h" #include #include #include #include #include #include #include #include #include "crc32c.h" #include "ovstest.h" #include "packets.h" #include "random.h" #include "unaligned.h" #include "util.h" struct test_case { char *data; size_t size; /* Test requires a multiple of 4. */ uint16_t csum; }; #define TEST_CASE(DATA, CSUM) { DATA, (sizeof DATA) - 1, CSUM } static const struct test_case test_cases[] = { /* RFC 1071 section 3. */ TEST_CASE("\x00\x01\xf2\x03" "\xf4\xf5\xf6\xf7", 0xffff - 0xddf2 /* ~0xddf2 */), /* http://www.sbprojects.com/projects/tcpip/theory/theory14.htm */ TEST_CASE("\x45\x00\x00\x28" "\x1F\xFD\x40\x00" "\x80\x06\x00\x00" "\xC0\xA8\x3B\x0A" "\xC0\xA8\x3B\x32", 0xe345), /* http://mathforum.org/library/drmath/view/54379.html */ TEST_CASE("\x86\x5e\xac\x60" "\x71\x2a\x81\xb5", 0xda60), }; static void mark(char c) { putchar(c); fflush(stdout); } #if 0 /* This code is useful for generating new test cases for RFC 1624 section 4. */ static void generate_rfc1624_test_case(void) { int i; for (i = 0; i < 10000000; i++) { uint32_t data[8]; int j; for (j = 0; j < 8; j++) { data[j] = random_uint32(); } data[7] &= 0x0000ffff; data[7] |= 0x55550000; if (ntohs(~csum(data, sizeof data - 2)) == 0xcd7a) { ovs_hex_dump(stdout, data, sizeof data, 0, false); exit(0); } } } #endif /* Make sure we get the calculation in RFC 1624 section 4 correct. */ static void test_rfc1624(void) { /* "...an IP packet header in which a 16-bit field m = 0x5555..." */ uint8_t data[32] = { 0xfe, 0x8f, 0xc1, 0x14, 0x4b, 0x6f, 0x70, 0x2a, 0x80, 0x29, 0x78, 0xc0, 0x58, 0x81, 0x77, 0xaa, 0x66, 0x64, 0xfc, 0x96, 0x63, 0x97, 0x64, 0xee, 0x12, 0x53, 0x1d, 0xa9, 0x2d, 0xa9, 0x55, 0x55 }; /* "...the one's complement sum of all other header octets is 0xCD7A." */ assert(ntohs(csum(data, sizeof data - 2)) == 0xffff - 0xcd7a); /* "...the header checksum would be: HC = ~(0xCD7A + 0x5555) = ~0x22D0 = 0xDD2F" */ assert(ntohs(csum(data, sizeof data)) == 0xdd2f); /* "a 16-bit field m = 0x5555 changes to m' = 0x3285..." */ data[30] = 0x32; data[31] = 0x85; /* "The new checksum via recomputation is: HC' = ~(0xCD7A + 0x3285) = ~0xFFFF = 0x0000" */ assert(ntohs(csum(data, sizeof data)) == 0x0000); /* "Applying [Eqn. 3] to the example above, we get the correct result: HC' = ~(C + (-m) + m') = ~(0x22D0 + ~0x5555 + 0x3285) = ~0xFFFF = 0x0000" */ assert(recalc_csum16(htons(0xdd2f), htons(0x5555), htons(0x3285)) == htons(0x0000)); mark('#'); } /* CRC32C checksum tests, based on Intel IPPs, Chapter 13, * ippsCRC32C_8u() example, found at the following location: * http://software.intel.com/sites/products/documentation/hpc/ipp/ipps/ */ static void test_crc32c(void) { int i; uint8_t data[48] = { 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* iSCSI Read PDU */ assert(ntohl(crc32c(data, 48)) == 0x563a96d9L); /* 32 bytes of all zeroes */ for (i = 0; i < 32; i++) data[i] = 0x00; assert(ntohl(crc32c(data, 32)) == 0xaa36918aL); /* 32 bytes of all ones */ for (i = 0; i < 32; i++) data[i] = 0xff; assert(ntohl(crc32c(data, 32)) == 0x43aba862L); /* 32 bytes of incrementing 00..1f */ for (i = 0; i < 32; i++) data[i] = i; assert(ntohl(crc32c(data, 32)) == 0x4e79dd46L); /* 32 bytes of decrementing 1f..00 */ for (i = 0; i < 32; i++) data[i] = 31 - i; assert(ntohl(crc32c(data, 32)) == 0x5cdb3f11L); mark('#'); } /* Check the IP pseudoheader calculation. */ static void test_pseudo(void) { ovs_be16 csum; /* Try an IP header similar to one that the tunnel code * might generate. */ struct ip_header ip = { .ip_ihl_ver = IP_IHL_VER(5, 4), .ip_tos = 0, .ip_tot_len = htons(134), .ip_id = 0, .ip_frag_off = htons(IP_DF), .ip_ttl = 64, .ip_proto = IPPROTO_UDP, .ip_csum = htons(0x1265), .ip_src = { .hi = htons(0x1400), .lo = htons(0x0002) }, .ip_dst = { .hi = htons(0x1400), .lo = htons(0x0001) } }; csum = csum_finish(packet_csum_pseudoheader(&ip)); assert(csum == htons(0xd779)); /* And also test something totally different to check for * corner cases. */ memset(&ip, 0xff, sizeof ip); csum = csum_finish(packet_csum_pseudoheader(&ip)); assert(csum == htons(0xff3c)); mark('#'); } static void test_csum_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { const struct test_case *tc; int i; for (tc = test_cases; tc < &test_cases[ARRAY_SIZE(test_cases)]; tc++) { const void *data = tc->data; const ovs_be16 *data16 = (OVS_FORCE const ovs_be16 *) data; const ovs_be32 *data32 = (OVS_FORCE const ovs_be32 *) data; uint32_t partial; /* Test csum(). */ assert(ntohs(csum(tc->data, tc->size)) == tc->csum); mark('.'); /* Test csum_add16(). */ partial = 0; for (i = 0; i < tc->size / 2; i++) { partial = csum_add16(partial, get_unaligned_be16(&data16[i])); } assert(ntohs(csum_finish(partial)) == tc->csum); mark('.'); /* Test csum_add32(). */ partial = 0; for (i = 0; i < tc->size / 4; i++) { partial = csum_add32(partial, get_unaligned_be32(&data32[i])); } assert(ntohs(csum_finish(partial)) == tc->csum); mark('.'); /* Test alternating csum_add16() and csum_add32(). */ partial = 0; for (i = 0; i < tc->size / 4; i++) { if (i % 2) { partial = csum_add32(partial, get_unaligned_be32(&data32[i])); } else { ovs_be16 u0 = get_unaligned_be16(&data16[i * 2]); ovs_be16 u1 = get_unaligned_be16(&data16[i * 2 + 1]); partial = csum_add16(partial, u0); partial = csum_add16(partial, u1); } } assert(ntohs(csum_finish(partial)) == tc->csum); mark('.'); /* Test csum_continue(). */ partial = 0; for (i = 0; i < tc->size / 4; i++) { if (i) { partial = csum_continue(partial, &data32[i], 4); } else { partial = csum_continue(partial, &data16[i * 2], 2); partial = csum_continue(partial, &data16[i * 2 + 1], 2); } } assert(ntohs(csum_finish(partial)) == tc->csum); mark('#'); } test_rfc1624(); test_crc32c(); test_pseudo(); /* Test recalc_csum16(). */ for (i = 0; i < 32; i++) { ovs_be16 old_u16, new_u16; ovs_be16 old_csum; ovs_be16 data[16]; int j, index; for (j = 0; j < ARRAY_SIZE(data); j++) { data[j] = (OVS_FORCE ovs_be16) random_uint32(); } old_csum = csum(data, sizeof data); index = random_range(ARRAY_SIZE(data)); old_u16 = data[index]; new_u16 = data[index] = (OVS_FORCE ovs_be16) random_uint32(); assert(csum(data, sizeof data) == recalc_csum16(old_csum, old_u16, new_u16)); mark('.'); } mark('#'); /* Test recalc_csum32(). */ for (i = 0; i < 32; i++) { ovs_be32 old_u32, new_u32; ovs_be16 old_csum; ovs_be32 data[16]; int j, index; for (j = 0; j < ARRAY_SIZE(data); j++) { data[j] = (OVS_FORCE ovs_be32) random_uint32(); } old_csum = csum(data, sizeof data); index = random_range(ARRAY_SIZE(data)); old_u32 = data[index]; new_u32 = data[index] = (OVS_FORCE ovs_be32) random_uint32(); assert(csum(data, sizeof data) == recalc_csum32(old_csum, old_u32, new_u32)); mark('.'); } mark('#'); putchar('\n'); } OVSTEST_REGISTER("test-csum", test_csum_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-daemon.py000066400000000000000000000032731514270232600227210ustar00rootroot00000000000000# Copyright (c) 2010, 2011 Nicira, Inc. # # 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. import argparse import signal import sys import time import ovs.daemon import ovs.util def handler(signum, _): raise Exception("Signal handler called with %d" % signum) def main(): if sys.platform != 'win32': # signal.SIGHUP does not exist on Windows signal.signal(signal.SIGHUP, handler) parser = argparse.ArgumentParser( description="Open vSwitch daemonization test program for Python.") parser.add_argument("-b", "--bail", action="store_true", help="Exit with an error after daemonize_start().") ovs.daemon.add_args(parser) args = parser.parse_args() ovs.daemon.handle_args(args) ovs.daemon.daemonize_start() if args.bail: sys.stderr.write("%s: exiting after daemonize_start() as requested\n" % ovs.util.PROGRAM_NAME) sys.exit(1) ovs.daemon.daemonize_complete() while True: time.sleep(1) if __name__ == '__main__': try: main() except SystemExit: # Let system.exit() calls complete normally raise except: sys.exit(ovs.daemon.RESTART_EXIT_CODE) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-dpparse.py000077500000000000000000000024471514270232600231210ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2022 Red Hat, Inc. # # 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. """test-dpparse reads flows from stdin and tries to parse them using the python flow parsing library. """ import fileinput import sys try: from ovs.flow.odp import ODPFlow except ImportError: sys.exit(0) def main(): for flow in fileinput.input(): try: result_flow = ODPFlow(flow) if flow != str(result_flow): print("in: {}".format(flow)) print("out: {}".format(str(result_flow))) raise ValueError("Flow conversion back to string failed") except Exception as e: print("Error parsing flow {}: {}".format(flow, e)) return 1 return 0 if __name__ == "__main__": sys.exit(main()) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-flows.c000066400000000000000000000064071514270232600224040ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2017 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "flow.h" #include #include #include #include #include "classifier.h" #include "openflow/openflow.h" #include "openvswitch/ofp-match.h" #include "openvswitch/ofp-print.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/vlog.h" #include "ovstest.h" #include "dp-packet.h" #include "pcap-file.h" #include "timeval.h" #include "util.h" static void test_flows_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ofp10_match expected_match; FILE *flows; struct pcap_file *pcap; int retval; int n = 0, errors = 0; set_program_name(argv[0]); flows = fopen(argv[1], "rb"); if (!flows) { ovs_fatal(errno, "failed to open %s", argv[1]); } pcap = ovs_pcap_open(argv[2], "rb"); if (!pcap) { ovs_fatal(errno, "failed to open %s", argv[2]); } while (fread(&expected_match, sizeof expected_match, 1, flows)) { struct dp_packet *packet; struct ofp10_match extracted_match; struct match match; struct flow flow; n++; retval = ovs_pcap_read(pcap, &packet, NULL); if (retval == EOF) { ovs_fatal(0, "unexpected end of file reading pcap file"); } else if (retval) { ovs_fatal(retval, "error reading pcap file"); } flow_extract(packet, &flow); flow.in_port.ofp_port = u16_to_ofp(1); match_wc_init(&match, &flow); ofputil_match_to_ofp10_match(&match, &extracted_match); if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) { char *exp_s = ofp10_match_to_string(&expected_match, NULL, 2); char *got_s = ofp10_match_to_string(&extracted_match, NULL, 2); errors++; printf("mismatch on packet #%d (1-based).\n", n); printf("Packet:\n"); ofp_print_packet(stdout, dp_packet_data(packet), dp_packet_size(packet), htonl(PT_ETH)); ovs_hex_dump(stdout, dp_packet_data(packet), dp_packet_size(packet), 0, true); match_print(&match, NULL); printf("Expected flow:\n%s\n", exp_s); printf("Actually extracted flow:\n%s\n", got_s); ovs_hex_dump(stdout, &expected_match, sizeof expected_match, 0, false); ovs_hex_dump(stdout, &extracted_match, sizeof extracted_match, 0, false); printf("\n"); free(exp_s); free(got_s); } dp_packet_delete(packet); } ovs_pcap_close(pcap); printf("checked %d packets, %d errors\n", n, errors); exit(errors != 0); } OVSTEST_REGISTER("test-flows", test_flows_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-hash.c000066400000000000000000000261021514270232600221670ustar00rootroot00000000000000/* * Copyright (c) 2009, 2012, 2014, 2015 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "hash.h" #include #include #include #include #include #include "jhash.h" #include "ovstest.h" static void set_bit(uint32_t array[3], int bit) { assert(bit >= 0 && bit <= 96); memset(array, 0, sizeof(uint32_t) * 3); if (bit < 96) { array[bit / 32] = UINT32_C(1) << (bit % 32); } } /* When bit == n_bits, the function just 0 sets the 'values'. */ static void set_bit128(ovs_u128 *values, int bit, int n_bits) { assert(bit >= 0 && bit <= 2048); memset(values, 0, n_bits/8); if (bit < n_bits) { int b = bit % 128; if (b < 64) { values[bit / 128].u64.lo = UINT64_C(1) << (b % 64); } else { values[bit / 128].u64.hi = UINT64_C(1) << (b % 64); } } } static uint64_t get_range128(ovs_u128 *value, int ofs, uint64_t mask) { if (ofs == 0) { return value->u64.lo & mask; } return ((ofs < 64 ? (value->u64.lo >> ofs) : 0) & mask) | ((ofs <= 64 ? (value->u64.hi << (64 - ofs)) : (value->u64.hi >> (ofs - 64)) & mask)); } static uint32_t hash_words_cb(uint32_t input) { return hash_words(&input, 1, 0); } static uint32_t jhash_words_cb(uint32_t input) { return jhash_words(&input, 1, 0); } static uint32_t hash_int_cb(uint32_t input) { return hash_int(input, 0); } static void check_word_hash(uint32_t (*hash)(uint32_t), const char *name, int min_unique) { int i, j; for (i = 0; i <= 32; i++) { uint32_t in1 = i < 32 ? UINT32_C(1) << i : 0; for (j = i + 1; j <= 32; j++) { uint32_t in2 = j < 32 ? UINT32_C(1) << j : 0; uint32_t out1 = hash(in1); uint32_t out2 = hash(in2); const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1; int ofs; for (ofs = 0; ofs < 32 - min_unique; ofs++) { uint32_t bits1 = (out1 >> ofs) & unique_mask; uint32_t bits2 = (out2 >> ofs) & unique_mask; if (bits1 == bits2) { printf("Partial collision for '%s':\n", name); printf("%s(%08"PRIx32") = %08"PRIx32"\n", name, in1, out1); printf("%s(%08"PRIx32") = %08"PRIx32"\n", name, in2, out2); printf("%d bits of output starting at bit %d " "are both 0x%"PRIx32"\n", min_unique, ofs, bits1); } } } } } static void check_3word_hash(uint32_t (*hash)(const uint32_t[], size_t, uint32_t), const char *name) { int i, j; for (i = 0; i <= 96; i++) { for (j = i + 1; j <= 96; j++) { uint32_t in0[3], in1[3], in2[3]; uint32_t out0,out1, out2; const int min_unique = 12; const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1; set_bit(in0, i); set_bit(in1, i); set_bit(in2, j); out0 = hash(in0, 3, 0); out1 = hash(in1, 3, 0); out2 = hash(in2, 3, 0); if (out0 != out1) { printf("%s hash not the same for non-64 aligned data " "%08"PRIx32" != %08"PRIx32"\n", name, out0, out1); } if ((out1 & unique_mask) == (out2 & unique_mask)) { printf("%s has a partial collision:\n", name); printf("hash(1 << %d) == %08"PRIx32"\n", i, out1); printf("hash(1 << %d) == %08"PRIx32"\n", j, out2); printf("The low-order %d bits of output are both " "0x%"PRIx32"\n", min_unique, out1 & unique_mask); } } } } static void check_hash_bytes128(void (*hash)(const void *, size_t, uint32_t, ovs_u128 *), const char *name, const int min_unique) { const uint64_t unique_mask = (UINT64_C(1) << min_unique) - 1; const int n_bits = sizeof(ovs_u128) * 8; int i, j; for (i = 0; i <= n_bits; i++) { OVS_PACKED(struct offset_ovs_u128 { uint32_t a; ovs_u128 b; }) in0; ovs_u128 in1; ovs_u128 out0, out1; set_bit128(&in1, i, n_bits); in0.b = in1; hash(&in0.b, sizeof(ovs_u128), 0, &out0); hash(&in1, sizeof(ovs_u128), 0, &out1); if (!ovs_u128_equals(out0, out1)) { printf("%s hash not the same for non-64 aligned data " "%016"PRIx64"%016"PRIx64" != %016"PRIx64"%016"PRIx64"\n", name, out0.u64.lo, out0.u64.hi, out1.u64.lo, out1.u64.hi); } for (j = i + 1; j <= n_bits; j++) { ovs_u128 in2; ovs_u128 out2; int ofs; set_bit128(&in2, j, n_bits); hash(&in2, sizeof(ovs_u128), 0, &out2); for (ofs = 0; ofs < 128 - min_unique; ofs++) { uint64_t bits1 = get_range128(&out1, ofs, unique_mask); uint64_t bits2 = get_range128(&out2, ofs, unique_mask); if (bits1 == bits2) { printf("%s has a partial collision:\n", name); printf("hash(1 << %d) == %016"PRIx64"%016"PRIx64"\n", i, out1.u64.hi, out1.u64.lo); printf("hash(1 << %d) == %016"PRIx64"%016"PRIx64"\n", j, out2.u64.hi, out2.u64.lo); printf("%d bits of output starting at bit %d " "are both 0x%016"PRIx64"\n", min_unique, ofs, bits1); } } } } } static void check_256byte_hash(void (*hash)(const void *, size_t, uint32_t, ovs_u128 *), const char *name, const int min_unique) { const uint64_t unique_mask = (UINT64_C(1) << min_unique) - 1; const int n_bits = sizeof(ovs_u128) * 8 * 16; int i, j; for (i = 0; i <= n_bits; i++) { OVS_PACKED(struct offset_ovs_u128 { uint32_t a; ovs_u128 b[16]; }) in0; ovs_u128 in1[16]; ovs_u128 out0, out1; set_bit128(in1, i, n_bits); for (j = 0; j < 16; j++) { in0.b[j] = in1[j]; } hash(&in0.b, sizeof(ovs_u128) * 16, 0, &out0); hash(in1, sizeof(ovs_u128) * 16, 0, &out1); if (!ovs_u128_equals(out0, out1)) { printf("%s hash not the same for non-64 aligned data " "%016"PRIx64"%016"PRIx64" != %016"PRIx64"%016"PRIx64"\n", name, out0.u64.lo, out0.u64.hi, out1.u64.lo, out1.u64.hi); } for (j = i + 1; j <= n_bits; j++) { ovs_u128 in2[16]; ovs_u128 out2; set_bit128(in2, j, n_bits); hash(in2, sizeof(ovs_u128) * 16, 0, &out2); if ((out1.u64.lo & unique_mask) == (out2.u64.lo & unique_mask)) { printf("%s has a partial collision:\n", name); printf("hash(1 << %4d) == %016"PRIx64"%016"PRIx64"\n", i, out1.u64.hi, out1.u64.lo); printf("hash(1 << %4d) == %016"PRIx64"%016"PRIx64"\n", j, out2.u64.hi, out2.u64.lo); printf("The low-order %d bits of output are both " "0x%"PRIx64"\n", min_unique, out1.u64.lo & unique_mask); } } } } static void test_hash_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { /* * The following tests check that all hashes computed with hash_function * with one 1-bit (or no 1-bits) set within a X-bit word have different * values in all N-bit consecutive comparisons. * * test_function(hash_function, test_name, N) * * Given a random distribution, the probability of at least one collision * in any set of N bits is approximately * * 1 - (prob of no collisions) * **(combination of all possible comparisons) * == 1 - ((2**N - 1)/2**N)**C(X+1,2) * == p * * There are (X-N) ways to pick N consecutive bits in a X-bit word, so if we * assumed independence then the chance of having no collisions in any of * those X-bit runs would be (1-p)**(X-N) == q. If this q is very small * and we can also find a relatively small 'magic number' N such that there * is no collision in any comparison, then it means we have a pretty good * hash function. * * The values of each parameters mentioned above for the tested hash * functions are summarized as follow: * * hash_function X N p q * ------------- --- --- ------- ------- * * hash_words_cb 32 11 0.22 0.0044 * jhash_words_cb 32 11 0.22 0.0044 * hash_int_cb 32 12 0.12 0.0078 * hash_bytes128 128 19 0.0156 0.174 * */ check_word_hash(hash_words_cb, "hash_words", 11); check_word_hash(jhash_words_cb, "jhash_words", 11); check_word_hash(hash_int_cb, "hash_int", 12); check_hash_bytes128(hash_bytes128, "hash_bytes128", 19); /* * The following tests check that all hashes computed with hash_function * with one 1-bit (or no 1-bits) set within Y X-bit word have different * values in their lowest N bits. * * test_function(hash_function, test_name, N) * * Given a random distribution, the probability of at least one collision * in any set of N bits is approximately * * 1 - (prob of no collisions) * **(combination of all possible comparisons) * == 1 - ((2**N - 1)/2**N)**C(Y*X+1,2) * == p * * If this p is not very small and we can also find a relatively small * 'magic number' N such that there is no collision in any comparison, * then it means we have a pretty good hash function. * * The values of each parameters mentioned above for the tested hash * functions are summarized as follow: * * hash_function Y X N p * ------------- --- --- --- ------- * * hash_words 3 32 12 0.68 * jhash_words 3 32 12 0.68 * hash_bytes128 16 128 23 0.22 * */ check_3word_hash(hash_words, "hash_words"); check_3word_hash(jhash_words, "jhash_words"); check_256byte_hash(hash_bytes128, "hash_bytes128", 23); } OVSTEST_REGISTER("test-hash", test_hash_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-heap.c000066400000000000000000000301351514270232600221620ustar00rootroot00000000000000/* * Copyright (c) 2012, 2014 Nicira, Inc. * * 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. */ /* A test for for functions and macros declared in heap.h. */ #include #undef NDEBUG #include "heap.h" #include #include #include #include #include "command-line.h" #include "ovstest.h" #include "random.h" #include "util.h" /* Sample heap element. */ struct element { uint32_t full_pri; struct heap_node heap_node; }; static struct element * element_from_heap_node(const struct heap_node *node) { return CONTAINER_OF(node, struct element, heap_node); } static int compare_uint32s(const void *a_, const void *b_) { const uint32_t *a = a_; const uint32_t *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'heap' is internally consistent and contains all 'n' of the * 'priorities'. */ static void check_heap(const struct heap *heap, const uint32_t priorities[], size_t n) { uint32_t *priorities_copy; uint32_t *elements_copy; struct element *element; size_t i; assert(heap_count(heap) == n); assert(heap_is_empty(heap) == !n); if (n > 0) { assert(heap_max(heap) == heap->array[1]); } /* Check indexes. */ for (i = 1; i <= n; i++) { assert(heap->array[i]->idx == i); } /* Check that priority values are internally consistent. */ for (i = 1; i <= n; i++) { element = element_from_heap_node(heap->array[i]); assert(element->heap_node.priority == (element->full_pri >> 16)); } /* Check the heap property. */ for (i = 1; i <= n; i++) { size_t parent = heap_parent__(i); size_t left = heap_left__(i); size_t right = heap_right__(i); if (parent >= 1) { assert(heap->array[parent]->priority >= heap->array[i]->priority); } if (left <= n) { assert(heap->array[left]->priority <= heap->array[i]->priority); } if (right <= n) { assert(heap->array[right]->priority <= heap->array[i]->priority); } } /* Check that HEAP_FOR_EACH iterates all the nodes in order. */ i = 0; HEAP_FOR_EACH (element, heap_node, heap) { assert(i < n); assert(&element->heap_node == heap->array[i + 1]); i++; } assert(i == n); priorities_copy = xmemdup(priorities, n * sizeof *priorities); elements_copy = xmalloc(n * sizeof *priorities); i = 0; HEAP_FOR_EACH (element, heap_node, heap) { elements_copy[i++] = element->heap_node.priority; } qsort(priorities_copy, n, sizeof *priorities_copy, compare_uint32s); qsort(elements_copy, n, sizeof *elements_copy, compare_uint32s); for (i = 0; i < n; i++) { assert((priorities_copy[i] >> 16) == elements_copy[i]); } free(priorities_copy); free(elements_copy); } static void shuffle(uint32_t *p, size_t n) { for (; n > 1; n--, p++) { uint32_t *q = &p[random_range(n)]; uint32_t tmp = *p; *p = *q; *q = tmp; } } /* Prints the values in 'heap', plus 'name' as a title. */ static void OVS_UNUSED print_heap(const char *name, struct heap *heap) { struct element *e; printf("%s:", name); HEAP_FOR_EACH (e, heap_node, heap) { printf(" %"PRIu32":%"PRIu32, e->full_pri >> 16, e->full_pri & 0xffff); } printf("\n"); } static int factorial(int n_items) { int n, i; n = 1; for (i = 2; i <= n_items; i++) { n *= i; } return n; } static void swap(uint32_t *a, uint32_t *b) { uint32_t tmp = *a; *a = *b; *b = tmp; } static void reverse(uint32_t *a, int n) { int i; for (i = 0; i < n / 2; i++) { int j = n - (i + 1); swap(&a[i], &a[j]); } } static bool next_permutation(uint32_t *a, int n) { int k; for (k = n - 2; k >= 0; k--) { if ((a[k] >> 16) < (a[k + 1] >> 16)) { int l; for (l = n - 1; ; l--) { if ((a[l] >> 16) > (a[k] >> 16)) { swap(&a[k], &a[l]); reverse(a + (k + 1), n - (k + 1)); return true; } } } } return false; } static void test_insert_delete__(struct element *elements, const uint32_t *insert, const uint32_t *delete, size_t n) { struct heap heap; size_t i; heap_init(&heap); check_heap(&heap, NULL, 0); for (i = 0; i < n; i++) { uint32_t priority = insert[i]; elements[i].full_pri = priority; heap_insert(&heap, &elements[i].heap_node, priority >> 16); check_heap(&heap, insert, i + 1); } for (i = 0; i < n; i++) { struct element *element; HEAP_FOR_EACH (element, heap_node, &heap) { if (element->full_pri == delete[i]) { goto found; } } OVS_NOT_REACHED(); found: heap_remove(&heap, &element->heap_node); check_heap(&heap, delete + i + 1, n - (i + 1)); } heap_destroy(&heap); } static void test_insert_delete_raw__(struct element *elements, const uint32_t *insert, unsigned int insert_pattern, const uint32_t *delete, unsigned int delete_pattern, size_t n) { struct heap heap; size_t i; heap_init(&heap); check_heap(&heap, NULL, 0); for (i = 0; i < n; i++) { uint32_t priority = insert[i]; elements[i].full_pri = priority; heap_raw_insert(&heap, &elements[i].heap_node, priority >> 16); if (insert_pattern & (1u << i)) { heap_rebuild(&heap); check_heap(&heap, insert, i + 1); } } for (i = 0; i < n; i++) { struct element *element; HEAP_FOR_EACH (element, heap_node, &heap) { if (element->full_pri == delete[i]) { goto found; } } OVS_NOT_REACHED(); found: heap_raw_remove(&heap, &element->heap_node); if (delete_pattern & (1u << i)) { heap_rebuild(&heap); check_heap(&heap, delete + i + 1, n - (i + 1)); } } heap_destroy(&heap); } static void test_heap_insert_delete_same_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete__(elements, insert, insert, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_reverse_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; uint32_t delete[N_ELEMS]; n_permutations++; for (i = 0; i < N_ELEMS; i++) { delete[N_ELEMS - i - 1] = insert[i]; } test_insert_delete__(elements, insert, delete, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_every_order(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 5 }; uint32_t insert[N_ELEMS]; int outer_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } outer_permutations = 0; do { struct element elements[N_ELEMS]; uint32_t delete[N_ELEMS]; int inner_permutations; outer_permutations++; for (i = 0; i < N_ELEMS; i++) { delete[i] = i << 16; } inner_permutations = 0; do { inner_permutations++; test_insert_delete__(elements, insert, delete, N_ELEMS); } while (next_permutation(delete, N_ELEMS)); assert(inner_permutations == factorial(N_ELEMS)); } while (next_permutation(insert, N_ELEMS)); assert(outer_permutations == factorial(N_ELEMS)); } static void test_heap_insert_delete_same_order_with_dups(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; unsigned int pattern; size_t i; for (pattern = 0; pattern < (1u << N_ELEMS); pattern += 2) { int n_permutations, expected_permutations; uint32_t insert[N_ELEMS]; int j; j = 0; for (i = 0; i < N_ELEMS; i++) { if (i && !(pattern & (1u << i))) { j++; } insert[i] = (j << 16) | i; } expected_permutations = factorial(N_ELEMS); for (i = 0; i < N_ELEMS; ) { j = i + 1; if (pattern & (1u << i)) { for (; j < N_ELEMS; j++) { if (!(pattern & (1u << j))) { break; } } expected_permutations /= factorial(j - i + 1); } i = j; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete__(elements, insert, insert, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == expected_permutations); } } static void test_heap_raw_insert(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 7 }; uint32_t insert[N_ELEMS]; int n_permutations; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; } n_permutations = 0; do { struct element elements[N_ELEMS]; n_permutations++; test_insert_delete_raw__(elements, insert, 1u << (N_ELEMS - 1), insert, UINT_MAX, N_ELEMS); } while (next_permutation(insert, N_ELEMS)); assert(n_permutations == factorial(N_ELEMS)); } static void test_heap_raw_delete(struct ovs_cmdl_context *ctx OVS_UNUSED) { enum { N_ELEMS = 16 }; uint32_t insert[N_ELEMS]; uint32_t delete[N_ELEMS]; size_t i; for (i = 0; i < N_ELEMS; i++) { insert[i] = i << 16; delete[i] = i << 16; } for (i = 0; i < 1000; i++) { struct element elements[N_ELEMS]; shuffle(insert, N_ELEMS); shuffle(delete, N_ELEMS); test_insert_delete_raw__(elements, insert, 0, delete, (1u << (N_ELEMS - 1)) | (1u << (N_ELEMS / 2)), N_ELEMS); } } static const struct ovs_cmdl_command commands[] = { { "insert-delete-same-order", NULL, 0, 0, test_heap_insert_delete_same_order, OVS_RO }, { "insert-delete-reverse-order", NULL, 0, 0, test_heap_insert_delete_reverse_order, OVS_RO }, { "insert-delete-every-order", NULL, 0, 0, test_heap_insert_delete_every_order, OVS_RO }, { "insert-delete-same-order-with-dups", NULL, 0, 0, test_heap_insert_delete_same_order_with_dups, OVS_RO }, { "raw-insert", NULL, 0, 0, test_heap_raw_insert, OVS_RO }, { "raw-delete", NULL, 0, 0, test_heap_raw_delete, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static void test_heap_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-heap", test_heap_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-hindex.c000066400000000000000000000221621514270232600225250ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2013, 2014 Nicira, Inc. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * hindex.h. */ #include #undef NDEBUG #include "hindex.h" #include #include #include "hash.h" #include "ovstest.h" #include "random.h" #include "util.h" /* Sample hindex element. */ struct element { int value; struct hindex_node node; }; typedef size_t hash_func(int value); static int compare_ints(const void *a_, const void *b_) { const int *a = a_; const int *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'hindex' contains exactly the 'n' values in 'values'. */ static void check_hindex(struct hindex *hindex, const int values[], size_t n, hash_func *hash) { int *sort_values, *hindex_values; struct element *e; size_t i; /* Check that all the values are there in iteration. */ sort_values = xmalloc(sizeof *sort_values * n); hindex_values = xmalloc(sizeof *sort_values * n); i = 0; HINDEX_FOR_EACH (e, node, hindex) { assert(i < n); hindex_values[i++] = e->value; } assert(i == n); memcpy(sort_values, values, sizeof *sort_values * n); qsort(sort_values, n, sizeof *sort_values, compare_ints); qsort(hindex_values, n, sizeof *hindex_values, compare_ints); for (i = 0; i < n; i++) { assert(sort_values[i] == hindex_values[i]); } free(hindex_values); free(sort_values); /* Check that all the values are there in lookup. */ for (i = 0; i < n; i++) { size_t count = 0; HINDEX_FOR_EACH_WITH_HASH (e, node, hash(values[i]), hindex) { count += e->value == values[i]; } assert(count == 1); } /* Check counters. */ assert(hindex_is_empty(hindex) == !n); assert(hindex->n_unique <= n); } /* Puts the 'n' values in 'values' into 'elements', and then puts those * elements into 'hindex'. */ static void make_hindex(struct hindex *hindex, struct element elements[], int values[], size_t n, hash_func *hash) { size_t i; hindex_init(hindex); for (i = 0; i < n; i++) { elements[i].value = i; hindex_insert(hindex, &elements[i].node, hash(elements[i].value)); values[i] = i; } } static void shuffle(int *p, size_t n) { for (; n > 1; n--, p++) { int *q = &p[random_range(n)]; int tmp = *p; *p = *q; *q = tmp; } } /* Prints the 'n' values in 'values', plus 'name' as a title. */ static void OVS_UNUSED print_ints(const char *name, const int *values, size_t n) { size_t i; printf("%s:", name); for (i = 0; i < n; i++) { printf(" %d", values[i]); } printf("\n"); } /* Prints the values in 'hindex', plus 'name' as a title. */ static void OVS_UNUSED print_hindex(const char *name, struct hindex *hindex) { struct element *e; printf("%s:", name); HINDEX_FOR_EACH (e, node, hindex) { printf(" %d(%"PRIuSIZE")", e->value, e->node.hash & hindex->mask); } printf("\n"); } static size_t unique_hash(int value) { return value; } static size_t good_hash(int value) { return hash_int(value, 0x1234abcd); } static size_t constant_hash(int value OVS_UNUSED) { return 123; } static size_t mod4_hash(int value) { return value % 4; } static size_t mod3_hash(int value) { return value % 3; } static size_t mod2_hash(int value) { return value % 2; } static size_t multipart_hash(int value) { return (mod4_hash(value) << 16) | (constant_hash(value) & 0xFFFF); } /* Tests basic hindex insertion and deletion. */ static void test_hindex_insert_delete(hash_func *hash) { enum { N_ELEMS = 100 }; struct element elements[N_ELEMS]; int values[N_ELEMS]; struct hindex hindex; size_t i; hindex_init(&hindex); for (i = 0; i < N_ELEMS; i++) { elements[i].value = i; hindex_insert(&hindex, &elements[i].node, hash(i)); values[i] = i; check_hindex(&hindex, values, i + 1, hash); } shuffle(values, N_ELEMS); for (i = 0; i < N_ELEMS; i++) { hindex_remove(&hindex, &elements[values[i]].node); check_hindex(&hindex, values + (i + 1), N_ELEMS - (i + 1), hash); } hindex_destroy(&hindex); } /* Tests basic hindex_reserve() and hindex_shrink(). */ static void test_hindex_reserve_shrink(hash_func *hash) { enum { N_ELEMS = 32 }; size_t i; for (i = 0; i < N_ELEMS; i++) { struct element elements[N_ELEMS]; int values[N_ELEMS]; struct hindex hindex; size_t j; hindex_init(&hindex); hindex_reserve(&hindex, i); for (j = 0; j < N_ELEMS; j++) { elements[j].value = j; hindex_insert(&hindex, &elements[j].node, hash(j)); values[j] = j; check_hindex(&hindex, values, j + 1, hash); } shuffle(values, N_ELEMS); for (j = 0; j < N_ELEMS; j++) { hindex_remove(&hindex, &elements[values[j]].node); hindex_shrink(&hindex); check_hindex(&hindex, values + (j + 1), N_ELEMS - (j + 1), hash); } hindex_destroy(&hindex); } } /* Tests that HINDEX_FOR_EACH_SAFE properly allows for deletion of the current * element of a hindex. */ static void test_hindex_for_each_safe(hash_func *hash) { enum { MAX_ELEMS = 10 }; size_t n; unsigned long int pattern; for (n = 0; n <= MAX_ELEMS; n++) { for (pattern = 0; pattern < 1ul << n; pattern++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct hindex hindex; struct element *e, *next; size_t n_remaining; int i; make_hindex(&hindex, elements, values, n, hash); i = 0; n_remaining = n; HINDEX_FOR_EACH_SAFE (e, next, node, &hindex) { if (hindex_next(&hindex, &e->node) == NULL) { assert(next == NULL); } else { assert(&next->node == hindex_next(&hindex, &e->node)); } assert(i < n); if (pattern & (1ul << e->value)) { size_t j; hindex_remove(&hindex, &e->node); for (j = 0; ; j++) { assert(j < n_remaining); if (values[j] == e->value) { values[j] = values[--n_remaining]; break; } } } check_hindex(&hindex, values, n_remaining, hash); i++; } assert(i == n); assert(next == NULL); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } assert(n == n_remaining); hindex_destroy(&hindex); /* Test short version (without the next variable). */ make_hindex(&hindex, elements, values, n, hash); i = 0; n_remaining = n; HINDEX_FOR_EACH_SAFE (e, node, &hindex) { assert(i < n); if (pattern & (1ul << e->value)) { size_t j; hindex_remove(&hindex, &e->node); for (j = 0; ; j++) { assert(j < n_remaining); if (values[j] == e->value) { values[j] = values[--n_remaining]; break; } } } check_hindex(&hindex, values, n_remaining, hash); i++; } assert(i == n); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } assert(n == n_remaining); hindex_destroy(&hindex); } } } static void run_test(void (*function)(hash_func *)) { hash_func *hash_funcs[] = { unique_hash, good_hash, constant_hash, mod4_hash, mod3_hash, mod2_hash, multipart_hash, }; size_t i; for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) { function(hash_funcs[i]); printf("."); fflush(stdout); } } static void test_hindex_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { run_test(test_hindex_insert_delete); run_test(test_hindex_for_each_safe); run_test(test_hindex_reserve_shrink); printf("\n"); } OVSTEST_REGISTER("test-hindex", test_hindex_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-hmap.c000066400000000000000000000233041514270232600221720ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2013, 2014 Nicira, Inc. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * hmap.h. */ #include #undef NDEBUG #include "openvswitch/hmap.h" #include #include #include "hash.h" #include "ovstest.h" #include "random.h" #include "util.h" /* Sample hmap element. */ struct element { int value; struct hmap_node node; }; typedef size_t hash_func(int value); static int compare_ints(const void *a_, const void *b_) { const int *a = a_; const int *b = b_; return *a < *b ? -1 : *a > *b; } /* Verifies that 'hmap' contains exactly the 'n' values in 'values'. */ static void check_hmap(struct hmap *hmap, const int values[], size_t n, hash_func *hash) { int *sort_values, *hmap_values; struct element *e; size_t i; /* Check that all the values are there in iteration. */ sort_values = xmalloc(sizeof *sort_values * n); hmap_values = xmalloc(sizeof *sort_values * n); i = 0; HMAP_FOR_EACH (e, node, hmap) { assert(i < n); hmap_values[i++] = e->value; } assert(i == n); assert(e == NULL); memcpy(sort_values, values, sizeof *sort_values * n); qsort(sort_values, n, sizeof *sort_values, compare_ints); qsort(hmap_values, n, sizeof *hmap_values, compare_ints); for (i = 0; i < n; i++) { assert(sort_values[i] == hmap_values[i]); } free(hmap_values); free(sort_values); /* Check that all the values are there in lookup. */ for (i = 0; i < n; i++) { size_t count = 0; HMAP_FOR_EACH_WITH_HASH (e, node, hash(values[i]), hmap) { count += e->value == values[i]; } assert(count == 1); assert(e == NULL); } /* Check counters. */ assert(hmap_is_empty(hmap) == !n); assert(hmap_count(hmap) == n); } /* Puts the 'n' values in 'values' into 'elements', and then puts those * elements into 'hmap'. */ static void make_hmap(struct hmap *hmap, struct element elements[], int values[], size_t n, hash_func *hash) { size_t i; hmap_init(hmap); for (i = 0; i < n; i++) { elements[i].value = i; hmap_insert(hmap, &elements[i].node, hash(elements[i].value)); values[i] = i; } } static void shuffle(int *p, size_t n) { for (; n > 1; n--, p++) { int *q = &p[random_range(n)]; int tmp = *p; *p = *q; *q = tmp; } } #if 0 /* Prints the values in 'hmap', plus 'name' as a title. */ static void print_hmap(const char *name, struct hmap *hmap) { struct element *e; printf("%s:", name); HMAP_FOR_EACH (e, node, hmap) { printf(" %d(%"PRIuSIZE")", e->value, e->node.hash & hmap->mask); } printf("\n"); } /* Prints the 'n' values in 'values', plus 'name' as a title. */ static void print_ints(const char *name, const int *values, size_t n) { size_t i; printf("%s:", name); for (i = 0; i < n; i++) { printf(" %d", values[i]); } printf("\n"); } #endif static size_t identity_hash(int value) { return value; } static size_t good_hash(int value) { return hash_int(value, 0x1234abcd); } static size_t constant_hash(int value OVS_UNUSED) { return 123; } /* Tests basic hmap insertion and deletion. */ static void test_hmap_insert_delete(hash_func *hash) { enum { N_ELEMS = 100 }; struct element elements[N_ELEMS]; int values[N_ELEMS]; struct hmap hmap; size_t i; hmap_init(&hmap); for (i = 0; i < N_ELEMS; i++) { elements[i].value = i; hmap_insert(&hmap, &elements[i].node, hash(i)); values[i] = i; check_hmap(&hmap, values, i + 1, hash); } shuffle(values, N_ELEMS); for (i = 0; i < N_ELEMS; i++) { hmap_remove(&hmap, &elements[values[i]].node); check_hmap(&hmap, values + (i + 1), N_ELEMS - (i + 1), hash); } hmap_destroy(&hmap); } /* Tests basic hmap_reserve() and hmap_shrink(). */ static void test_hmap_reserve_shrink(hash_func *hash) { enum { N_ELEMS = 32 }; size_t i; for (i = 0; i < N_ELEMS; i++) { struct element elements[N_ELEMS]; int values[N_ELEMS]; struct hmap hmap; size_t j; hmap_init(&hmap); hmap_reserve(&hmap, i); for (j = 0; j < N_ELEMS; j++) { elements[j].value = j; hmap_insert(&hmap, &elements[j].node, hash(j)); values[j] = j; check_hmap(&hmap, values, j + 1, hash); } shuffle(values, N_ELEMS); for (j = 0; j < N_ELEMS; j++) { hmap_remove(&hmap, &elements[values[j]].node); hmap_shrink(&hmap); check_hmap(&hmap, values + (j + 1), N_ELEMS - (j + 1), hash); } hmap_destroy(&hmap); } } /* Tests that HMAP_FOR_EACH_SAFE properly allows for deletion of the current * element of a hmap. */ static void test_hmap_for_each_safe(hash_func *hash) { enum { MAX_ELEMS = 10 }; size_t n; unsigned long int pattern; for (n = 0; n <= MAX_ELEMS; n++) { for (pattern = 0; pattern < 1ul << n; pattern++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct hmap hmap; struct element *e, *next; size_t n_remaining; int i; make_hmap(&hmap, elements, values, n, hash); i = 0; n_remaining = n; HMAP_FOR_EACH_SAFE (e, next, node, &hmap) { if (hmap_next(&hmap, &e->node) == NULL) { assert(next == NULL); } else { assert(&next->node == hmap_next(&hmap, &e->node)); } assert(i < n); if (pattern & (1ul << e->value)) { size_t j; hmap_remove(&hmap, &e->node); for (j = 0; ; j++) { assert(j < n_remaining); if (values[j] == e->value) { values[j] = values[--n_remaining]; break; } } } check_hmap(&hmap, values, n_remaining, hash); i++; } assert(i == n); assert(next == NULL); assert(e == NULL); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } assert(n == n_remaining); hmap_destroy(&hmap); /* Test short version (without next variable). */ make_hmap(&hmap, elements, values, n, hash); i = 0; n_remaining = n; HMAP_FOR_EACH_SAFE (e, node, &hmap) { assert(i < n); if (pattern & (1ul << e->value)) { size_t j; hmap_remove(&hmap, &e->node); for (j = 0; ; j++) { assert(j < n_remaining); if (values[j] == e->value) { values[j] = values[--n_remaining]; break; } } } check_hmap(&hmap, values, n_remaining, hash); i++; } assert(i == n); assert(e == NULL); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } assert(n == n_remaining); hmap_destroy(&hmap); } } } /* Tests that HMAP_FOR_EACH_POP removes every element of a hmap. */ static void test_hmap_for_each_pop(hash_func *hash) { enum { MAX_ELEMS = 10 }; size_t n; for (n = 0; n <= MAX_ELEMS; n++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct hmap hmap; struct element *e; size_t n_remaining, i; make_hmap(&hmap, elements, values, n, hash); i = 0; n_remaining = n; HMAP_FOR_EACH_POP (e, node, &hmap) { size_t j; assert(i < n); for (j = 0; ; j++) { assert(j < n_remaining); if (values[j] == e->value) { values[j] = values[--n_remaining]; break; } } /* Trash the element memory (including the hmap node) */ memset(e, 0, sizeof *e); check_hmap(&hmap, values, n_remaining, hash); i++; } assert(i == n); assert(e == NULL); hmap_destroy(&hmap); } } static void run_test(void (*function)(hash_func *)) { hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash }; size_t i; for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) { function(hash_funcs[i]); printf("."); fflush(stdout); } } static void test_hmap_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { run_test(test_hmap_insert_delete); run_test(test_hmap_for_each_safe); run_test(test_hmap_reserve_shrink); run_test(test_hmap_for_each_pop); printf("\n"); } OVSTEST_REGISTER("test-hmap", test_hmap_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-id-fpool.c000066400000000000000000000352171514270232600227640ustar00rootroot00000000000000/* * Copyright (c) 2021 NVIDIA Corporation. * * 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. */ #include #undef NDEBUG #include #include #include #include #include "command-line.h" #include "id-fpool.h" #include "id-pool.h" #include "openvswitch/vlog.h" #include "openvswitch/util.h" #include "ovs-thread.h" #include "ovs-rcu.h" #include "ovs-numa.h" #include "ovstest.h" #include "random.h" #include "timeval.h" #include "util.h" static void test_id_fpool_alloc(void) { const uint32_t base = 0; const uint32_t n_id = 10; struct id_fpool *pool = id_fpool_create(1, base, n_id); uint32_t ids[10]; size_t i; /* Can do n_id allocs. */ for (i = 0; i < n_id; i++) { ovs_assert(id_fpool_new_id(pool, 0, &ids[i])); ovs_assert(ids[i] >= base); ovs_assert(ids[i] < base + n_id); } /* Only n_id successful allocations. */ ovs_assert(id_fpool_new_id(pool, 0, NULL) == false); /* Monotonic alloc. */ for (i = 0; i < n_id - 1; i++) { ovs_assert(ids[i] < ids[i + 1]); } for (i = 0; i < n_id; i++) { id_fpool_free_id(pool, 0, ids[i]); } /* Can do n_id new allocs. */ for (i = 0; i < n_id; i++) { ovs_assert(id_fpool_new_id(pool, 0, &ids[i])); ovs_assert(ids[i] >= base); ovs_assert(ids[i] < base + n_id); } /* Only n_id successful allocations. */ ovs_assert(id_fpool_new_id(pool, 0, NULL) == false); for (i = 0; i < n_id; i++) { id_fpool_free_id(pool, 0, ids[i]); } id_fpool_destroy(pool); } static void test_id_fpool_alloc_range(void) { const uint32_t base = 200; const uint32_t n_id = 100; const uint32_t ceil = base + n_id; struct id_fpool *pool = id_fpool_create(1, base, n_id); bool id_allocated[100]; size_t i; memset(id_allocated, 0, sizeof id_allocated); /* Allocate all IDs only once. */ for (i = 0; i < n_id; i++) { uint32_t id; ovs_assert(id_fpool_new_id(pool, 0, &id)); ovs_assert(id >= base); ovs_assert(id < ceil); ovs_assert(id_allocated[id - base] == false); id_allocated[id - base] = true; } /* Only n_id successful allocations. */ ovs_assert(id_fpool_new_id(pool, 0, NULL) == false); for (i = 0; i < n_id; i++) { ovs_assert(id_allocated[i]); id_fpool_free_id(pool, 0, base + i); id_allocated[i] = false; } /* The full range is again fully available. */ for (i = 0; i < n_id; i++) { uint32_t id; ovs_assert(id_fpool_new_id(pool, 0, &id)); ovs_assert(id >= base); ovs_assert(id < ceil); ovs_assert(id_allocated[id - base] == false); id_allocated[id - base] = true; } id_fpool_destroy(pool); } static void test_id_fpool_alloc_steal(void) { /* N must be less than a slab size to force the second user * to steal from the first. */ #define N (ID_FPOOL_CACHE_SIZE / 2) bool ids[N]; struct id_fpool *pool; uint32_t id; size_t i; memset(ids, 0, sizeof ids); pool = id_fpool_create(2, 0, N); /* Fill up user 0 cache. */ ovs_assert(id_fpool_new_id(pool, 0, &id)); for (i = 0; i < N - 1; i++) { /* Check that user 1 can still alloc from user 0 cache. */ ovs_assert(id_fpool_new_id(pool, 1, &id)); } id_fpool_destroy(pool); } static void test_id_fpool_alloc_under_limit(void) { const size_t n_id = 100; uint32_t ids[100]; unsigned int limit; struct id_fpool *pool; size_t i; memset(ids, 0, sizeof ids); pool = id_fpool_create(1, 0, n_id); for (limit = 1; limit < n_id; limit++) { /* Allocate until arbitrary limit then free allocated ids. */ for (i = 0; i < limit; i++) { ovs_assert(id_fpool_new_id(pool, 0, &ids[i])); } for (i = 0; i < limit; i++) { id_fpool_free_id(pool, 0, ids[i]); } /* Verify that the N='limit' next allocations are under limit. */ for (i = 0; i < limit; i++) { ovs_assert(id_fpool_new_id(pool, 0, &ids[i])); ovs_assert(ids[i] < limit + ID_FPOOL_CACHE_SIZE); } for (i = 0; i < limit; i++) { id_fpool_free_id(pool, 0, ids[i]); } } id_fpool_destroy(pool); } static void run_tests(struct ovs_cmdl_context *ctx OVS_UNUSED) { test_id_fpool_alloc(); test_id_fpool_alloc_range(); test_id_fpool_alloc_steal(); test_id_fpool_alloc_under_limit(); } static uint32_t *ids; static uint64_t *thread_working_ms; /* Measured work time. */ static unsigned int n_threads; static unsigned int n_ids; static struct ovs_barrier barrier; #define TIMEOUT_MS (10 * 1000) /* 10 sec timeout */ static int running_time_ms; static volatile bool stop = false; static int elapsed(int *start) { return running_time_ms - *start; } static void swap_u32(uint32_t *a, uint32_t *b) { uint32_t t; t = *a; *a = *b; *b = t; } static void shuffle(uint32_t *p, size_t n) { for (; n > 1; n--, p++) { uint32_t *q = &p[random_range(n)]; swap_u32(p, q); } } static void print_result(const char *prefix) { uint64_t avg; size_t i; avg = 0; for (i = 0; i < n_threads; i++) { avg += thread_working_ms[i]; } avg /= n_threads ? n_threads : 1; printf("%s: ", prefix); for (i = 0; i < n_threads; i++) { if (thread_working_ms[i] >= TIMEOUT_MS) { printf(" %5" PRIu64 "+", thread_working_ms[i]); } else { printf(" %6" PRIu64, thread_working_ms[i]); } } if (avg >= TIMEOUT_MS) { printf(" -1 ms\n"); } else { printf(" %6" PRIu64 " ms\n", avg); } } struct id_fpool_aux { struct id_fpool *pool; atomic_uint thread_id; }; static void * id_fpool_thread(void *aux_) { unsigned int n_ids_per_thread; struct id_fpool_aux *aux = aux_; uint32_t *th_ids; unsigned int tid; int start; size_t i; atomic_add(&aux->thread_id, 1u, &tid); n_ids_per_thread = n_ids / n_threads; th_ids = &ids[tid * n_ids_per_thread]; /* NEW / ALLOC */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { ovs_ignore(id_fpool_new_id(aux->pool, tid, &th_ids[i])); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* DEL */ shuffle(th_ids, n_ids_per_thread); start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { id_fpool_free_id(aux->pool, tid, th_ids[i]); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* MIX */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { ovs_ignore(id_fpool_new_id(aux->pool, tid, &th_ids[i])); id_fpool_free_id(aux->pool, tid, th_ids[i]); ovs_ignore(id_fpool_new_id(aux->pool, tid, &th_ids[i])); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* Do not interfere with other threads still in 'MIX' phase. */ for (i = 0; i < n_ids_per_thread; i++) { id_fpool_free_id(aux->pool, tid, th_ids[i]); } ovs_barrier_block(&barrier); /* MIX SHUFFLED */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { if (elapsed(&start) >= TIMEOUT_MS) { break; } ovs_ignore(id_fpool_new_id(aux->pool, tid, &th_ids[i])); swap_u32(&th_ids[i], &th_ids[random_range(i + 1)]); id_fpool_free_id(aux->pool, tid, th_ids[i]); ovs_ignore(id_fpool_new_id(aux->pool, tid, &th_ids[i])); } thread_working_ms[tid] = elapsed(&start); return NULL; } static void benchmark_id_fpool(void) { pthread_t *threads; struct id_fpool_aux aux; size_t i; memset(ids, 0, n_ids & sizeof *ids); memset(thread_working_ms, 0, n_threads & sizeof *thread_working_ms); aux.pool = id_fpool_create(n_threads, 0, n_ids); atomic_store(&aux.thread_id, 0); for (i = n_ids - (n_ids % n_threads); i < n_ids; i++) { id_fpool_new_id(aux.pool, 0, &ids[i]); } threads = xmalloc(n_threads * sizeof *threads); ovs_barrier_init(&barrier, n_threads + 1); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("id_fpool_alloc", id_fpool_thread, &aux); } ovs_barrier_block(&barrier); print_result("id-fpool new"); ovs_barrier_block(&barrier); print_result("id-fpool del"); ovs_barrier_block(&barrier); /* Cleanup. */ ovs_barrier_block(&barrier); print_result("id-fpool mix"); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } print_result("id-fpool rnd"); id_fpool_destroy(aux.pool); ovs_barrier_destroy(&barrier); free(threads); } struct id_pool_aux { struct id_pool *pool; struct ovs_mutex *lock; atomic_uint thread_id; }; static void * id_pool_thread(void *aux_) { unsigned int n_ids_per_thread; struct id_pool_aux *aux = aux_; uint32_t *th_ids; unsigned int tid; int start; size_t i; atomic_add(&aux->thread_id, 1u, &tid); n_ids_per_thread = n_ids / n_threads; th_ids = &ids[tid * n_ids_per_thread]; /* NEW */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { ovs_mutex_lock(aux->lock); ovs_assert(id_pool_alloc_id(aux->pool, &th_ids[i])); ovs_mutex_unlock(aux->lock); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* DEL */ shuffle(th_ids, n_ids_per_thread); start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { ovs_mutex_lock(aux->lock); id_pool_free_id(aux->pool, th_ids[i]); ovs_mutex_unlock(aux->lock); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* MIX */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { ovs_mutex_lock(aux->lock); ovs_ignore(id_pool_alloc_id(aux->pool, &th_ids[i])); id_pool_free_id(aux->pool, th_ids[i]); ovs_ignore(id_pool_alloc_id(aux->pool, &th_ids[i])); ovs_mutex_unlock(aux->lock); } thread_working_ms[tid] = elapsed(&start); ovs_barrier_block(&barrier); /* Do not interfere with other threads still in 'MIX' phase. */ ovs_mutex_lock(aux->lock); for (i = 0; i < n_ids_per_thread; i++) { id_pool_free_id(aux->pool, th_ids[i]); } ovs_mutex_unlock(aux->lock); ovs_barrier_block(&barrier); /* MIX SHUFFLED */ start = running_time_ms; for (i = 0; i < n_ids_per_thread; i++) { if (elapsed(&start) >= TIMEOUT_MS) { break; } ovs_mutex_lock(aux->lock); ovs_ignore(id_pool_alloc_id(aux->pool, &th_ids[i])); swap_u32(&th_ids[i], &th_ids[random_range(i + 1)]); id_pool_free_id(aux->pool, th_ids[i]); ovs_ignore(id_pool_alloc_id(aux->pool, &th_ids[i])); ovs_mutex_unlock(aux->lock); } thread_working_ms[tid] = elapsed(&start); return NULL; } OVS_UNUSED static void benchmark_id_pool(void) { pthread_t *threads; struct id_pool_aux aux; struct ovs_mutex lock; size_t i; memset(ids, 0, n_ids & sizeof *ids); memset(thread_working_ms, 0, n_threads & sizeof *thread_working_ms); aux.pool = id_pool_create(0, n_ids); aux.lock = &lock; ovs_mutex_init(&lock); atomic_store(&aux.thread_id, 0); for (i = n_ids - (n_ids % n_threads); i < n_ids; i++) { id_pool_alloc_id(aux.pool, &ids[i]); } threads = xmalloc(n_threads * sizeof *threads); ovs_barrier_init(&barrier, n_threads + 1); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("id_pool_alloc", id_pool_thread, &aux); } ovs_barrier_block(&barrier); print_result(" id-pool new"); ovs_barrier_block(&barrier); print_result(" id-pool del"); ovs_barrier_block(&barrier); /* Cleanup. */ ovs_barrier_block(&barrier); print_result(" id-pool mix"); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } print_result(" id-pool rnd"); id_pool_destroy(aux.pool); ovs_barrier_destroy(&barrier); free(threads); } static void * clock_main(void *arg OVS_UNUSED) { struct timeval start; struct timeval end; xgettimeofday(&start); while (!stop) { xgettimeofday(&end); running_time_ms = timeval_to_msec(&end) - timeval_to_msec(&start); xnanosleep(1000); } return NULL; } static void do_perf_test(struct ovs_cmdl_context *ctx, bool test_id_pool) { pthread_t clock; long int l_threads; long int l_ids; size_t i; l_ids = strtol(ctx->argv[1], NULL, 10); l_threads = strtol(ctx->argv[2], NULL, 10); ovs_assert(l_ids > 0 && l_threads > 0); n_ids = l_ids; n_threads = l_threads; ids = xcalloc(n_ids, sizeof *ids); thread_working_ms = xcalloc(n_threads, sizeof *thread_working_ms); clock = ovs_thread_create("clock", clock_main, NULL); printf("Benchmarking n=%u on %u thread%s.\n", n_ids, n_threads, n_threads > 1 ? "s" : ""); printf(" type\\thread: "); for (i = 0; i < n_threads; i++) { printf(" %3" PRIuSIZE " ", i + 1); } printf(" Avg\n"); ovsrcu_quiesce_start(); benchmark_id_fpool(); if (test_id_pool) { benchmark_id_pool(); } stop = true; free(thread_working_ms); xpthread_join(clock, NULL); } static void run_benchmark(struct ovs_cmdl_context *ctx) { do_perf_test(ctx, true); } static void run_perf(struct ovs_cmdl_context *ctx) { do_perf_test(ctx, false); } static const struct ovs_cmdl_command commands[] = { {"check", NULL, 0, 0, run_tests, OVS_RO}, {"benchmark", " ", 2, 2, run_benchmark, OVS_RO}, {"perf", " ", 2, 2, run_perf, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void id_fpool_test_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - optind, .argv = argv + optind, }; vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_OFF); set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-id-fpool", id_fpool_test_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-json.c000066400000000000000000000220611514270232600222150ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "openvswitch/json.h" #include #include #include #include #include "ovstest.h" #include "random.h" #include "timeval.h" #include "util.h" /* --pretty: If set, the JSON output is pretty-printed, instead of printed as * compactly as possible. */ static int pretty = 0; /* --multiple: If set, the input is a sequence of JSON objects or arrays, * instead of exactly one object or array. */ static int multiple = 0; static void test_json_equal(const struct json *a, const struct json *b, bool allow_the_same); static void test_json_equal_object(const struct shash *a, const struct shash *b, bool allow_the_same) { struct shash_node *a_node; ovs_assert(allow_the_same || a != b); if (a == b) { return; } ovs_assert(shash_count(a) == shash_count(b)); SHASH_FOR_EACH (a_node, a) { struct shash_node *b_node = shash_find(b, a_node->name); ovs_assert(b_node); test_json_equal(a_node->data, b_node->data, allow_the_same); } } static void test_json_equal_array(const struct json *a, const struct json *b, bool allow_the_same) { ovs_assert(allow_the_same || a != b); if (a == b) { return; } size_t n = json_array_size(a); ovs_assert(n == json_array_size(b)); for (size_t i = 0; i < n; i++) { test_json_equal(json_array_at(a, i), json_array_at(b, i), allow_the_same); } } static void test_json_equal(const struct json *a, const struct json *b, bool allow_the_same) { ovs_assert(allow_the_same || a != b); ovs_assert(a && b); if (a == b) { ovs_assert(a->count > 1); return; } ovs_assert(a->type == b->type); switch (a->type) { case JSON_OBJECT: test_json_equal_object(a->object, b->object, allow_the_same); return; case JSON_ARRAY: test_json_equal_array(a, b, allow_the_same); return; case JSON_STRING: ovs_assert(json_string(a) != json_string(b)); ovs_assert(!strcmp(json_string(a), json_string(b))); return; case JSON_SERIALIZED_OBJECT: ovs_assert(json_serialized_object(a) != json_serialized_object(b)); ovs_assert(!strcmp(json_serialized_object(a), json_serialized_object(b))); return; case JSON_NULL: case JSON_FALSE: case JSON_TRUE: return; case JSON_INTEGER: ovs_assert(a->integer == b->integer); return; case JSON_REAL: ovs_assert(a->real == b->real); return; case JSON_N_TYPES: default: OVS_NOT_REACHED(); } } static void test_json_clone(struct json *json) { struct json *copy, *deep_copy; copy = json_clone(json); ovs_assert(json_equal(json, copy)); test_json_equal(json, copy, true); ovs_assert(json->count == 2); json_destroy(copy); ovs_assert(json->count == 1); deep_copy = json_deep_clone(json); ovs_assert(json_equal(json, deep_copy)); test_json_equal(json, deep_copy, false); ovs_assert(json->count == 1); ovs_assert(deep_copy->count == 1); json_destroy(deep_copy); ovs_assert(json->count == 1); } static bool print_test_and_free_json(struct json *json) { bool ok; if (json->type == JSON_STRING) { printf("error: %s\n", json_string(json)); ok = false; } else { char *s = json_to_string(json, JSSF_SORT | (pretty ? JSSF_PRETTY : 0)); puts(s); free(s); ok = true; } test_json_clone(json); json_destroy(json); return ok; } static bool refill(FILE *file, void *buffer, size_t buffer_size, size_t *n, size_t *used) { *used = 0; if (feof(file)) { *n = 0; return false; } else { *n = fread(buffer, 1, buffer_size, file); if (ferror(file)) { ovs_fatal(errno, "Error reading input file"); } return *n > 0; } } static bool parse_multiple(FILE *stream) { struct json_parser *parser; char buffer[BUFSIZ]; size_t n, used; bool ok; parser = NULL; n = used = 0; ok = true; while (used < n || refill(stream, buffer, sizeof buffer, &n, &used)) { if (!parser && isspace((unsigned char) buffer[used])) { /* Skip white space. */ used++; } else { if (!parser) { parser = json_parser_create(0); } used += json_parser_feed(parser, &buffer[used], n - used); if (used < n) { if (!print_test_and_free_json(json_parser_finish(parser))) { ok = false; } parser = NULL; } } } if (parser) { if (!print_test_and_free_json(json_parser_finish(parser))) { ok = false; } } return ok; } static void test_json_main(int argc, char *argv[]) { const char *input_file; FILE *stream; bool ok; set_program_name(argv[0]); for (;;) { static const struct option options[] = { {"pretty", no_argument, &pretty, 1}, {"multiple", no_argument, &multiple, 1}, }; int option_index = 0; int c = getopt_long (argc, argv, "", options, &option_index); if (c == -1) { break; } switch (c) { case 0: break; case '?': exit(1); default: abort(); } } if (argc - optind != 1) { ovs_fatal(0, "usage: %s [--pretty] [--multiple] INPUT.json", program_name); } input_file = argv[optind]; stream = !strcmp(input_file, "-") ? stdin : fopen(input_file, "r"); if (!stream) { ovs_fatal(errno, "Cannot open \"%s\"", input_file); } if (multiple) { ok = parse_multiple(stream); } else { ok = print_test_and_free_json(json_from_stream(stream)); } fclose(stream); exit(!ok); } OVSTEST_REGISTER("test-json", test_json_main); static void json_string_benchmark_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct { int n; int quote_probablility; int special_probability; int iter; } configs[] = { { 100000, 0, 0, 1000, }, { 100000, 2, 1, 1000, }, { 100000, 10, 1, 1000, }, { 10000000, 0, 0, 100, }, { 10000000, 2, 1, 100, }, { 10000000, 10, 1, 100, }, { 100000000, 0, 0, 10. }, { 100000000, 2, 1, 10, }, { 100000000, 10, 1, 10, }, }; printf(" SIZE Q S TO STRING FROM STRING\n"); printf("----------------------------------------------------\n"); for (int i = 0; i < ARRAY_SIZE(configs); i++) { int iter = configs[i].iter; int n = configs[i].n; char *str = xzalloc(n); for (int j = 0; j < n - 1; j++) { int r = random_range(100); if (r < configs[i].special_probability) { str[j] = random_range(' ' - 1) + 1; } else if (r < (configs[i].special_probability + configs[i].quote_probablility)) { str[j] = '"'; } else { str[j] = random_range(256 - ' ') + ' '; } } printf("%-11d %-2d %-2d: ", n, configs[i].quote_probablility, configs[i].special_probability); fflush(stdout); struct json *json = json_array_create_1( json_string_create_nocopy(str)); uint64_t start = time_msec(); char **res = xzalloc(iter * sizeof *res); for (int j = 0; j < iter; j++) { res[j] = json_to_string(json, 0); } printf("%12.3lf ms", (double) (time_msec() - start) / iter); struct json **json_parsed = xzalloc(iter * sizeof *json_parsed); start = time_msec(); for (int j = 0; j < iter; j++) { json_parsed[j] = json_from_string(res[j]); } printf("%12.3lf ms\n", (double) (time_msec() - start) / iter); for (int j = 0; j < iter; j++) { ovs_assert(json_equal(json, json_parsed[j])); json_destroy(json_parsed[j]); free(res[j]); } json_destroy(json); free(res); free(json_parsed); } exit(0); } OVSTEST_REGISTER("json-string-benchmark", json_string_benchmark_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-json.py000066400000000000000000000044751514270232600224340ustar00rootroot00000000000000# Copyright (c) 2009, 2010 Nicira, Inc. # # 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. import getopt import sys import ovs.json def print_json(json): if isinstance(json, str): print("error: %s" % json) return False else: ovs.json.to_stream(json, sys.stdout) sys.stdout.write("\n") return True def parse_multiple(stream): buf = stream.read(4096) ok = True parser = None while len(buf): if parser is None and buf[0] in " \t\r\n": buf = buf[1:] else: if parser is None: parser = ovs.json.Parser() n = parser.feed(buf) buf = buf[n:] if len(buf): if not print_json(parser.finish()): ok = False parser = None if len(buf) == 0: buf = stream.read(4096) if parser and not print_json(parser.finish()): ok = False return ok def main(argv): argv0 = argv[0] try: options, args = getopt.gnu_getopt(argv[1:], '', ['multiple']) except getopt.GetoptError as geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) multiple = False for key, value in options: if key == '--multiple': multiple = True else: sys.stderr.write("%s: unhandled option %s\n" % (argv0, key)) sys.exit(1) if len(args) != 1: sys.stderr.write("usage: %s [--multiple] INPUT.json\n" % argv0) sys.exit(1) input_file = args[0] if input_file == "-": stream = sys.stdin else: stream = open(input_file, "r") if multiple: ok = parse_multiple(stream) else: ok = print_json(ovs.json.from_stream(stream)) if not ok: sys.exit(1) if __name__ == '__main__': main(sys.argv) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-jsonrpc.c000066400000000000000000000222661514270232600227310ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "jsonrpc.h" #include #include #include #include #include #include "command-line.h" #include "daemon.h" #include "openvswitch/json.h" #include "ovstest.h" #include "openvswitch/poll-loop.h" #include "stream-ssl.h" #include "stream.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static struct ovs_cmdl_command *get_all_commands(void); static void test_jsonrpc_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); ctx.argc = argc - optind; ctx.argv = argv + optind; ovs_cmdl_run_command(&ctx, get_all_commands()); } static void parse_options(int argc, char *argv[]) { enum { OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1, DAEMON_OPTION_ENUMS, SSL_OPTION_ENUMS, }; static const struct option long_options[] = { {"verbose", optional_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, DAEMON_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, STREAM_SSL_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'v': vlog_set_verbosity(optarg); break; DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void) { printf("%s: JSON-RPC test utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" " listen LOCAL listen for connections on LOCAL\n" " request REMOTE METHOD PARAMS send request, print reply\n" " notify REMOTE METHOD PARAMS send notification and exit\n", program_name, program_name); stream_usage("JSON-RPC", true, true, true); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n"); exit(EXIT_SUCCESS); } /* Command helper functions. */ static struct json * parse_json(const char *s) { struct json *json = json_from_string(s); if (json->type == JSON_STRING) { ovs_fatal(0, "\"%s\": %s", s, json_string(json)); } return json; } static void print_and_free_json(struct json *json) { char *string = json_to_string(json, JSSF_SORT); json_destroy(json); puts(string); free(string); } /* Command implementations. */ static int handle_rpc(struct jsonrpc *rpc, struct jsonrpc_msg *msg, bool *done) { if (msg->type == JSONRPC_REQUEST) { struct jsonrpc_msg *reply = NULL; if (!strcmp(msg->method, "echo")) { reply = jsonrpc_create_reply(json_clone(msg->params), msg->id); } else { struct json *error = json_object_create(); json_object_put_string(error, "error", "unknown method"); reply = jsonrpc_create_error(error, msg->id); ovs_error(0, "unknown request %s", msg->method); } jsonrpc_send(rpc, reply); return 0; } else if (msg->type == JSONRPC_NOTIFY) { if (!strcmp(msg->method, "shutdown")) { *done = true; return 0; } else { ovs_error(0, "unknown notification %s", msg->method); return ENOTTY; } } else { ovs_error(0, "unsolicited JSON-RPC reply or error"); return EPROTO; } } static void do_listen(struct ovs_cmdl_context *ctx) { struct pstream *pstream; struct jsonrpc **rpcs; size_t n_rpcs, allocated_rpcs; bool done; int error; error = jsonrpc_pstream_open(ctx->argv[1], &pstream, DSCP_DEFAULT); if (error) { ovs_fatal(error, "could not listen on \"%s\"", ctx->argv[1]); } daemonize(); rpcs = NULL; n_rpcs = allocated_rpcs = 0; done = false; for (;;) { struct stream *stream; size_t i; /* Accept new connections. */ error = pstream_accept(pstream, &stream); if (!error) { if (n_rpcs >= allocated_rpcs) { rpcs = x2nrealloc(rpcs, &allocated_rpcs, sizeof *rpcs); } rpcs[n_rpcs++] = jsonrpc_open(stream); } else if (error != EAGAIN) { ovs_fatal(error, "pstream_accept failed"); } /* Service existing connections. */ for (i = 0; i < n_rpcs; ) { struct jsonrpc *rpc = rpcs[i]; struct jsonrpc_msg *msg; jsonrpc_run(rpc); if (!jsonrpc_get_backlog(rpc)) { error = jsonrpc_recv(rpc, &msg); if (!error) { error = handle_rpc(rpc, msg, &done); jsonrpc_msg_destroy(msg); } else if (error == EAGAIN) { error = 0; } } if (!error) { error = jsonrpc_get_status(rpc); } if (error) { jsonrpc_close(rpc); ovs_error(error, "connection closed"); memmove(&rpcs[i], &rpcs[i + 1], (n_rpcs - i - 1) * sizeof *rpcs); n_rpcs--; } else { i++; } } /* Wait for something to do. */ if (done && !n_rpcs) { break; } pstream_wait(pstream); for (i = 0; i < n_rpcs; i++) { struct jsonrpc *rpc = rpcs[i]; jsonrpc_wait(rpc); if (!jsonrpc_get_backlog(rpc)) { jsonrpc_recv_wait(rpc); } } poll_block(); } free(rpcs); pstream_close(pstream); } static void do_request(struct ovs_cmdl_context *ctx) { struct jsonrpc_msg *msg; struct jsonrpc *rpc; struct json *params; struct stream *stream; const char *method; char *string; int error; method = ctx->argv[2]; params = parse_json(ctx->argv[3]); msg = jsonrpc_create_request(method, params, NULL); string = jsonrpc_msg_is_valid(msg); if (string) { ovs_fatal(0, "not a valid JSON-RPC request: %s", string); } error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream, DSCP_DEFAULT), -1, &stream); if (error) { ovs_fatal(error, "could not open \"%s\"", ctx->argv[1]); } rpc = jsonrpc_open(stream); error = jsonrpc_send(rpc, msg); if (error) { ovs_fatal(error, "could not send request"); } error = jsonrpc_recv_block(rpc, &msg); if (error) { ovs_fatal(error, "error waiting for reply"); } print_and_free_json(jsonrpc_msg_to_json(msg)); jsonrpc_close(rpc); } static void do_notify(struct ovs_cmdl_context *ctx) { struct jsonrpc_msg *msg; struct jsonrpc *rpc; struct json *params; struct stream *stream; const char *method; char *string; int error; method = ctx->argv[2]; params = parse_json(ctx->argv[3]); msg = jsonrpc_create_notify(method, params); string = jsonrpc_msg_is_valid(msg); if (string) { ovs_fatal(0, "not a JSON RPC-valid notification: %s", string); } error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream, DSCP_DEFAULT), -1, &stream); if (error) { ovs_fatal(error, "could not open \"%s\"", ctx->argv[1]); } rpc = jsonrpc_open(stream); error = jsonrpc_send_block(rpc, msg); if (error) { ovs_fatal(error, "could not send notification"); } jsonrpc_close(rpc); } static void do_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } static struct ovs_cmdl_command all_commands[] = { { "listen", NULL, 1, 1, do_listen, OVS_RO }, { "request", NULL, 3, 3, do_request, OVS_RO }, { "notify", NULL, 3, 3, do_notify, OVS_RO }, { "help", NULL, 0, INT_MAX, do_help, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static struct ovs_cmdl_command * get_all_commands(void) { return all_commands; } OVSTEST_REGISTER("test-jsonrpc", test_jsonrpc_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-jsonrpc.py000066400000000000000000000154501514270232600231340ustar00rootroot00000000000000# Copyright (c) 2009, 2010, 2011 Nicira, Inc. # # 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. import argparse import errno import os import sys import ovs.daemon import ovs.json import ovs.jsonrpc import ovs.poller import ovs.stream def handle_rpc(rpc, msg): done = False reply = None if msg.type == ovs.jsonrpc.Message.T_REQUEST: if msg.method == "echo": reply = ovs.jsonrpc.Message.create_reply(msg.params, msg.id) else: reply = ovs.jsonrpc.Message.create_error( {"error": "unknown method"}, msg.id) sys.stderr.write("unknown request %s" % msg.method) elif msg.type == ovs.jsonrpc.Message.T_NOTIFY: if msg.method == "shutdown": done = True else: rpc.error(errno.ENOTTY) sys.stderr.write("unknown notification %s" % msg.method) else: rpc.error(errno.EPROTO) sys.stderr.write("unsolicited JSON-RPC reply or error\n") if reply: rpc.send(reply) return done def do_listen(name): if sys.platform != 'win32' or ( ovs.daemon._detach and ovs.daemon._detached): # On Windows the child is a new process created which should be the # one that creates the PassiveStream. Without this check, the new # child process will create a new PassiveStream overwriting the one # that the parent process created. error, pstream = ovs.stream.PassiveStream.open(name) if error: sys.stderr.write("could not listen on \"%s\": %s\n" % (name, os.strerror(error))) sys.exit(1) ovs.daemon.daemonize() rpcs = [] done = False while True: # Accept new connections. error, stream = pstream.accept() if stream: rpcs.append(ovs.jsonrpc.Connection(stream)) elif error != errno.EAGAIN: sys.stderr.write("PassiveStream.accept() failed\n") sys.exit(1) # Service existing connections. dead_rpcs = [] for rpc in rpcs: rpc.run() error = 0 if not rpc.get_backlog(): error, msg = rpc.recv() if not error: if handle_rpc(rpc, msg): done = True error = rpc.get_status() if error: rpc.close() dead_rpcs.append(rpc) rpcs = [rpc for rpc in rpcs if rpc not in dead_rpcs] if done and not rpcs: break poller = ovs.poller.Poller() pstream.wait(poller) for rpc in rpcs: rpc.wait(poller) if not rpc.get_backlog(): rpc.recv_wait(poller) poller.block() pstream.close() def do_request(name, method, params_string): params = ovs.json.from_string(params_string) msg = ovs.jsonrpc.Message.create_request(method, params) s = msg.is_valid() if s: sys.stderr.write("not a valid JSON-RPC request: %s\n" % s) sys.exit(1) error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name)) if error: sys.stderr.write("could not open \"%s\": %s\n" % (name, os.strerror(error))) sys.exit(1) rpc = ovs.jsonrpc.Connection(stream) error = rpc.send(msg) if error: sys.stderr.write("could not send request: %s\n" % os.strerror(error)) sys.exit(1) error, msg = rpc.recv_block() if error: sys.stderr.write("error waiting for reply: %s\n" % os.strerror(error)) sys.exit(1) print(ovs.json.to_string(msg.to_json())) rpc.close() def do_notify(name, method, params_string): params = ovs.json.from_string(params_string) msg = ovs.jsonrpc.Message.create_notify(method, params) s = msg.is_valid() if s: sys.stderr.write("not a valid JSON-RPC notification: %s\n" % s) sys.exit(1) error, stream = ovs.stream.Stream.open_block(ovs.stream.Stream.open(name)) if error: sys.stderr.write("could not open \"%s\": %s\n" % (name, os.strerror(error))) sys.exit(1) rpc = ovs.jsonrpc.Connection(stream) error = rpc.send_block(msg) if error: sys.stderr.write("could not send notification: %s\n" % os.strerror(error)) sys.exit(1) rpc.close() def main(argv): parser = argparse.ArgumentParser( description="JSON-RPC test utility for Python.", formatter_class=argparse.RawDescriptionHelpFormatter) commands = {"listen": (do_listen, 1), "request": (do_request, 3), "notify": (do_notify, 3), "help": (parser.print_help, (0,))} group_description = """\ listen LOCAL listen for connections on LOCAL request REMOTE METHOD PARAMS send request, print reply notify REMOTE METHOD PARAMS send notification and exit """ + ovs.stream.usage("JSON-RPC") group = parser.add_argument_group(title="Commands", description=group_description) group.add_argument('command', metavar="COMMAND", nargs=1, choices=commands, help="Command to use.") group.add_argument('command_args', metavar="ARG", nargs='*', help="Arguments to COMMAND.") ovs.daemon.add_args(parser) args = parser.parse_args() ovs.daemon.handle_args(args) command_name = args.command[0] args = args.command_args if command_name not in commands: sys.stderr.write("%s: unknown command \"%s\" " "(use --help for help)\n" % (argv[0], command_name)) sys.exit(1) func, n_args = commands[command_name] if type(n_args) is tuple: if len(args) < n_args[0]: sys.stderr.write("%s: \"%s\" requires at least %d arguments but " "only %d provided\n" % (argv[0], command_name, n_args, len(args))) sys.exit(1) elif type(n_args) is int: if len(args) != n_args: sys.stderr.write("%s: \"%s\" requires %d arguments but %d " "provided\n" % (argv[0], command_name, n_args, len(args))) sys.exit(1) else: assert False func(*args) if __name__ == '__main__': main(sys.argv) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-l7.py000077500000000000000000000063341514270232600220040ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2015, 2016, 2020 Nicira, Inc. # # 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. import argparse import socket try: # Python 2.7 from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler from SocketServer import TCPServer except: from http.server import HTTPServer, SimpleHTTPRequestHandler from socketserver import TCPServer class TCPServerV6(HTTPServer): address_family = socket.AF_INET6 def get_ftpd(): try: from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer import logging import pyftpdlib.log pyftpdlib.log.LEVEL = logging.DEBUG class OVSFTPHandler(FTPHandler): authorizer = DummyAuthorizer() authorizer.add_anonymous("/tmp") # Hack around a bug in pyftpdlib, which rejects EPRT # connection due to mismatching textual representation of # the IPv6 address. permit_foreign_addresses = True server = [FTPServer, OVSFTPHandler, 21] except ImportError: server = None pass return server def get_tftpd(): try: from tftpy import TftpServer, TftpShared class OVSTFTPServer(TftpServer): def __init__(self, listen, handler=None): (ip, port) = listen self.ip = ip self.port = port TftpServer.__init__(self, tftproot='./') def serve_forever(self): self.listen(self.ip, self.port) server = [OVSTFTPServer, None, TftpShared.DEF_TFTP_PORT] except (ImportError, SyntaxError): server = None pass return server def main(): SERVERS = { 'http': [TCPServer, SimpleHTTPRequestHandler, 80], 'http6': [TCPServerV6, SimpleHTTPRequestHandler, 80], 'ftp': get_ftpd(), 'tftp': get_tftpd(), } protocols = [srv for srv in SERVERS if SERVERS[srv] is not None] parser = argparse.ArgumentParser( description='Run basic application servers.') parser.add_argument('proto', default='http', nargs='?', help='protocol to serve (%s)' % protocols) parser.add_argument('port', default=0, nargs='?', help='server port number') args = parser.parse_args() if args.proto not in protocols: parser.print_help() exit(1) constructor = SERVERS[args.proto][0] handler = SERVERS[args.proto][1] port = SERVERS[args.proto][2] if args.port != 0: port = args.port srv = constructor(('', port), handler) srv.serve_forever() if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-lib-route-table.c000066400000000000000000000124171514270232600242370ustar00rootroot00000000000000/* * Copyright (c) 2024 Canonical Ltd. * * 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. */ #include #undef NDEBUG #include #include #include #include "netlink-notifier.h" #include "ovstest.h" #include "packets.h" #include "route-table.h" /* The following definition should be available in Linux 6.15 and might be * missing if we have older headers. */ #ifndef RTPROT_OVN #define RTPROT_OVN 84 #endif /* Definition was added in Linux v4.18. */ #ifndef RTPROT_BGP #define RTPROT_BGP 186 #endif static char * rt_prot_name(unsigned char p) { /* We concentrate on the most used protocols, as they are the ones most * likely to be defined in the build environment. */ return p == RTPROT_UNSPEC ? "RTPROT_UNSPEC" : p == RTPROT_REDIRECT ? "RTPROT_REDIRECT" : p == RTPROT_KERNEL ? "RTPROT_KERNEL" : p == RTPROT_BOOT ? "RTPROT_BOOT" : p == RTPROT_STATIC ? "RTPROT_STATIC" : p == RTPROT_RA ? "RTPROT_RA" : p == RTPROT_DHCP ? "RTPROT_DHCP" : p == RTPROT_OVN ? "RTPROT_OVN" : p == RTPROT_BGP ? "RTPROT_BGP" : "UNKNOWN"; } static char * rt_table_name(uint32_t id) { static char tid[11] = ""; snprintf(tid, sizeof tid, "%"PRIu32, id); return id == RT_TABLE_UNSPEC ? "RT_TABLE_UNSPEC" : id == RT_TABLE_COMPAT ? "RT_TABLE_COMPAT" : id == RT_TABLE_DEFAULT ? "RT_TABLE_DEFAULT" : id == RT_TABLE_MAIN ? "RT_TABLE_MAIN" : id == RT_TABLE_LOCAL ? "RT_TABLE_LOCAL" : tid; } static void test_lib_route_table_handle_msg(const struct route_table_msg *change, void *data OVS_UNUSED, uint32_t table OVS_UNUSED) { struct ds nexthop_addr = DS_EMPTY_INITIALIZER; struct ds rta_prefsrc = DS_EMPTY_INITIALIZER; const struct route_data *rd = &change->rd; struct ds rta_dst = DS_EMPTY_INITIALIZER; const struct route_data_nexthop *rdnh; ipv6_format_mapped(&change->rd.rta_prefsrc, &rta_prefsrc); ipv6_format_mapped(&change->rd.rta_dst, &rta_dst); printf("%s/%u relevant: %d nlmsg_type: %d rtm_protocol: %s (%u) " "rtn_local: %d rta_prefsrc: %s rta_mark: %"PRIu32" " "rta_table_id: %s rta_priority: %"PRIu32"\n", ds_cstr(&rta_dst), rd->rtm_dst_len, change->relevant, change->nlmsg_type, rt_prot_name(rd->rtm_protocol), rd->rtm_protocol, rd->rtn_local, ds_cstr(&rta_prefsrc), rd->rta_mark, rt_table_name(rd->rta_table_id), rd->rta_priority); LIST_FOR_EACH (rdnh, nexthop_node, &rd->nexthops) { ds_clear(&nexthop_addr); ipv6_format_mapped(&rdnh->addr, &nexthop_addr); printf(" %s/%u nexthop family: %s addr: %s ifname: %s\n", ds_cstr(&rta_dst), rd->rtm_dst_len, rdnh->family == AF_INET ? "AF_INET" : rdnh->family == AF_INET6 ? "AF_INET6" : "UNKNOWN", ds_cstr(&nexthop_addr), rdnh->ifname); } ds_destroy(&nexthop_addr); ds_destroy(&rta_prefsrc); ds_destroy(&rta_dst); } static void test_lib_route_table_dump(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { route_table_dump_one_table(RT_TABLE_UNSPEC, test_lib_route_table_handle_msg, NULL); } static void test_lib_route_table_change(struct route_table_msg *change, void *aux OVS_UNUSED) { test_lib_route_table_handle_msg(change, NULL, 0); route_data_destroy(&change->rd); } static void test_lib_route_table_monitor(int argc, char *argv[]) { static struct nln_notifier *route6_notifier OVS_UNUSED; static struct nln_notifier *route_notifier OVS_UNUSED; static struct route_table_msg rtmsg; static struct nln *nln OVS_UNUSED; const char *cmd = argv[1]; if (argc != 2) { printf("usage: ovstest %s 'ip route add ...'\n", argv[0]); exit(EXIT_FAILURE); } nln = nln_create(NETLINK_ROUTE, route_table_parse, &rtmsg); route_notifier = nln_notifier_create(nln, RTNLGRP_IPV4_ROUTE, (nln_notify_func *) test_lib_route_table_change, NULL); route6_notifier = nln_notifier_create(nln, RTNLGRP_IPV6_ROUTE, (nln_notify_func *) test_lib_route_table_change, NULL); nln_run(nln); nln_wait(nln); int rc = system(cmd); if (rc) { exit(rc); } nln_run(nln); } OVSTEST_REGISTER("test-lib-route-table-monitor", test_lib_route_table_monitor); OVSTEST_REGISTER("test-lib-route-table-dump", test_lib_route_table_dump); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-lib.c000066400000000000000000000042111514270232600220070ustar00rootroot00000000000000/* * Copyright (C) 2014 Cisco Systems, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include static void show_version(void) { printf("%s - %s\n", ovs_get_program_name(), ovs_get_program_version()); exit(EXIT_SUCCESS); } static void usage(void) { printf("%s: Open vSwitch library test utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n\n", ovs_get_program_name(), ovs_get_program_name()); vlog_usage(); exit(EXIT_SUCCESS); } static void parse_options(int argc, char *argv[]) { static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"verbose", optional_argument, NULL, 'v'}, {NULL, 0, NULL, 0}, }; char *short_options = "hVv"; for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'V': show_version(); break; case 'h': usage(); break; case 'v': vlog_set_verbosity(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } } int main(int argc, char *argv[]) { ovs_set_program_name(argv[0], "1.0"); parse_options(argc, argv); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-list.c000066400000000000000000000164711514270232600222270ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2014 Nicira, Inc. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * openvswitch/list.h. */ #include #undef NDEBUG #include "openvswitch/list.h" #include #include #include #include "ovstest.h" /* Sample list element. */ struct element { int value; struct ovs_list node; }; /* Puts the 'n' values in 'values' into 'elements', and then puts those * elements in order into 'list'. */ static void make_list(struct ovs_list *list, struct element elements[], int values[], size_t n) { size_t i; ovs_list_init(list); for (i = 0; i < n; i++) { elements[i].value = i; ovs_list_push_back(list, &elements[i].node); values[i] = i; } } /* Verifies that 'list' contains exactly the 'n' values in 'values', in the * specified order. */ static void check_list(struct ovs_list *list, const int values[], size_t n) { struct element *e; size_t i; i = 0; LIST_FOR_EACH (e, node, list) { assert(i < n); assert(e->value == values[i]); i++; } assert(e == NULL); assert(i == n); i = 0; LIST_FOR_EACH_REVERSE (e, node, list) { assert(i < n); assert(e->value == values[n - i - 1]); i++; } assert(e == NULL); assert(i == n); assert(ovs_list_is_empty(list) == !n); assert(ovs_list_is_singleton(list) == (n == 1)); assert(ovs_list_is_short(list) == (n < 2)); assert(ovs_list_size(list) == n); } #if 0 /* Prints the values in 'list', plus 'name' as a title. */ static void print_list(const char *name, struct ovs_list *list) { struct element *e; printf("%s:", name); LIST_FOR_EACH (e, node, list) { printf(" %d", e->value); } printf("\n"); } #endif /* Tests basic list construction. */ static void test_list_construction(void) { enum { MAX_ELEMS = 100 }; size_t n; for (n = 0; n <= MAX_ELEMS; n++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct ovs_list list; memset(elements, 0, sizeof elements); memset(values, 0, sizeof values); make_list(&list, elements, values, n); check_list(&list, values, n); } } /* Tests that LIST_FOR_EACH_SAFE properly allows for deletion of the current * element of a list. */ static void test_list_for_each_safe(void) { enum { MAX_ELEMS = 10 }; size_t n; unsigned long int pattern; for (n = 0; n <= MAX_ELEMS; n++) { for (pattern = 0; pattern < 1ul << n; pattern++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct ovs_list list; struct element *e, *next; size_t values_idx, n_remaining; int i; make_list(&list, elements, values, n); i = 0; values_idx = 0; n_remaining = n; LIST_FOR_EACH_SAFE (e, next, node, &list) { /* "next" is valid as long as it's not pointing to &list. */ if (&e->node == list.prev) { assert(next == NULL); } else { assert(&next->node == e->node.next); } assert(i < n); if (pattern & (1ul << i)) { ovs_list_remove(&e->node); n_remaining--; memmove(&values[values_idx], &values[values_idx + 1], sizeof *values * (n_remaining - values_idx)); } else { values_idx++; } check_list(&list, values, n_remaining); i++; } assert(i == n); assert(e == NULL); assert(next == NULL); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } assert(n == n_remaining); /* Test short version (without next variable). */ make_list(&list, elements, values, n); i = 0; values_idx = 0; n_remaining = n; LIST_FOR_EACH_SAFE (e, node, &list) { assert(i < n); if (pattern & (1ul << i)) { ovs_list_remove(&e->node); n_remaining--; memmove(&values[values_idx], &values[values_idx + 1], sizeof *values * (n_remaining - values_idx)); } else { values_idx++; } check_list(&list, values, n_remaining); i++; } assert(i == n); assert(e == NULL); for (i = 0; i < n; i++) { if (pattern & (1ul << i)) { n_remaining++; } } } } } /* Tests that LIST_FOR_EACH_POP removes the elements of a list. */ static void test_list_for_each_pop(void) { enum { MAX_ELEMS = 10 }; size_t n; for (n = 0; n <= MAX_ELEMS; n++) { struct element elements[MAX_ELEMS]; int values[MAX_ELEMS]; struct ovs_list list; struct element *e; size_t n_remaining; make_list(&list, elements, values, n); n_remaining = n; LIST_FOR_EACH_POP (e, node, &list) { n_remaining--; memmove(values, values + 1, sizeof *values * n_remaining); check_list(&list, values, n_remaining); } } } /* Tests the transplant of one list into another */ static void test_list_push_back_all(void) { struct ovs_list list_a, list_b; struct element a, b, c, d; a.value = 0; b.value = 1; c.value = 2; d.value = 3; ovs_list_init(&list_a); ovs_list_init(&list_b); ovs_list_insert(&list_a, &a.node); ovs_list_insert(&list_a, &b.node); ovs_list_insert(&list_b, &c.node); ovs_list_insert(&list_b, &d.node); /* Check test preconditions */ assert(2 == ovs_list_size(&list_a)); assert(2 == ovs_list_size(&list_b)); /* Perform transplant */ ovs_list_push_back_all(&list_a, &list_b); /* Check expected result */ assert(4 == ovs_list_size(&list_a)); assert(0 == ovs_list_size(&list_b)); struct element *node; int n = 0; LIST_FOR_EACH(node, node, &list_a) { assert(n == node->value); n++; } assert(n == 4); } static void run_test(void (*function)(void)) { function(); printf("."); } static void test_list_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { run_test(test_list_construction); run_test(test_list_for_each_safe); run_test(test_list_for_each_pop); run_test(test_list_push_back_all); printf("\n"); } OVSTEST_REGISTER("test-list", test_list_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-lockfile.c000066400000000000000000000176371514270232600230510ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "lockfile.h" #include #include #include #include #include #include "ovstest.h" #include "process.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" struct test { const char *name; void (*function)(void); }; static void run_help(void); #define CHECK(A, B) check(A, B, #A, #B, __FILE__, __LINE__) static void check(int a, int b, const char *a_string, const char *b_string, const char *file, int line) { if (a != b) { fprintf(stderr, "%s:%d: expected %s == %s but %d != %d\n", file, line, a_string, b_string, a, b); fflush(stderr); abort(); } } static void run_lock_and_unlock(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); } static void run_lock_and_unlock_twice(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); } static void run_lock_blocks_same_process(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); CHECK(lockfile_lock("file", &lockfile), EDEADLK); lockfile_unlock(lockfile); } static void run_lock_blocks_same_process_twice(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); CHECK(lockfile_lock("file", &lockfile), EDEADLK); CHECK(lockfile_lock("file", &lockfile), EDEADLK); lockfile_unlock(lockfile); } #ifndef _WIN32 static enum { PARENT, CHILD } do_fork(void) { switch (fork()) { case 0: lockfile_postfork(); return CHILD; default: return PARENT; case -1: /* Error. */ ovs_fatal(errno, "fork failed"); } } static void run_lock_blocks_other_process(void) { /* Making this static prevents a memory leak warning from valgrind for the * parent process, which cannot easily unlock (and free) 'lockfile' because * it can only do so after the child has exited, and it's the caller of * this function that does the wait() call. */ static struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); if (do_fork() == CHILD) { lockfile_unlock(lockfile); CHECK(lockfile_lock("file", &lockfile), EAGAIN); exit(11); } } static void run_lock_twice_blocks_other_process(void) { struct lockfile *lockfile, *dummy; CHECK(lockfile_lock("file", &lockfile), 0); CHECK(lockfile_lock("file", &dummy), EDEADLK); if (do_fork() == CHILD) { CHECK(lockfile_lock("file", &dummy), EAGAIN); exit(11); } } static void run_lock_and_unlock_allows_other_process(void) { struct lockfile *lockfile; CHECK(lockfile_lock("file", &lockfile), 0); lockfile_unlock(lockfile); if (do_fork() == CHILD) { CHECK(lockfile_lock("file", &lockfile), 0); exit(11); } } /* Checks that locking a dangling symlink works OK. (It used to hang.) */ static void run_lock_symlink(void) { struct lockfile *a, *b, *dummy; struct stat s; /* Create a symlink .a.~lock~ pointing to .b.~lock~. */ CHECK(symlink(".b.~lock~", ".a.~lock~"), 0); CHECK(lstat(".a.~lock~", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); CHECK(stat(".a.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(stat(".b.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("a", &dummy), EDEADLK); CHECK(lockfile_lock("b", &dummy), EDEADLK); lockfile_unlock(a); CHECK(lockfile_lock("b", &b), 0); CHECK(lockfile_lock("b", &dummy), EDEADLK); CHECK(lockfile_lock("a", &dummy), EDEADLK); lockfile_unlock(b); CHECK(lstat(".a.~lock~", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); CHECK(stat(".a.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); CHECK(stat(".b.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); } /* Checks that locking a file that is itself a symlink yields a lockfile in the * directory that the symlink points to, named for the target of the * symlink. * * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named * "dir/.b.~lock".) */ static void run_lock_symlink_to_dir(void) { struct lockfile *a, *dummy; struct stat s; /* Create a symlink "a" pointing to "dir/b". */ CHECK(mkdir("dir", 0700), 0); CHECK(symlink("dir/b", "a"), 0); CHECK(lstat("a", &s), 0); CHECK(S_ISLNK(s.st_mode) != 0, 1); /* Lock 'a'. */ CHECK(lockfile_lock("a", &a), 0); CHECK(lstat("dir/.b.~lock~", &s), 0); CHECK(S_ISREG(s.st_mode) != 0, 1); CHECK(lstat(".a.~lock~", &s), -1); CHECK(errno, ENOENT); CHECK(lockfile_lock("dir/b", &dummy), EDEADLK); lockfile_unlock(a); } #endif /* _WIN32 */ static void run_lock_multiple(void) { struct lockfile *a, *b, *c, *dummy; CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("b", &b), 0); CHECK(lockfile_lock("c", &c), 0); lockfile_unlock(a); CHECK(lockfile_lock("a", &a), 0); CHECK(lockfile_lock("a", &dummy), EDEADLK); lockfile_unlock(a); lockfile_unlock(b); CHECK(lockfile_lock("a", &a), 0); lockfile_unlock(c); lockfile_unlock(a); } static const struct test tests[] = { #define TEST(NAME) { #NAME, run_##NAME } TEST(lock_and_unlock), TEST(lock_and_unlock_twice), TEST(lock_blocks_same_process), TEST(lock_blocks_same_process_twice), #ifndef _WIN32 TEST(lock_blocks_other_process), TEST(lock_twice_blocks_other_process), TEST(lock_and_unlock_allows_other_process), TEST(lock_symlink), TEST(lock_symlink_to_dir), #endif /* _WIN32 */ TEST(lock_multiple), TEST(help), { NULL, NULL } #undef TEST }; static void run_help(void) { size_t i; printf("usage: %s TESTNAME\n" "where TESTNAME is one of the following:\n", program_name); for (i = 0; tests[i].name; i++) { fprintf(stderr, "\t%s\n", tests[i].name); } } static void test_lockfile_main(int argc, char *argv[]) { size_t i; set_program_name(argv[0]); vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m"); vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF); if (argc != 2) { ovs_fatal(0, "exactly one argument required; use \"%s help\" for help", program_name); } for (i = 0; tests[i].name; i++) { if (!strcmp(argv[1], tests[i].name)) { int n_children; int status; (tests[i].function)(); n_children = 0; #ifndef _WIN32 while (wait(&status) > 0) { if (WIFEXITED(status) && WEXITSTATUS(status) == 11) { n_children++; } else { ovs_fatal(0, "child exited in unexpected way: %s", process_status_msg(status)); } } if (errno != ECHILD) { ovs_fatal(errno, "wait"); } #endif /* _WIN32 */ printf("%s: success (%d child%s)\n", tests[i].name, n_children, n_children != 1 ? "ren" : ""); exit(0); } } ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help", argv[1], program_name); } OVSTEST_REGISTER("test-lockfile", test_lockfile_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-mpsc-queue.c000066400000000000000000000472151514270232600233400ustar00rootroot00000000000000/* * Copyright (c) 2021 NVIDIA Corporation. * * 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. */ #include #undef NDEBUG #include #include #include #include #include "command-line.h" #include "guarded-list.h" #include "mpsc-queue.h" #include "openvswitch/list.h" #include "openvswitch/util.h" #include "openvswitch/vlog.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "ovstest.h" #include "timeval.h" #include "util.h" struct element { union { struct mpsc_queue_node mpscq; struct ovs_list list; } node; uint64_t mark; }; static void test_mpsc_queue_mark_element(struct mpsc_queue_node *node, uint64_t mark, unsigned int *counter) { struct element *elem; elem = CONTAINER_OF(node, struct element, node.mpscq); elem->mark = mark; *counter += 1; } static void test_mpsc_queue_insert(void) { struct element elements[100]; struct mpsc_queue_node *node; struct mpsc_queue queue; unsigned int counter; size_t i; memset(elements, 0, sizeof(elements)); mpsc_queue_init(&queue); mpsc_queue_acquire(&queue); for (i = 0; i < ARRAY_SIZE(elements); i++) { mpsc_queue_insert(&queue, &elements[i].node.mpscq); } counter = 0; while (mpsc_queue_poll(&queue, &node) == MPSC_QUEUE_ITEM) { test_mpsc_queue_mark_element(node, 1, &counter); } mpsc_queue_release(&queue); mpsc_queue_destroy(&queue); ovs_assert(counter == ARRAY_SIZE(elements)); for (i = 0; i < ARRAY_SIZE(elements); i++) { ovs_assert(elements[i].mark == 1); } printf("."); } static void test_mpsc_queue_removal_fifo(void) { struct element elements[100]; struct mpsc_queue_node *node; struct mpsc_queue queue; unsigned int counter; size_t i; memset(elements, 0, sizeof(elements)); mpsc_queue_init(&queue); mpsc_queue_acquire(&queue); for (i = 0; i < ARRAY_SIZE(elements); i++) { mpsc_queue_insert(&queue, &elements[i].node.mpscq); } /* Elements are in the same order in the list as they * were declared / initialized. */ counter = 0; while (mpsc_queue_poll(&queue, &node) == MPSC_QUEUE_ITEM) { test_mpsc_queue_mark_element(node, counter, &counter); } /* The list is valid once extracted from the queue, * the queue can be destroyed here. */ mpsc_queue_release(&queue); mpsc_queue_destroy(&queue); for (i = 0; i < ARRAY_SIZE(elements) - 1; i++) { struct element *e1, *e2; e1 = &elements[i]; e2 = &elements[i + 1]; ovs_assert(e1->mark < e2->mark); } printf("."); } /* Partial insert: * * Those functions are 'mpsc_queue_insert()' divided in two parts. * They serve to test the behavior of the queue when forcing the potential * condition of a thread starting an insertion then yielding. */ static struct mpsc_queue_node * mpsc_queue_insert_begin(struct mpsc_queue *queue, struct mpsc_queue_node *node) { struct mpsc_queue_node *prev; atomic_store_explicit(&node->next, NULL, memory_order_relaxed); prev = atomic_exchange_explicit(&queue->head, node, memory_order_acq_rel); return prev; } static void mpsc_queue_insert_end(struct mpsc_queue_node *prev, struct mpsc_queue_node *node) { atomic_store_explicit(&prev->next, node, memory_order_release); } static void test_mpsc_queue_insert_partial(void) { struct element elements[10]; struct mpsc_queue_node *prevs[ARRAY_SIZE(elements)]; struct mpsc_queue_node *node; struct mpsc_queue queue, *q = &queue; size_t i; mpsc_queue_init(q); /* Insert the first half of elements entirely, * insert the second hald of elements partially. */ for (i = 0; i < ARRAY_SIZE(elements); i++) { elements[i].mark = i; if (i > ARRAY_SIZE(elements) / 2) { prevs[i] = mpsc_queue_insert_begin(q, &elements[i].node.mpscq); } else { prevs[i] = NULL; mpsc_queue_insert(q, &elements[i].node.mpscq); } } mpsc_queue_acquire(q); /* Verify that when the chain is broken, iterators will stop. */ i = 0; MPSC_QUEUE_FOR_EACH (node, q) { struct element *e = CONTAINER_OF(node, struct element, node.mpscq); ovs_assert(e == &elements[i]); i++; } ovs_assert(i < ARRAY_SIZE(elements)); for (i = 0; i < ARRAY_SIZE(elements); i++) { if (prevs[i] != NULL) { mpsc_queue_insert_end(prevs[i], &elements[i].node.mpscq); } } i = 0; MPSC_QUEUE_FOR_EACH (node, q) { struct element *e = CONTAINER_OF(node, struct element, node.mpscq); ovs_assert(e == &elements[i]); i++; } ovs_assert(i == ARRAY_SIZE(elements)); MPSC_QUEUE_FOR_EACH_POP (node, q) { struct element *e = CONTAINER_OF(node, struct element, node.mpscq); ovs_assert(e->mark == (unsigned int)(e - elements)); } mpsc_queue_release(q); mpsc_queue_destroy(q); printf("."); } static void test_mpsc_queue_push_front(void) { struct mpsc_queue queue, *q = &queue; struct mpsc_queue_node *node; struct element elements[10]; size_t i; mpsc_queue_init(q); mpsc_queue_acquire(q); ovs_assert(mpsc_queue_pop(q) == NULL); mpsc_queue_push_front(q, &elements[0].node.mpscq); node = mpsc_queue_pop(q); ovs_assert(node == &elements[0].node.mpscq); ovs_assert(mpsc_queue_pop(q) == NULL); mpsc_queue_push_front(q, &elements[0].node.mpscq); mpsc_queue_push_front(q, &elements[1].node.mpscq); ovs_assert(mpsc_queue_pop(q) == &elements[1].node.mpscq); ovs_assert(mpsc_queue_pop(q) == &elements[0].node.mpscq); ovs_assert(mpsc_queue_pop(q) == NULL); mpsc_queue_push_front(q, &elements[1].node.mpscq); mpsc_queue_push_front(q, &elements[0].node.mpscq); mpsc_queue_insert(q, &elements[2].node.mpscq); ovs_assert(mpsc_queue_pop(q) == &elements[0].node.mpscq); ovs_assert(mpsc_queue_pop(q) == &elements[1].node.mpscq); ovs_assert(mpsc_queue_pop(q) == &elements[2].node.mpscq); ovs_assert(mpsc_queue_pop(q) == NULL); for (i = 0; i < ARRAY_SIZE(elements); i++) { elements[i].mark = i; mpsc_queue_insert(q, &elements[i].node.mpscq); } node = mpsc_queue_pop(q); mpsc_queue_push_front(q, node); ovs_assert(mpsc_queue_pop(q) == node); mpsc_queue_push_front(q, node); i = 0; MPSC_QUEUE_FOR_EACH (node, q) { struct element *e = CONTAINER_OF(node, struct element, node.mpscq); ovs_assert(e == &elements[i]); i++; } ovs_assert(i == ARRAY_SIZE(elements)); MPSC_QUEUE_FOR_EACH_POP (node, q) { struct element *e = CONTAINER_OF(node, struct element, node.mpscq); ovs_assert(e->mark == (unsigned int)(e - elements)); } mpsc_queue_release(q); mpsc_queue_destroy(q); printf("."); } static void run_tests(struct ovs_cmdl_context *ctx OVS_UNUSED) { /* Verify basic insertion. */ test_mpsc_queue_insert(); /* Test partial insertion. */ test_mpsc_queue_insert_partial(); /* Verify removal order is respected. */ test_mpsc_queue_removal_fifo(); /* Verify tail-end insertion works. */ test_mpsc_queue_push_front(); printf("\n"); } static struct element *elements; static uint64_t *thread_working_ms; /* Measured work time. */ static unsigned int n_threads; static unsigned int n_elems; static struct ovs_barrier barrier; static volatile bool working; static int elapsed(const struct timeval *start) { struct timeval end; xgettimeofday(&end); return timeval_to_msec(&end) - timeval_to_msec(start); } static void print_result(const char *prefix, int reader_elapsed) { uint64_t avg; size_t i; avg = 0; for (i = 0; i < n_threads; i++) { avg += thread_working_ms[i]; } avg /= n_threads ? n_threads : 1; printf("%s: %6d", prefix, reader_elapsed); for (i = 0; i < n_threads; i++) { printf(" %6" PRIu64, thread_working_ms[i]); } printf(" %6" PRIu64 " ms\n", avg); } struct mpscq_aux { struct mpsc_queue *queue; atomic_uint thread_id; }; static void * mpsc_queue_insert_thread(void *aux_) { unsigned int n_elems_per_thread; struct element *th_elements; struct mpscq_aux *aux = aux_; struct timeval start; unsigned int id; size_t i; atomic_add(&aux->thread_id, 1u, &id); n_elems_per_thread = n_elems / n_threads; th_elements = &elements[id * n_elems_per_thread]; ovs_barrier_block(&barrier); xgettimeofday(&start); for (i = 0; i < n_elems_per_thread; i++) { mpsc_queue_insert(aux->queue, &th_elements[i].node.mpscq); } thread_working_ms[id] = elapsed(&start); ovs_barrier_block(&barrier); working = false; return NULL; } static void benchmark_mpsc_queue(void) { struct mpsc_queue_node *node; struct mpsc_queue queue; struct timeval start; unsigned int counter; bool work_complete; pthread_t *threads; struct mpscq_aux aux; uint64_t epoch; size_t i; memset(elements, 0, n_elems & sizeof *elements); memset(thread_working_ms, 0, n_threads & sizeof *thread_working_ms); mpsc_queue_init(&queue); aux.queue = &queue; atomic_store(&aux.thread_id, 0); for (i = n_elems - (n_elems % n_threads); i < n_elems; i++) { mpsc_queue_insert(&queue, &elements[i].node.mpscq); } working = true; threads = xmalloc(n_threads * sizeof *threads); ovs_barrier_init(&barrier, n_threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("sc_queue_insert", mpsc_queue_insert_thread, &aux); } mpsc_queue_acquire(&queue); xgettimeofday(&start); counter = 0; epoch = 1; do { while (mpsc_queue_poll(&queue, &node) == MPSC_QUEUE_ITEM) { test_mpsc_queue_mark_element(node, epoch, &counter); } if (epoch == UINT64_MAX) { epoch = 0; } epoch++; } while (working); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } /* Elements might have been inserted before threads were joined. */ while (mpsc_queue_poll(&queue, &node) == MPSC_QUEUE_ITEM) { test_mpsc_queue_mark_element(node, epoch, &counter); } print_result(" mpsc-queue", elapsed(&start)); mpsc_queue_release(&queue); mpsc_queue_destroy(&queue); ovs_barrier_destroy(&barrier); free(threads); work_complete = true; for (i = 0; i < n_elems; i++) { if (elements[i].mark == 0) { printf("Element %" PRIuSIZE " was never consumed.\n", i); work_complete = false; } } ovs_assert(work_complete); ovs_assert(counter == n_elems); } #ifdef HAVE_PTHREAD_SPIN_LOCK #define spin_lock_type ovs_spin #define spin_lock_init(l) ovs_spin_init(l) #define spin_lock_destroy(l) ovs_spin_destroy(l) #define spin_lock(l) ovs_spin_lock(l) #define spin_unlock(l) ovs_spin_unlock(l) #else #define spin_lock_type ovs_mutex #define spin_lock_init(l) ovs_mutex_init(l) #define spin_lock_destroy(l) ovs_mutex_destroy(l) #define spin_lock(l) ovs_mutex_lock(l) #define spin_unlock(l) ovs_mutex_unlock(l) #endif struct list_aux { struct ovs_list *list; struct ovs_mutex *mutex; struct spin_lock_type *spin; atomic_uint thread_id; }; static void * locked_list_insert_main(void *aux_) OVS_NO_THREAD_SAFETY_ANALYSIS { unsigned int n_elems_per_thread; struct element *th_elements; struct list_aux *aux = aux_; struct timeval start; unsigned int id; size_t i; atomic_add(&aux->thread_id, 1u, &id); n_elems_per_thread = n_elems / n_threads; th_elements = &elements[id * n_elems_per_thread]; ovs_barrier_block(&barrier); xgettimeofday(&start); for (i = 0; i < n_elems_per_thread; i++) { aux->mutex ? ovs_mutex_lock(aux->mutex) : spin_lock(aux->spin); ovs_list_push_front(aux->list, &th_elements[i].node.list); aux->mutex ? ovs_mutex_unlock(aux->mutex) : spin_unlock(aux->spin); } thread_working_ms[id] = elapsed(&start); ovs_barrier_block(&barrier); working = false; return NULL; } static void benchmark_list(bool use_mutex) { struct ovs_mutex mutex; struct spin_lock_type spin; struct ovs_list list; struct element *elem; struct timeval start; unsigned int counter; bool work_complete; pthread_t *threads; struct list_aux aux; uint64_t epoch; size_t i; memset(elements, 0, n_elems * sizeof *elements); memset(thread_working_ms, 0, n_threads * sizeof *thread_working_ms); use_mutex ? ovs_mutex_init(&mutex) : spin_lock_init(&spin); ovs_list_init(&list); aux.list = &list; aux.mutex = use_mutex ? &mutex : NULL; aux.spin = use_mutex ? NULL : &spin; atomic_store(&aux.thread_id, 0); for (i = n_elems - (n_elems % n_threads); i < n_elems; i++) { ovs_list_push_front(&list, &elements[i].node.list); } working = true; threads = xmalloc(n_threads * sizeof *threads); ovs_barrier_init(&barrier, n_threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("locked_list_insert", locked_list_insert_main, &aux); } xgettimeofday(&start); counter = 0; epoch = 1; do { if (use_mutex) { ovs_mutex_lock(&mutex); LIST_FOR_EACH_POP (elem, node.list, &list) { elem->mark = epoch; counter++; } ovs_mutex_unlock(&mutex); } else { struct ovs_list *node = NULL; spin_lock(&spin); if (!ovs_list_is_empty(&list)) { node = ovs_list_pop_front(&list); } spin_unlock(&spin); if (!node) { continue; } elem = CONTAINER_OF(node, struct element, node.list); elem->mark = epoch; counter++; } if (epoch == UINT64_MAX) { epoch = 0; } epoch++; } while (working); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } /* Elements might have been inserted before threads were joined. */ LIST_FOR_EACH_POP (elem, node.list, &list) { elem->mark = epoch; counter++; } if (use_mutex) { print_result(" list(mutex)", elapsed(&start)); } else { print_result(" list(spin)", elapsed(&start)); } use_mutex ? ovs_mutex_destroy(&mutex) : spin_lock_destroy(&spin); ovs_barrier_destroy(&barrier); free(threads); work_complete = true; for (i = 0; i < n_elems; i++) { if (elements[i].mark == 0) { printf("Element %" PRIuSIZE " was never consumed.\n", i); work_complete = false; } } ovs_assert(work_complete); ovs_assert(counter == n_elems); } struct guarded_list_aux { struct guarded_list *glist; atomic_uint thread_id; }; static void * guarded_list_insert_thread(void *aux_) { unsigned int n_elems_per_thread; struct element *th_elements; struct guarded_list_aux *aux = aux_; struct timeval start; unsigned int id; size_t i; atomic_add(&aux->thread_id, 1u, &id); n_elems_per_thread = n_elems / n_threads; th_elements = &elements[id * n_elems_per_thread]; ovs_barrier_block(&barrier); xgettimeofday(&start); for (i = 0; i < n_elems_per_thread; i++) { guarded_list_push_back(aux->glist, &th_elements[i].node.list, n_elems); } thread_working_ms[id] = elapsed(&start); ovs_barrier_block(&barrier); working = false; return NULL; } static void benchmark_guarded_list(void) { struct guarded_list_aux aux; struct ovs_list extracted; struct guarded_list glist; struct element *elem; struct timeval start; unsigned int counter; bool work_complete; pthread_t *threads; uint64_t epoch; size_t i; memset(elements, 0, n_elems * sizeof *elements); memset(thread_working_ms, 0, n_threads * sizeof *thread_working_ms); guarded_list_init(&glist); ovs_list_init(&extracted); aux.glist = &glist; atomic_store(&aux.thread_id, 0); for (i = n_elems - (n_elems % n_threads); i < n_elems; i++) { guarded_list_push_back(&glist, &elements[i].node.list, n_elems); } working = true; threads = xmalloc(n_threads * sizeof *threads); ovs_barrier_init(&barrier, n_threads); for (i = 0; i < n_threads; i++) { threads[i] = ovs_thread_create("guarded_list_insert", guarded_list_insert_thread, &aux); } xgettimeofday(&start); counter = 0; epoch = 1; do { guarded_list_pop_all(&glist, &extracted); LIST_FOR_EACH_POP (elem, node.list, &extracted) { elem->mark = epoch; counter++; } if (epoch == UINT64_MAX) { epoch = 0; } epoch++; } while (working); for (i = 0; i < n_threads; i++) { xpthread_join(threads[i], NULL); } /* Elements might have been inserted before threads were joined. */ guarded_list_pop_all(&glist, &extracted); LIST_FOR_EACH_POP (elem, node.list, &extracted) { elem->mark = epoch; counter++; } print_result("guarded list", elapsed(&start)); ovs_barrier_destroy(&barrier); free(threads); guarded_list_destroy(&glist); work_complete = true; for (i = 0; i < n_elems; i++) { if (elements[i].mark == 0) { printf("Element %" PRIuSIZE " was never consumed.\n", i); work_complete = false; } } ovs_assert(work_complete); ovs_assert(counter == n_elems); } static void run_benchmarks(struct ovs_cmdl_context *ctx) { long int l_threads; long int l_elems; size_t i; ovsrcu_quiesce_start(); l_elems = strtol(ctx->argv[1], NULL, 10); l_threads = strtol(ctx->argv[2], NULL, 10); ovs_assert(l_elems > 0 && l_threads > 0); n_elems = l_elems; n_threads = l_threads; elements = xcalloc(n_elems, sizeof *elements); thread_working_ms = xcalloc(n_threads, sizeof *thread_working_ms); printf("Benchmarking n=%u on 1 + %u threads.\n", n_elems, n_threads); printf(" type\\thread: Reader "); for (i = 0; i < n_threads; i++) { printf(" %3" PRIuSIZE " ", i + 1); } printf(" Avg\n"); benchmark_mpsc_queue(); #ifdef HAVE_PTHREAD_SPIN_LOCK benchmark_list(false); #endif benchmark_list(true); benchmark_guarded_list(); free(thread_working_ms); free(elements); } static const struct ovs_cmdl_command commands[] = { {"check", NULL, 0, 0, run_tests, OVS_RO}, {"benchmark", " ", 2, 2, run_benchmarks, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_mpsc_queue_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - optind, .argv = argv + optind, }; vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_OFF); set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-mpsc-queue", test_mpsc_queue_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-multipath.c000066400000000000000000000076341514270232600232640ustar00rootroot00000000000000/* * Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include #include #include #include #include #include "flow.h" #include "multipath.h" #include "openvswitch/ofp-actions.h" #include "ovstest.h" #include "util.h" static void test_multipath_main(int argc, char *argv[]) { enum { MP_MAX_LINKS = 63 }; struct ofpact_multipath mp; bool ok = true; char *error; int n; set_program_name(argv[0]); if (argc != 2) { ovs_fatal(0, "usage: %s multipath_action", program_name); } error = multipath_parse(&mp, argv[1]); if (error) { ovs_fatal(0, "%s", error); } for (n = 1; n <= MP_MAX_LINKS; n++) { enum { N_FLOWS = 65536 }; double disruption, perfect, distribution; int histogram[MP_MAX_LINKS]; double sum_dev2, stddev; int changed; int i; changed = 0; memset(histogram, 0, sizeof histogram); for (i = 0; i < N_FLOWS; i++) { int old_link, new_link; struct flow_wildcards wc; struct flow flow; flow_random_hash_fields(&flow); flow_wildcards_init_catchall(&wc); mp.max_link = n - 1; multipath_execute(&mp, &flow, &wc); old_link = flow.regs[0]; mp.max_link = n; multipath_execute(&mp, &flow, &wc); new_link = flow.regs[0]; assert(old_link >= 0 && old_link < n); assert(new_link >= 0 && new_link < n + 1); histogram[old_link]++; changed += old_link != new_link; } sum_dev2 = 0.0; for (i = 0; i < n; i++) { double mean = (double) N_FLOWS / n; double deviation = histogram[i] - mean; sum_dev2 += deviation * deviation; } stddev = sqrt(sum_dev2 / n); disruption = (double) changed / N_FLOWS; perfect = 1.0 / (n + 1); distribution = stddev / ((double) N_FLOWS / n); printf("%2d -> %2d: disruption=%.2f (perfect=%.2f); " "stddev/expected=%.4f\n", n, n + 1, disruption, perfect, distribution); switch (mp.algorithm) { case NX_MP_ALG_MODULO_N: if (disruption < (n < 2 ? .25 : .5)) { fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n", n, n + 1, disruption); ok = false; } break; case NX_MP_ALG_HASH_THRESHOLD: if (disruption < .48 || disruption > .52) { fprintf(stderr, "%d -> %d: disruption=%.2f not approximately " ".5\n", n, n + 1, disruption); ok = false; } break; case NX_MP_ALG_ITER_HASH: if (!(n & (n - 1))) { break; } /* Fall through. */ case NX_MP_ALG_HRW: if (fabs(disruption - perfect) >= .01) { fprintf(stderr, "%d -> %d: disruption=%.5f differs from " "perfect=%.5f by more than .01\n", n, n + 1, disruption, perfect); ok = false; } break; default: OVS_NOT_REACHED(); } } exit(ok ? 0 : 1); } OVSTEST_REGISTER("test-multipath", test_multipath_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-netflow.c000066400000000000000000000205271514270232600227270ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2017 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "netflow.h" #include #include #include #include #include #include "command-line.h" #include "daemon.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "packets.h" #include "openvswitch/poll-loop.h" #include "socket-util.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static unixctl_cb_func test_netflow_exit; static void print_netflow(struct ofpbuf *buf) { const struct netflow_v5_header *hdr; int i; hdr = ofpbuf_try_pull(buf, sizeof *hdr); if (!hdr) { printf("truncated NetFlow packet header\n"); return; } printf("header: v%"PRIu16", " "uptime %"PRIu32", " "now %"PRIu32".%09"PRIu32", " "seq %"PRIu32", " "engine %"PRIu8",%"PRIu8, ntohs(hdr->version), ntohl(hdr->sysuptime), ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs), ntohl(hdr->flow_seq), hdr->engine_type, hdr->engine_id); if (hdr->sampling_interval != htons(0)) { printf(", interval %"PRIu16, ntohs(hdr->sampling_interval)); } putchar('\n'); for (i = 0; i < ntohs(hdr->count); i++) { struct netflow_v5_record *rec; rec = ofpbuf_try_pull(buf, sizeof *rec); if (!rec) { printf("truncated NetFlow records\n"); return; } printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq), IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr)); printf(", if %"PRIu16" > %"PRIu16, ntohs(rec->input), ntohs(rec->output)); printf(", %"PRIu32" pkts, %"PRIu32" bytes", ntohl(rec->packet_count), ntohl(rec->byte_count)); switch (rec->ip_proto) { case IPPROTO_TCP: printf(", TCP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); if (rec->tcp_flags) { struct ds s = DS_EMPTY_INITIALIZER; packet_format_tcp_flags(&s, rec->tcp_flags); printf(" %s", ds_cstr(&s)); ds_destroy(&s); } break; case IPPROTO_UDP: printf(", UDP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); break; case IPPROTO_SCTP: printf(", SCTP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); break; case IPPROTO_ICMP: printf(", ICMP %u:%u", ntohs(rec->dst_port) >> 8, ntohs(rec->dst_port) & 0xff); if (rec->src_port != htons(0)) { printf(", src_port=%"PRIu16, ntohs(rec->src_port)); } break; default: printf(", proto %"PRIu8, rec->ip_proto); break; } if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) { printf(", flags %"PRIx8, rec->tcp_flags); } if (rec->ip_proto != IPPROTO_TCP && rec->ip_proto != IPPROTO_UDP && rec->ip_proto != IPPROTO_SCTP && rec->ip_proto != IPPROTO_ICMP) { if (rec->src_port != htons(0)) { printf(", src_port %"PRIu16, ntohs(rec->src_port)); } if (rec->dst_port != htons(0)) { printf(", dst_port %"PRIu16, ntohs(rec->dst_port)); } } if (rec->ip_tos) { printf(", TOS %"PRIx8, rec->ip_tos); } printf(", time %"PRIu32"...%"PRIu32, ntohl(rec->init_time), ntohl(rec->used_time)); if (rec->nexthop != htonl(0)) { printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop)); } if (rec->src_as != htons(0) || rec->dst_as != htons(0)) { printf(", AS %"PRIu16" > %"PRIu16, ntohs(rec->src_as), ntohs(rec->dst_as)); } if (rec->src_mask != 0 || rec->dst_mask != 0) { printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask); } if (rec->pad1) { printf(", pad1 %"PRIu8, rec->pad1); } if (rec->pad[0] || rec->pad[1]) { printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]); } putchar('\n'); } if (buf->size) { printf("%"PRIu32" extra bytes after last record\n", buf->size); } } static void test_netflow_main(int argc, char *argv[]) { struct unixctl_server *server; enum { MAX_RECV = 1500 }; const char *target; struct ofpbuf buf; bool exiting = false; int error; int sock; int n; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); if (argc - optind != 1) { ovs_fatal(0, "exactly one non-option argument required " "(use --help for help)"); } target = argv[optind]; sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0, true); if (sock < 0) { ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock)); } daemon_save_fd(STDOUT_FILENO); daemonize_start(false, false); error = unixctl_server_create(NULL, &server); if (error) { ovs_fatal(error, "failed to create unixctl server"); } unixctl_command_register("exit", "", 0, 0, test_netflow_exit, &exiting); daemonize_complete(); ofpbuf_init(&buf, MAX_RECV); n = 0; for (;;) { int retval; unixctl_server_run(server); ofpbuf_clear(&buf); do { retval = recv(sock, buf.data, buf.allocated, 0); } while (retval < 0 && errno == EINTR); if (retval > 0) { ofpbuf_put_uninit(&buf, retval); if (n++ > 0) { putchar('\n'); } print_netflow(&buf); fflush(stdout); } if (exiting) { break; } poll_fd_wait(sock, POLLIN); unixctl_server_wait(server); poll_block(); } ofpbuf_uninit(&buf); unixctl_server_destroy(server); } static void parse_options(int argc, char *argv[]) { enum { DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void) { printf("%s: netflow collector test utility\n" "usage: %s [OPTIONS] PORT[:IP]\n" "where PORT is the UDP port to listen on and IP is optionally\n" "the IP address to listen on.\n", program_name, program_name); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n"); exit(EXIT_SUCCESS); } static void test_netflow_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } OVSTEST_REGISTER("test-netflow", test_netflow_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-netlink-conntrack.c000066400000000000000000000112741514270232600246740ustar00rootroot00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * 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. */ #include #include #include #include "ct-dpif.h" #include "netlink-conntrack.h" #include "netlink-notifier.h" #include "ovstest.h" #include "openvswitch/poll-loop.h" /* Monitor command */ struct test_change { enum nl_ct_event_type type; struct ct_dpif_entry entry; }; static int event_parse(struct ofpbuf *buf, void *change_) { struct test_change *change = change_; if (nl_ct_parse_entry(buf, &change->entry, &change->type)) { switch (change->type) { case NL_CT_EVENT_NEW: return NFNLGRP_CONNTRACK_NEW; case NL_CT_EVENT_UPDATE: return NFNLGRP_CONNTRACK_UPDATE; case NL_CT_EVENT_DELETE: return NFNLGRP_CONNTRACK_DESTROY; } } return 0; } static void event_print(const void *change_, void *aux OVS_UNUSED) { const struct test_change *change = change_; if (change) { struct ds ds = DS_EMPTY_INITIALIZER; nl_ct_format_event_entry(&change->entry, change->type, &ds, true, true); printf("%s\n", ds_cstr(&ds)); ds_destroy(&ds); } } static void test_nl_ct_monitor(struct ovs_cmdl_context *ctx OVS_UNUSED) { int groups [] = { NFNLGRP_CONNTRACK_DESTROY, NFNLGRP_CONNTRACK_NEW, NFNLGRP_CONNTRACK_UPDATE, }; struct nln *nln; struct nln_notifier *notifiers[ARRAY_SIZE(groups)]; struct test_change change; unsigned i; nln = nln_create(NETLINK_NETFILTER, event_parse, &change); for (i = 0; i < ARRAY_SIZE(groups); i++) { notifiers[i] = nln_notifier_create(nln, groups[i], event_print, NULL); } for (;;) { nln_run(nln); nln_wait(nln); poll_block(); } for (i = 0; i < ARRAY_SIZE(groups); i++) { nln_notifier_destroy(notifiers[i]); } nln_destroy(nln); } /* Dump command */ static void test_nl_ct_dump(struct ovs_cmdl_context *ctx) { struct nl_ct_dump_state *dump; uint16_t zone, *pzone = NULL; struct ct_dpif_entry entry; int err; int tot_bkts; if (ctx->argc >= 2) { if (!ovs_scan(ctx->argv[1], "zone=%"SCNu16, &zone)) { ovs_fatal(0, "Error parsing zone= specifier"); } pzone = &zone; } err = nl_ct_dump_start(&dump, pzone, &tot_bkts); if (err) { ovs_fatal(err, "Error creating conntrack netlink dump"); } do { err = nl_ct_dump_next(dump, &entry); if (!err) { struct ds ds = DS_EMPTY_INITIALIZER; ct_dpif_format_entry(&entry, &ds, true, true); printf("%s\n", ds_cstr(&ds)); ds_destroy(&ds); } } while (!err); if (err != EOF) { ovs_fatal(err, "Error dumping conntrack netlink entry"); } nl_ct_dump_done(dump); } /* Flush command */ static void test_nl_ct_flush(struct ovs_cmdl_context *ctx OVS_UNUSED) { int err; if (ctx->argc >= 2) { uint16_t zone; if (ovs_scan(ctx->argv[1], "zone=%"SCNu16, &zone)) { err = nl_ct_flush_zone(zone); } else { ovs_fatal(0, "Error parsing zone= specifier"); } } else { err = nl_ct_flush(); } if (err) { ovs_fatal(err, "Error flushing conntrack netlink"); } } static const struct ovs_cmdl_command commands[] = { /* Linux netlink connection tracker interface test. */ /* Prints all the entries in the connection table and exits. */ {"dump", "[zone=zone]", 0, 1, test_nl_ct_dump, OVS_RO}, /* Listens to all the connection tracking events and prints them to * standard output until killed. */ {"monitor", "", 0, 0, test_nl_ct_monitor, OVS_RO}, /* Flushes all the entries from all the tables.. */ {"flush", "[zone=zone]", 0, 1, test_nl_ct_flush, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_netlink_conntrack(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-netlink-conntrack", test_netlink_conntrack); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-netlink-policy.c000066400000000000000000000134041514270232600242060ustar00rootroot00000000000000/* * Copyright (c) 2021 Canonical * * 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. */ #include #include #include "netlink.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/types.h" #include "ovstest.h" #include "util.h" struct nlattr_fixture { struct nlattr nlattr; uint8_t data[32]; }; /* nla_len is an inline function in the kernel net/netlink header, which we * don't necessarilly have at build time, so provide our own with * non-conflicting name. */ static int _nla_len(const struct nlattr *nla) { return nla->nla_len - NLA_HDRLEN; } #define TEST_POLICY_ATTR 42 static void test_nl_policy_parse_ll_addr(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct nl_policy policy[] = { [TEST_POLICY_ATTR] = { .type = NL_A_LL_ADDR, .optional = false, }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; struct nlattr_fixture fixture_nl_data_policy_short = { /* too short according to policy */ .nlattr = { .nla_len = 5, .nla_type = TEST_POLICY_ATTR }, .data = { 0x00 }, }; struct nlattr_fixture fixture_nl_data_policy_long = { /* too long according to policy */ .nlattr = { .nla_len = 25, .nla_type = TEST_POLICY_ATTR }, .data = { 0x00, 0x00, 0x67, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x1d, 0x2d, 0x03, 0x00, 0xa5, 0xf0, 0x2f, 0x00, 0x00 }, }; struct nlattr_fixture fixture_nl_data_eth = { /* valid policy and eth_addr length */ .nlattr = { .nla_len = 10, .nla_type = TEST_POLICY_ATTR }, .data = { 0x00, 0x53, 0x00, 0x00, 0x00, 0x2a }, }; struct nlattr_fixture fixture_nl_data_ib = { /* valid policy and ib_addr length */ .nlattr = { .nla_len = 24, .nla_type = TEST_POLICY_ATTR }, .data = { 0x00, 0x00, 0x00, 0x67, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x1d, 0x2d, 0x03, 0x00, 0xa5, 0xf0, 0x2f }, }; struct nlattr_fixture fixture_nl_data_invalid = { /* valid policy but data neither eth_addr nor ib_addr */ .nlattr = { .nla_len = 11, .nla_type = TEST_POLICY_ATTR }, .data = { 0x00, 0x53, 0x00, 0x00, 0x00, 0x2a, 0x00 }, }; struct ofpbuf *buf; /* confirm policy fails with too short data */ buf = ofpbuf_clone_data(&fixture_nl_data_policy_short, fixture_nl_data_policy_short.nlattr.nla_len); ovs_assert(!nl_policy_parse(buf, 0, policy, attrs, ARRAY_SIZE(policy))); ofpbuf_delete(buf); memset(&attrs, 0, sizeof *attrs); /* confirm policy fails with too long data */ buf = ofpbuf_clone_data(&fixture_nl_data_policy_long, fixture_nl_data_policy_long.nlattr.nla_len); ovs_assert(!nl_policy_parse(buf, 0, policy, attrs, ARRAY_SIZE(policy))); ofpbuf_delete(buf); memset(&attrs, 0, sizeof *attrs); /* confirm policy passes and interpret valid ethernet lladdr */ buf = ofpbuf_clone_data(&fixture_nl_data_eth, fixture_nl_data_eth.nlattr.nla_len); ovs_assert(nl_policy_parse(buf, 0, policy, attrs, ARRAY_SIZE(policy))); ovs_assert((_nla_len(attrs[42]) == sizeof(struct eth_addr))); struct eth_addr eth_expect = ETH_ADDR_C(00,53,00,00,00,2a); struct eth_addr eth_parsed = nl_attr_get_eth_addr(attrs[42]); ovs_assert((!memcmp(ð_expect, ð_parsed, sizeof(struct eth_addr)))); ofpbuf_delete(buf); memset(&attrs, 0, sizeof *attrs); /* confirm policy passes and interpret valid infiniband lladdr */ buf = ofpbuf_clone_data(&fixture_nl_data_ib, fixture_nl_data_ib.nlattr.nla_len); ovs_assert(nl_policy_parse(buf, 0, policy, attrs, ARRAY_SIZE(policy))); ovs_assert((_nla_len(attrs[42]) == sizeof(struct ib_addr))); struct ib_addr ib_expect = { .ia = { 0x00, 0x00, 0x00, 0x67, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x1d, 0x2d, 0x03, 0x00, 0xa5, 0xf0, 0x2f, }, }; struct ib_addr ib_parsed = nl_attr_get_ib_addr(attrs[42]); ovs_assert((!memcmp(&ib_expect, &ib_parsed, sizeof(struct eth_addr)))); ofpbuf_delete(buf); memset(&attrs, 0, sizeof *attrs); /* confirm we're able to detect invalid data that passes policy check, this * can happen because the policy defines the data to be between the * currently known lladdr sizes of 6 (ETH_ALEN) and 20 (INFINIBAND_ALEN) */ buf = ofpbuf_clone_data(&fixture_nl_data_invalid, fixture_nl_data_invalid.nlattr.nla_len); ovs_assert(nl_policy_parse(buf, 0, policy, attrs, ARRAY_SIZE(policy))); ovs_assert(_nla_len(attrs[42]) != sizeof(struct eth_addr) && _nla_len(attrs[42]) != sizeof(struct ib_addr)); ofpbuf_delete(buf); memset(&attrs, 0, sizeof *attrs); } static const struct ovs_cmdl_command commands[] = { {"ll_addr", "", 0, 0, test_nl_policy_parse_ll_addr, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_netlink_policy(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; ovs_set_program_name(argv[0], OVS_PACKAGE_VERSION); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-netlink-policy", test_netlink_policy); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-odp.c000066400000000000000000000171641514270232600220360ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2019 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "odp-util.h" #include #include "openvswitch/dynamic-string.h" #include "flow.h" #include "openvswitch/match.h" #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "util.h" #include "openvswitch/ofp-flow.h" #include "openvswitch/vlog.h" static int parse_keys(bool wc_keys) { int exit_code = 0; struct ds in; ds_init(&in); vlog_set_levels_from_string_assert("odp_util:console:dbg"); while (!ds_get_test_line(&in, stdin)) { enum odp_key_fitness fitness; struct ofpbuf odp_key; struct ofpbuf odp_mask; struct flow flow; struct ds out; int error; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); char *error_s; error = odp_flow_from_string(ds_cstr(&in), NULL, &odp_key, &odp_mask, &error_s); if (error) { printf("odp_flow_from_string: error (%s)\n", error_s); free(error_s); goto next; } if (!wc_keys) { struct odp_flow_key_parms odp_parms = { .flow = &flow, .support = { .recirc = true, .ct_state = true, .ct_zone = true, .ct_mark = true, .ct_label = true, .max_vlan_headers = SIZE_MAX, .nd_ext = true, }, }; /* Convert odp_key to flow. */ fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow, &error_s); switch (fitness) { case ODP_FIT_PERFECT: break; case ODP_FIT_TOO_LITTLE: printf("ODP_FIT_TOO_LITTLE: "); break; case ODP_FIT_TOO_MUCH: printf("ODP_FIT_TOO_MUCH: "); break; case ODP_FIT_ERROR: printf("odp_flow_key_to_flow: error (%s)\n", error_s); free(error_s); goto next; } /* Convert cls_rule back to odp_key. */ ofpbuf_uninit(&odp_key); ofpbuf_init(&odp_key, 0); odp_flow_key_from_flow(&odp_parms, &odp_key); if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) { printf ("too long: %"PRIu32" > %d\n", odp_key.size, ODPUTIL_FLOW_KEY_BYTES); exit_code = 1; } } /* Convert odp_key to string. */ ds_init(&out); if (wc_keys) { odp_flow_format(odp_key.data, odp_key.size, odp_mask.data, odp_mask.size, NULL, &out, false, false); } else { odp_flow_key_format(odp_key.data, odp_key.size, &out); } puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); } ds_destroy(&in); return exit_code; } static int parse_actions(void) { struct ds in; ds_init(&in); vlog_set_levels_from_string_assert("odp_util:console:dbg"); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf odp_actions; struct ds out; int error; /* Convert string to OVS DP actions. */ ofpbuf_init(&odp_actions, 0); error = odp_actions_from_string(ds_cstr(&in), NULL, &odp_actions); if (error) { printf("odp_actions_from_string: error\n"); goto next; } /* Convert odp_actions back to string. */ ds_init(&out); format_odp_actions(&out, odp_actions.data, odp_actions.size, NULL); puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_actions); } ds_destroy(&in); return 0; } static int parse_filter(char *filter_parse) { struct ds in; struct flow flow_filter; struct flow_wildcards wc_filter; char *error, *filter = NULL; vlog_set_levels_from_string_assert("odp_util:console:dbg"); if (filter_parse && !strncmp(filter_parse, "filter=", 7)) { filter = xstrdup(filter_parse + 7); memset(&flow_filter, 0, sizeof(flow_filter)); memset(&wc_filter, 0, sizeof(wc_filter)); error = parse_ofp_exact_flow(&flow_filter, &wc_filter, NULL, filter, NULL); if (error) { ovs_fatal(0, "Failed to parse filter (%s)", error); } } else { ovs_fatal(0, "No filter to parse."); } ds_init(&in); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf odp_key; struct ofpbuf odp_mask; struct ds out; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); char *error_s; if (odp_flow_from_string(ds_cstr(&in), NULL, &odp_key, &odp_mask, &error_s)) { printf("odp_flow_from_string: error (%s)\n", error_s); free(error_s); goto next; } if (filter) { struct flow flow; struct flow_wildcards wc; struct match match, match_filter; struct minimatch minimatch; odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow, NULL); odp_flow_key_to_mask(odp_mask.data, odp_mask.size, &wc, &flow, NULL); match_init(&match, &flow, &wc); match_init(&match_filter, &flow_filter, &wc); match_init(&match_filter, &match_filter.flow, &wc_filter); minimatch_init(&minimatch, &match_filter); if (!minimatch_matches_flow(&minimatch, &match.flow)) { minimatch_destroy(&minimatch); goto next; } minimatch_destroy(&minimatch); } /* Convert odp_key to string. */ ds_init(&out); odp_flow_format(odp_key.data, odp_key.size, odp_mask.data, odp_mask.size, NULL, &out, false, false); puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); } ds_destroy(&in); free(filter); return 0; } static void test_odp_main(int argc, char *argv[]) { int exit_code = 0; set_program_name(argv[0]); if (argc == 2 &&!strcmp(argv[1], "parse-keys")) { exit_code =parse_keys(false); } else if (argc == 2 &&!strcmp(argv[1], "parse-wc-keys")) { exit_code =parse_keys(true); } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) { exit_code = parse_actions(); } else if (argc == 3 && !strcmp(argv[1], "parse-filter")) { exit_code =parse_filter(argv[2]); } else { ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions", argv[0]); } exit(exit_code); } OVSTEST_REGISTER("test-odp", test_odp_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-ofparse.py000077500000000000000000000024471514270232600231220ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2022 Red Hat, Inc. # # 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. """test-ofparse reads flows from stdin and tries to parse them using the python flow parsing library. """ import fileinput import sys try: from ovs.flow.ofp import OFPFlow except ImportError: sys.exit(0) def main(): for flow in fileinput.input(): try: result_flow = OFPFlow(flow) if flow != str(result_flow): print("in: {}".format(flow)) print("out: {}".format(str(result_flow))) raise ValueError("Flow conversion back to string failed") except Exception as e: print("Error parsing flow {}: {}".format(flow, e)) return 1 return 0 if __name__ == "__main__": sys.exit(main()) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-ofpbuf.c000066400000000000000000000050701514270232600225260ustar00rootroot00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "util.h" #define BUF_SIZE 100 #define HDR_OFS 10 #define MSG_OFS 50 #define DATA_SIZE 16 static void test_ofpbuf_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ofpbuf *buf = ofpbuf_new(BUF_SIZE); int exit_code = 0; /* Init checks. */ ovs_assert(!buf->size); ovs_assert(buf->allocated == BUF_SIZE); ovs_assert(buf->base == buf->data); /* Sets 'buf->header' and 'buf->msg'. */ buf->header = (char *) buf->base + HDR_OFS; buf->msg = (char *) buf->base + MSG_OFS; /* Gets another 'BUF_SIZE' bytes headroom. */ ofpbuf_prealloc_headroom(buf, BUF_SIZE); ovs_assert(!buf->size); ovs_assert(buf->allocated == 2 * BUF_SIZE); ovs_assert((char *) buf->base + BUF_SIZE == buf->data); /* Now 'buf->header' and 'buf->msg' must be BUF_SIZE away from * their original offsets. */ ovs_assert(buf->header == (char *) buf->base + BUF_SIZE + HDR_OFS); ovs_assert(buf->msg == (char *) buf->base + BUF_SIZE + MSG_OFS); /* Gets another 'BUF_SIZE' bytes tailroom. */ ofpbuf_prealloc_tailroom(buf, BUF_SIZE); /* Must remain unchanged. */ ovs_assert(!buf->size); ovs_assert(buf->allocated == 2 * BUF_SIZE); ovs_assert((char *) buf->base + BUF_SIZE == buf->data); ovs_assert(buf->header == (char *) buf->base + BUF_SIZE + HDR_OFS); ovs_assert(buf->msg == (char *) buf->base + BUF_SIZE + MSG_OFS); size_t prev_size = buf->size; ofpbuf_put_uninit(buf, DATA_SIZE); ofpbuf_truncate(buf, prev_size); /* Check that everything else is unchanged after truncate. */ ovs_assert(!buf->size); ovs_assert((char *) buf->base + BUF_SIZE == buf->data); ovs_assert(buf->header == (char *) buf->base + BUF_SIZE + HDR_OFS); ovs_assert(buf->msg == (char *) buf->base + BUF_SIZE + MSG_OFS); ofpbuf_delete(buf); exit(exit_code); } OVSTEST_REGISTER("test-ofpbuf", test_ofpbuf_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-ovsdb.c000066400000000000000000003554751514270232600224030ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2015, 2016, 2017 Nicira, Inc. * * 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. */ #include #ifdef NDEBUG #define TEST_OVSDB_SKIP_ASSERT 1 #undef NDEBUG #endif #include #include #include #include #include #include #include "byte-order.h" #include "command-line.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/json.h" #include "jsonrpc.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-idl.h" #include "ovsdb-types.h" #include "ovsdb/column.h" #include "ovsdb/condition.h" #include "ovsdb/file.h" #include "ovsdb/log.h" #include "ovsdb/mutation.h" #include "ovsdb/ovsdb.h" #include "ovsdb/query.h" #include "ovsdb/row.h" #include "ovsdb/server.h" #include "ovsdb/storage.h" #include "ovsdb/table.h" #include "ovsdb/transaction.h" #include "ovsdb/trigger.h" #include "openvswitch/poll-loop.h" #include "stream.h" #include "svec.h" #include "tests/idltest.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(test_ovsdb); struct test_ovsdb_pvt_context { bool write_changed_only; bool assert_read_only; bool track; }; /* Magic to pass to ovsdb_log_open(). */ static const char *magic = OVSDB_MAGIC; OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct test_ovsdb_pvt_context *pvt); static struct ovs_cmdl_command *get_all_commands(void); int main(int argc, char *argv[]) { struct test_ovsdb_pvt_context pvt = {.track = false}; struct ovs_cmdl_context ctx = { .argc = 0, .pvt = &pvt}; set_program_name(argv[0]); parse_options(argc, argv, &pvt); ctx.argc = argc - optind; ctx.argv = argv + optind; ovs_cmdl_run_command(&ctx, get_all_commands()); return 0; } static void parse_options(int argc, char *argv[], struct test_ovsdb_pvt_context *pvt) { enum { OPT_MAGIC = CHAR_MAX + 1, OPT_NO_RENAME_OPEN_FILES }; static const struct option long_options[] = { {"timeout", required_argument, NULL, 't'}, {"verbose", optional_argument, NULL, 'v'}, {"change-track", optional_argument, NULL, 'c'}, {"write-changed-only", optional_argument, NULL, 'w'}, {"assert-read-only", optional_argument, NULL, 'r'}, {"magic", required_argument, NULL, OPT_MAGIC}, {"no-rename-open-files", no_argument, NULL, OPT_NO_RENAME_OPEN_FILES}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); unsigned int timeout = 0; for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 't': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ovs_fatal(0, "value %s on -t or --timeout is invalid", optarg); } break; case 'h': usage(); case 'v': vlog_set_verbosity(optarg); break; case 'c': pvt->track = true; break; case 'w': pvt->write_changed_only = true; break; case 'r': #ifdef TEST_OVSDB_SKIP_ASSERT ovs_fatal(0, "Assertion tests are not available due to NDEBUG"); #endif pvt->assert_read_only = true; break; case OPT_MAGIC: magic = optarg; break; case OPT_NO_RENAME_OPEN_FILES: ovsdb_log_disable_renaming_open_files(); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); ctl_timeout_setup(timeout); } static void usage(void) { printf("%s: Open vSwitch database test utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n\n" " [--magic=MAGIC] [--no-rename-open-files] " " log-io FILE FLAGS COMMAND...\n" " open FILE with FLAGS (and MAGIC), run COMMANDs\n" " default-atoms\n" " test ovsdb_atom_default()\n" " default-data\n" " test ovsdb_datum_default()\n" " parse-atomic-type TYPE\n" " parse TYPE as OVSDB atomic type, and re-serialize\n" " parse-base-type TYPE\n" " parse TYPE as OVSDB base type, and re-serialize\n" " parse-type JSON\n" " parse JSON as OVSDB type, and re-serialize\n" " parse-atoms TYPE ATOM...\n" " parse JSON ATOMs as atoms of TYPE, and re-serialize\n" " parse-atom-strings TYPE ATOM...\n" " parse string ATOMs as atoms of given TYPE, and re-serialize\n" " sort-atoms TYPE ATOM...\n" " print JSON ATOMs in sorted order\n" " parse-data TYPE DATUM...\n" " parse JSON DATUMs as data of given TYPE, and re-serialize\n" " parse-data-strings TYPE DATUM...\n" " parse string DATUMs as data of given TYPE, and re-serialize\n" " parse-column NAME OBJECT\n" " parse column NAME with info OBJECT, and re-serialize\n" " parse-table NAME OBJECT [DEFAULT-IS-ROOT]\n" " parse table NAME with info OBJECT\n" " parse-row TABLE ROW..., and re-serialize\n" " parse each ROW of defined TABLE\n" " compare-row TABLE ROW...\n" " mutually compare all of the ROWs, print those that are equal\n" " parse-conditions TABLE CONDITION...\n" " parse each CONDITION on TABLE, and re-serialize\n" " evaluate-conditions TABLE [CONDITION,...] [ROW,...]\n" " test CONDITIONS on TABLE against each ROW, print results\n" " evaluate-conditions-any TABLE [CONDITION,...] [ROW,...]\n" " test CONDITIONS to match any of the CONDITONS on TABLE\n" " against each ROW, print results\n" " compare-conditions TABLE [CONDITION,...]\n" " mutually compare all of the CONDITION, print results for\n" " each pair\n" " parse-mutations TABLE MUTATION...\n" " parse each MUTATION on TABLE, and re-serialize\n" " execute-mutations TABLE [MUTATION,...] [ROW,...]\n" " execute MUTATIONS on TABLE on each ROW, print results\n" " query TABLE [ROW,...] [CONDITION,...]\n" " add each ROW to TABLE, then query and print the rows that\n" " satisfy each CONDITION.\n" " query-distinct TABLE [ROW,...] [CONDITION,...] COLUMNS\n" " add each ROW to TABLE, then query and print the rows that\n" " satisfy each CONDITION and have distinct COLUMNS.\n" " parse-schema JSON\n" " parse JSON as an OVSDB schema, and re-serialize\n" " transact COMMAND\n" " execute each specified transactional COMMAND:\n" " commit\n" " abort\n" " insert UUID I J\n" " delete UUID\n" " modify UUID I J\n" " print\n" " execute SCHEMA TRANSACTION...\n" " executes each TRANSACTION on an initially empty database\n" " the specified SCHEMA\n" " execute-readonly SCHEMA TRANSACTION...\n" " same as execute, except the TRANSACTION will be executed\n" " against the database server that is in read only mode\n" " trigger SCHEMA TRANSACTION...\n" " executes each TRANSACTION on an initially empty database\n" " the specified SCHEMA. A TRANSACTION of the form\n" " [\"advance\", NUMBER] advances NUMBER milliseconds in\n" " simulated time, for causing triggers to time out.\n" " idl SERVER [TRANSACTION...]\n" " connect to SERVER and dump the contents of the database\n" " as seen initially by the IDL implementation and after\n" " executing each TRANSACTION. (Each TRANSACTION must modify\n" " the database or this command will hang.)\n" " idl-partial-update-map-column SERVER \n" " connect to SERVER and executes different operations to\n" " test the capacity of updating elements inside a map column\n" " displaying the table information after each operation.\n" " idl-partial-update-set-column SERVER \n" " connect to SERVER and executes different operations to\n" " test the capacity of updating elements inside a set column\n" " displaying the table information after each operation.\n" " idl-compound-index TEST_TO_EXECUTE\n" " Execute the tests to verify compound-index feature.\n" " The TEST_TO_EXECUTE are:\n" " idl_compound_index_single_column:\n" " test for indexes using one column.\n" " idl_compound_index_double_column:\n" " test for indexes using two columns.\n", program_name, program_name); vlog_usage(); printf("\nOther options:\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" " -c, --change-track used with the 'idl' command to\n" " enable tracking of IDL changes\n"); exit(EXIT_SUCCESS); } /* Command helper functions. */ static struct json * parse_json(const char *s) { struct json *json = json_from_string(s); if (json->type == JSON_STRING) { ovs_fatal(0, "\"%s\": %s", s, json_string(json)); } return json; } static struct json * unbox_json(struct json *json) { if (json->type == JSON_ARRAY && json_array_size(json) == 1) { struct json *inner = json_array_pop(json); json_destroy(json); return inner; } else { return json; } } static void print_and_free_json(struct json *json) { char *string = json_to_string(json, JSSF_SORT); json_destroy(json); puts(string); free(string); } static void print_and_free_ovsdb_error(struct ovsdb_error *error) { char *string = ovsdb_error_to_string_free(error); puts(string); free(string); } static struct json **json_to_destroy; static void destroy_on_ovsdb_error(struct json **json) { json_to_destroy = json; } static void check_ovsdb_error(struct ovsdb_error *error) { if (error) { char *s = ovsdb_error_to_string_free(error); if (json_to_destroy) { json_destroy(*json_to_destroy); json_to_destroy = NULL; } ovs_fatal(0, "%s", s); } } static void die_if_error(char *error) { if (error) { ovs_fatal(0, "%s", error); } } /* Command implementations. */ static void do_log_io(struct ovs_cmdl_context *ctx) { const char *name = ctx->argv[1]; char *mode_string = ctx->argv[2]; struct ovsdb_error *error; enum ovsdb_log_open_mode mode; int i; if (!strcmp(mode_string, "read-only")) { mode = OVSDB_LOG_READ_ONLY; } else if (!strcmp(mode_string, "read/write")) { mode = OVSDB_LOG_READ_WRITE; } else if (!strcmp(mode_string, "create")) { mode = OVSDB_LOG_CREATE; } else if (!strcmp(mode_string, "create-excl")) { mode = OVSDB_LOG_CREATE_EXCL; } else { ovs_fatal(0, "unknown log-io open mode \"%s\"", mode_string); } struct ovsdb_log *log; check_ovsdb_error(ovsdb_log_open(name, magic, mode, -1, &log)); printf("%s: open successful\n", name); struct ovsdb_log *replacement = NULL; for (i = 3; i < ctx->argc; i++) { const char *command = ctx->argv[i]; struct ovsdb_log *target; const char *target_name; if (!strncmp(command, "old-", 4)) { command += 4; target = log; target_name = name; } else if (!strncmp(command, "new-", 4)) { if (!replacement) { ovs_fatal(0, "%s: can't execute command without " "replacement log", command); } command += 4; target = replacement; target_name = "(temp)"; } else { target = log; target_name = name; } if (!strcmp(command, "read")) { struct json *json; error = ovsdb_log_read(target, &json); if (!error) { printf("%s: read: ", target_name); if (json) { print_and_free_json(json); } else { printf("end of log\n"); } continue; } } else if (!strncmp(command, "write:", 6)) { struct json *json = parse_json(command + 6); error = ovsdb_log_write_and_free(target, json); } else if (!strcmp(command, "commit")) { error = ovsdb_log_commit_block(target); } else if (!strcmp(command, "replace_start")) { ovs_assert(!replacement); error = ovsdb_log_replace_start(log, &replacement); } else if (!strcmp(command, "replace_commit")) { ovs_assert(replacement); error = ovsdb_log_replace_commit(log, replacement); replacement = NULL; } else if (!strcmp(command, "replace_abort")) { ovs_assert(replacement); ovsdb_log_replace_abort(replacement); replacement = NULL; error = NULL; } else { ovs_fatal(0, "unknown log-io command \"%s\"", command); } if (error) { char *s = ovsdb_error_to_string_free(error); printf("%s: %s failed: %s\n", target_name, command, s); free(s); } else { printf("%s: %s successful\n", target_name, command); } } ovsdb_log_close(log); ovsdb_log_close(replacement); } static void do_default_atoms(struct ovs_cmdl_context *ctx OVS_UNUSED) { int type; for (type = 0; type < OVSDB_N_TYPES; type++) { union ovsdb_atom atom; if (type == OVSDB_TYPE_VOID) { continue; } printf("%s: ", ovsdb_atomic_type_to_string(type)); ovsdb_atom_init_default(&atom, type); if (!ovsdb_atom_equals(&atom, ovsdb_atom_default(type), type)) { printf("wrong\n"); exit(1); } ovsdb_atom_destroy(&atom, type); printf("OK\n"); } } static void do_default_data(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int n_min; int key, value; for (n_min = 0; n_min <= 1; n_min++) { for (key = 0; key < OVSDB_N_TYPES; key++) { if (key == OVSDB_TYPE_VOID) { continue; } for (value = 0; value < OVSDB_N_TYPES; value++) { struct ovsdb_datum datum; struct ovsdb_type type; ovsdb_base_type_init(&type.key, key); ovsdb_base_type_init(&type.value, value); type.n_min = n_min; type.n_max = 1; ovs_assert(ovsdb_type_is_valid(&type)); printf("key %s, value %s, n_min %u: ", ovsdb_atomic_type_to_string(key), ovsdb_atomic_type_to_string(value), n_min); ovsdb_datum_init_default(&datum, &type); if (!ovsdb_datum_equals(&datum, ovsdb_datum_default(&type), &type)) { printf("wrong\n"); exit(1); } ovsdb_datum_destroy(&datum, &type); ovsdb_type_destroy(&type); printf("OK\n"); } } } } static void do_diff_data(struct ovs_cmdl_context *ctx) { struct ovsdb_type type; struct json *json; struct ovsdb_datum new, old, diff, reincarnation; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_type_from_json(&type, json)); json_destroy(json); /* Arguments in pairs of 'old' and 'new'. */ for (int i = 2; i < ctx->argc - 1; i+=2) { struct ovsdb_error *error; json = unbox_json(parse_json(ctx->argv[i])); check_ovsdb_error(ovsdb_datum_from_json(&old, &type, json, NULL)); json_destroy(json); json = unbox_json(parse_json(ctx->argv[i+1])); check_ovsdb_error(ovsdb_transient_datum_from_json(&new, &type, json)); json_destroy(json); /* Generate the diff. */ ovsdb_datum_diff(&diff, &old, &new, &type); /* Apply diff to 'old' to create'reincarnation'. */ error = ovsdb_datum_apply_diff(&reincarnation, &old, &diff, &type); if (error) { char *string = ovsdb_error_to_string_free(error); ovs_fatal(0, "%s", string); } /* Test to make sure 'new' equals 'reincarnation'. */ if (!ovsdb_datum_equals(&new, &reincarnation, &type)) { ovs_fatal(0, "failed to apply diff"); } /* Apply diff to 'old' in place. */ error = ovsdb_datum_apply_diff_in_place(&old, &diff, &type); if (error) { char *string = ovsdb_error_to_string_free(error); ovs_fatal(0, "%s", string); } /* Test to make sure 'old' equals 'new' now. */ if (!ovsdb_datum_equals(&new, &old, &type)) { ovs_fatal(0, "failed to apply diff in place"); } /* Print diff */ json = ovsdb_datum_to_json(&diff, &type); printf ("diff: "); print_and_free_json(json); /* Print reincarnation */ json = ovsdb_datum_to_json(&reincarnation, &type); printf ("apply diff: "); print_and_free_json(json); /* Print updated 'old' */ json = ovsdb_datum_to_json(&old, &type); printf ("apply diff in place: "); print_and_free_json(json); ovsdb_datum_destroy(&new, &type); ovsdb_datum_destroy(&old, &type); ovsdb_datum_destroy(&diff, &type); ovsdb_datum_destroy(&reincarnation, &type); printf("OK\n"); } ovsdb_type_destroy(&type); } static void do_parse_atomic_type(struct ovs_cmdl_context *ctx) { enum ovsdb_atomic_type type; struct json *json; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json)); json_destroy(json); print_and_free_json(ovsdb_atomic_type_to_json(type)); } static void do_parse_base_type(struct ovs_cmdl_context *ctx) { struct ovsdb_base_type base; struct json *json; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); json_destroy(json); print_and_free_json(ovsdb_base_type_to_json(&base)); ovsdb_base_type_destroy(&base); } static void do_parse_type(struct ovs_cmdl_context *ctx) { struct ovsdb_type type; struct json *json; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_type_from_json(&type, json)); json_destroy(json); print_and_free_json(ovsdb_type_to_json(&type)); ovsdb_type_destroy(&type); } static void do_parse_atoms(struct ovs_cmdl_context *ctx) { struct ovsdb_base_type base; struct json *json; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); json_destroy(json); for (i = 2; i < ctx->argc; i++) { struct ovsdb_error *error; union ovsdb_atom atom; json = unbox_json(parse_json(ctx->argv[i])); error = ovsdb_atom_from_json(&atom, &base, json, NULL); json_destroy(json); if (error) { print_and_free_ovsdb_error(error); } else { print_and_free_json(ovsdb_atom_to_json(&atom, base.type)); ovsdb_atom_destroy(&atom, base.type); } } ovsdb_base_type_destroy(&base); } static void do_parse_atom_strings(struct ovs_cmdl_context *ctx) { struct ovsdb_base_type base; struct json *json; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); json_destroy(json); for (i = 2; i < ctx->argc; i++) { union ovsdb_atom atom, *range_end_atom = NULL; struct ds out; die_if_error(ovsdb_atom_from_string(&atom, &range_end_atom, &base, ctx->argv[i], NULL)); ds_init(&out); ovsdb_atom_to_string(&atom, base.type, &out); if (range_end_atom) { struct ds range_end_ds; ds_init(&range_end_ds); ovsdb_atom_to_string(range_end_atom, base.type, &range_end_ds); ds_put_char(&out, '-'); ds_put_cstr(&out, ds_cstr(&range_end_ds));; ds_destroy(&range_end_ds); } puts(ds_cstr(&out)); ds_destroy(&out); ovsdb_atom_destroy(&atom, base.type); if (range_end_atom) { ovsdb_atom_destroy(range_end_atom, base.type); free(range_end_atom); } } ovsdb_base_type_destroy(&base); } static void do_parse_data__(int argc, char *argv[], struct ovsdb_error * (*parse)(struct ovsdb_datum *datum, const struct ovsdb_type *type, const struct json *json, struct ovsdb_symbol_table *symtab)) { struct ovsdb_type type; struct json *json; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(argv[1])); check_ovsdb_error(ovsdb_type_from_json(&type, json)); json_destroy(json); for (i = 2; i < argc; i++) { struct ovsdb_datum datum; json = unbox_json(parse_json(argv[i])); check_ovsdb_error(parse(&datum, &type, json, NULL)); json_destroy(json); print_and_free_json(ovsdb_datum_to_json(&datum, &type)); ovsdb_datum_destroy(&datum, &type); } ovsdb_type_destroy(&type); } static void do_parse_data(struct ovs_cmdl_context *ctx) { do_parse_data__(ctx->argc, ctx->argv, ovsdb_datum_from_json); } static void do_parse_data_strings(struct ovs_cmdl_context *ctx) { struct ovsdb_type type; struct json *json; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_type_from_json(&type, json)); json_destroy(json); for (i = 2; i < ctx->argc; i++) { struct ovsdb_datum datum; struct ds out; die_if_error(ovsdb_datum_from_string(&datum, &type, ctx->argv[i], NULL)); ds_init(&out); ovsdb_datum_to_string(&datum, &type, &out); puts(ds_cstr(&out)); ds_destroy(&out); ovsdb_datum_destroy(&datum, &type); } ovsdb_type_destroy(&type); } static enum ovsdb_atomic_type compare_atoms_atomic_type; static int compare_atoms(const void *a_, const void *b_) { const union ovsdb_atom *a = a_; const union ovsdb_atom *b = b_; return ovsdb_atom_compare_3way(a, b, compare_atoms_atomic_type); } static void do_sort_atoms(struct ovs_cmdl_context *ctx) { struct ovsdb_base_type base; union ovsdb_atom *atoms; struct json *json, **json_atoms; size_t n_atoms; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); json_destroy(json); json = unbox_json(parse_json(ctx->argv[2])); if (json->type != JSON_ARRAY) { ovs_fatal(0, "second argument must be array"); } /* Convert JSON atoms to internal representation. */ n_atoms = json_array_size(json); atoms = xmalloc(n_atoms * sizeof *atoms); for (i = 0; i < n_atoms; i++) { check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], &base, json_array_at(json, i), NULL)); } json_destroy(json); /* Sort atoms. */ compare_atoms_atomic_type = base.type; qsort(atoms, n_atoms, sizeof *atoms, compare_atoms); /* Convert internal representation back to JSON. */ json_atoms = xmalloc(n_atoms * sizeof *json_atoms); for (i = 0; i < n_atoms; i++) { json_atoms[i] = ovsdb_atom_to_json(&atoms[i], base.type); ovsdb_atom_destroy(&atoms[i], base.type); } print_and_free_json(json_array_create(json_atoms, n_atoms)); free(atoms); ovsdb_base_type_destroy(&base); } static void do_parse_column(struct ovs_cmdl_context *ctx) { struct ovsdb_column *column; struct json *json; destroy_on_ovsdb_error(&json); json = parse_json(ctx->argv[2]); check_ovsdb_error(ovsdb_column_from_json(json, ctx->argv[1], &column)); json_destroy(json); print_and_free_json(ovsdb_column_to_json(column)); ovsdb_column_destroy(column); } static void do_parse_table(struct ovs_cmdl_context *ctx) { struct ovsdb_table_schema *ts; bool default_is_root; struct json *json; default_is_root = ctx->argc > 3 && !strcmp(ctx->argv[3], "true"); destroy_on_ovsdb_error(&json); json = parse_json(ctx->argv[2]); check_ovsdb_error(ovsdb_table_schema_from_json(json, ctx->argv[1], &ts)); json_destroy(json); print_and_free_json(ovsdb_table_schema_to_json(ts, default_is_root)); ovsdb_table_schema_destroy(ts); } static void do_parse_rows(struct ovs_cmdl_context *ctx) { struct ovsdb_column_set all_columns; struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct json *json; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); ovsdb_column_set_init(&all_columns); ovsdb_column_set_add_all(&all_columns, table); for (i = 2; i < ctx->argc; i++) { struct ovsdb_column_set columns; struct ovsdb_row *row; ovsdb_column_set_init(&columns); row = ovsdb_row_create(table); json = unbox_json(parse_json(ctx->argv[i])); check_ovsdb_error(ovsdb_row_from_json(row, json, NULL, &columns, false)); json_destroy(json); print_and_free_json(ovsdb_row_to_json(row, &all_columns)); if (columns.n_columns) { struct svec names; size_t j; char *s; svec_init(&names); for (j = 0; j < columns.n_columns; j++) { svec_add(&names, columns.columns[j]->name); } svec_sort(&names); s = svec_join(&names, ", ", ""); puts(s); free(s); svec_destroy(&names); } else { printf("\n"); } ovsdb_column_set_destroy(&columns); ovsdb_row_destroy(row); } ovsdb_column_set_destroy(&all_columns); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ } static void do_compare_rows(struct ovs_cmdl_context *ctx) { struct ovsdb_column_set all_columns; struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct ovsdb_row **rows; struct json *json; char **names; int n_rows; int i, j; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); ovsdb_column_set_init(&all_columns); ovsdb_column_set_add_all(&all_columns, table); n_rows = ctx->argc - 2; rows = xmalloc(sizeof *rows * n_rows); names = xmalloc(sizeof *names * n_rows); for (i = 0; i < n_rows; i++) { rows[i] = ovsdb_row_create(table); json = parse_json(ctx->argv[i + 2]); if (json->type != JSON_ARRAY || json_array_size(json) != 2 || json_array_at(json, 0)->type != JSON_STRING) { ovs_fatal(0, "\"%s\" does not have expected form " "[\"name\", {data}]", ctx->argv[i]); } names[i] = xstrdup(json_string(json_array_at(json, 0))); check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, 1), NULL, NULL, false)); json_destroy(json); } for (i = 0; i < n_rows; i++) { uint32_t i_hash = ovsdb_row_hash_columns(rows[i], &all_columns, 0); for (j = i + 1; j < n_rows; j++) { uint32_t j_hash = ovsdb_row_hash_columns(rows[j], &all_columns, 0); if (ovsdb_row_equal_columns(rows[i], rows[j], &all_columns)) { printf("%s == %s\n", names[i], names[j]); if (i_hash != j_hash) { printf("but hash(%s) != hash(%s)\n", names[i], names[j]); abort(); } } else if (i_hash == j_hash) { printf("hash(%s) == hash(%s)\n", names[i], names[j]); } } } for (i = 0; i < n_rows; i++) { ovsdb_row_destroy(rows[i]); free(names[i]); } free(rows); free(names); ovsdb_column_set_destroy(&all_columns); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ } static void do_parse_conditions(struct ovs_cmdl_context *ctx) { struct ovsdb_table_schema *ts; struct json *json; int exit_code = 0; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); for (i = 2; i < ctx->argc; i++) { struct ovsdb_condition cnd; struct ovsdb_error *error; json = parse_json(ctx->argv[i]); error = ovsdb_condition_from_json(ts, json, NULL, &cnd); if (!error) { print_and_free_json(ovsdb_condition_to_json(&cnd)); } else { char *s = ovsdb_error_to_string_free(error); ovs_error(0, "%s", s); free(s); exit_code = 1; } json_destroy(json); ovsdb_condition_destroy(&cnd); } ovsdb_table_schema_destroy(ts); exit(exit_code); } #define OVSDB_CONDITION_EVERY 0 #define OVSDB_CONDITION_ANY 1 static void do_evaluate_condition__(struct ovs_cmdl_context *ctx, int mode) { struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct ovsdb_condition *conditions; size_t n_conditions; struct ovsdb_row **rows; size_t n_rows; struct json *json; size_t i, j; destroy_on_ovsdb_error(&json); /* Parse table schema, create table. */ json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); /* Parse conditions. */ json = parse_json(ctx->argv[2]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "CONDITION argument is not JSON array"); } n_conditions = json_array_size(json); conditions = xmalloc(n_conditions * sizeof *conditions); for (i = 0; i < n_conditions; i++) { check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i), NULL, &conditions[i])); } json_destroy(json); /* Parse rows. */ json = parse_json(ctx->argv[3]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "ROW argument is not JSON array"); } n_rows = json_array_size(json); rows = xmalloc(n_rows * sizeof *rows); for (i = 0; i < n_rows; i++) { rows[i] = ovsdb_row_create(table); check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, i), NULL, NULL, false)); } json_destroy(json); for (i = 0; i < n_conditions; i++) { printf("condition %2"PRIuSIZE":", i); for (j = 0; j < n_rows; j++) { bool result; if (mode == OVSDB_CONDITION_EVERY) { result = ovsdb_condition_match_every_clause(rows[j], &conditions[i]); } else { result = ovsdb_condition_match_any_clause(rows[j]->fields, &conditions[i], NULL); } if (j % 5 == 0) { putchar(' '); } putchar(result ? 'T' : '-'); } printf("\n"); } for (i = 0; i < n_conditions; i++) { ovsdb_condition_destroy(&conditions[i]); } free(conditions); for (i = 0; i < n_rows; i++) { ovsdb_row_destroy(rows[i]); } free(rows); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ } static void do_evaluate_conditions(struct ovs_cmdl_context *ctx) { do_evaluate_condition__(ctx, OVSDB_CONDITION_EVERY); } static void do_evaluate_conditions_any(struct ovs_cmdl_context *ctx) { do_evaluate_condition__(ctx, OVSDB_CONDITION_ANY); } static void do_compare_conditions(struct ovs_cmdl_context *ctx) { struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct ovsdb_condition *conditions; size_t n_conditions; struct json *json; size_t i; destroy_on_ovsdb_error(&json); /* Parse table schema, create table. */ json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); /* Parse conditions. */ json = parse_json(ctx->argv[2]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "CONDITION argument is not JSON array"); } n_conditions = json_array_size(json); conditions = xmalloc(n_conditions * sizeof *conditions); for (i = 0; i < n_conditions; i++) { check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i), NULL, &conditions[i])); } json_destroy(json); for (i = 0; i < n_conditions - 1; i++) { int res = ovsdb_condition_cmp_3way(&conditions[i], &conditions[i + 1]); printf("condition %"PRIuSIZE"-%"PRIuSIZE": %d\n", i, i + 1, res); } for (i = 0; i < n_conditions; i++) { ovsdb_condition_destroy(&conditions[i]); } free(conditions); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ } static void do_parse_mutations(struct ovs_cmdl_context *ctx) { struct ovsdb_table_schema *ts; struct json *json; int exit_code = 0; int i; destroy_on_ovsdb_error(&json); json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); for (i = 2; i < ctx->argc; i++) { struct ovsdb_mutation_set set; struct ovsdb_error *error; json = parse_json(ctx->argv[i]); error = ovsdb_mutation_set_from_json(ts, json, NULL, &set); if (!error) { print_and_free_json(ovsdb_mutation_set_to_json(&set)); } else { char *s = ovsdb_error_to_string_free(error); ovs_error(0, "%s", s); free(s); exit_code = 1; } json_destroy(json); ovsdb_mutation_set_destroy(&set); } ovsdb_table_schema_destroy(ts); exit(exit_code); } static void do_execute_mutations(struct ovs_cmdl_context *ctx) { struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct ovsdb_mutation_set *sets; size_t n_sets; struct ovsdb_row **rows; size_t n_rows; struct json *json; size_t i, j; destroy_on_ovsdb_error(&json); /* Parse table schema, create table. */ json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); /* Parse mutations. */ json = parse_json(ctx->argv[2]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "MUTATION argument is not JSON array"); } n_sets = json_array_size(json); sets = xmalloc(n_sets * sizeof *sets); for (i = 0; i < n_sets; i++) { check_ovsdb_error(ovsdb_mutation_set_from_json(ts, json_array_at(json, i), NULL, &sets[i])); } json_destroy(json); /* Parse rows. */ json = parse_json(ctx->argv[3]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "ROW argument is not JSON array"); } n_rows = json_array_size(json); rows = xmalloc(n_rows * sizeof *rows); for (i = 0; i < n_rows; i++) { rows[i] = ovsdb_row_create(table); check_ovsdb_error(ovsdb_row_from_json(rows[i], json_array_at(json, i), NULL, NULL, false)); } json_destroy(json); for (i = 0; i < n_sets; i++) { printf("mutation %2"PRIuSIZE":\n", i); for (j = 0; j < n_rows; j++) { struct ovsdb_error *error; struct ovsdb_row *row; row = ovsdb_row_clone(rows[j]); error = ovsdb_mutation_set_execute(row, &sets[i]); printf("row %"PRIuSIZE": ", j); if (error) { print_and_free_ovsdb_error(error); } else { struct ovsdb_column_set columns; struct shash_node *node; ovsdb_column_set_init(&columns); SHASH_FOR_EACH (node, &ts->columns) { struct ovsdb_column *c = node->data; if (!ovsdb_datum_equals(&row->fields[c->index], &rows[j]->fields[c->index], &c->type)) { ovsdb_column_set_add(&columns, c); } } if (columns.n_columns) { print_and_free_json(ovsdb_row_to_json(row, &columns)); } else { printf("no change\n"); } ovsdb_column_set_destroy(&columns); } ovsdb_row_destroy(row); } printf("\n"); } for (i = 0; i < n_sets; i++) { ovsdb_mutation_set_destroy(&sets[i]); } free(sets); for (i = 0; i < n_rows; i++) { ovsdb_row_destroy(rows[i]); } free(rows); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ } /* Inserts a row and update the indexes, without bothering to update metadata * such as refcounts. */ static void put_row(struct ovsdb_table *table, struct ovsdb_row *row) { const struct uuid *uuid = ovsdb_row_get_uuid(row); if (!ovsdb_table_get_row(table, uuid)) { hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); } for (size_t i = 0; i < table->schema->n_indexes; i++) { const struct ovsdb_column_set *index = &table->schema->indexes[i]; struct hmap_node *node = ovsdb_row_get_index_node(row, i); uint32_t hash = ovsdb_row_hash_columns(row, index, 0); hmap_insert(&table->indexes[i], node, hash); } } struct do_query_cbdata { struct uuid *row_uuids; int *counts; size_t n_rows; }; static bool do_query_cb(const struct ovsdb_row *row, void *cbdata_) { struct do_query_cbdata *cbdata = cbdata_; size_t i; for (i = 0; i < cbdata->n_rows; i++) { if (uuid_equals(ovsdb_row_get_uuid(row), &cbdata->row_uuids[i])) { cbdata->counts[i]++; } } return true; } static void do_query(struct ovs_cmdl_context *ctx) { struct do_query_cbdata cbdata; struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct json *json; int exit_code = 0; size_t i; destroy_on_ovsdb_error(&json); /* Parse table schema, create table. */ json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); /* Parse rows, add to table. */ json = parse_json(ctx->argv[2]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "ROW argument is not JSON array"); } cbdata.n_rows = json_array_size(json); cbdata.row_uuids = xmalloc(cbdata.n_rows * sizeof *cbdata.row_uuids); cbdata.counts = xmalloc(cbdata.n_rows * sizeof *cbdata.counts); for (i = 0; i < cbdata.n_rows; i++) { struct ovsdb_row *row = ovsdb_row_create(table); uuid_generate(ovsdb_row_get_uuid_rw(row)); check_ovsdb_error(ovsdb_row_from_json(row, json_array_at(json, i), NULL, NULL, false)); if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) { ovs_fatal(0, "duplicate UUID "UUID_FMT" in table", UUID_ARGS(ovsdb_row_get_uuid(row))); } cbdata.row_uuids[i] = *ovsdb_row_get_uuid(row); put_row(table, row); } json_destroy(json); /* Parse conditions and execute queries. */ json = parse_json(ctx->argv[3]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "CONDITION argument is not JSON array"); } for (i = 0; i < json_array_size(json); i++) { struct ovsdb_condition cnd; size_t j; check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i), NULL, &cnd)); memset(cbdata.counts, 0, cbdata.n_rows * sizeof *cbdata.counts); ovsdb_query(table, &cnd, do_query_cb, &cbdata, NULL); printf("query %2"PRIuSIZE":", i); for (j = 0; j < cbdata.n_rows; j++) { if (j % 5 == 0) { putchar(' '); } if (cbdata.counts[j]) { printf("%d", cbdata.counts[j]); if (cbdata.counts[j] > 1) { /* Dup! */ exit_code = 1; } } else { putchar('-'); } } putchar('\n'); ovsdb_condition_destroy(&cnd); } json_destroy(json); ovsdb_table_destroy(table); /* Also destroys 'ts'. */ exit(exit_code); } struct do_query_distinct_class { struct ovsdb_row *example; int count; }; struct do_query_distinct_row { struct uuid uuid; struct do_query_distinct_class *class; }; static void do_query_distinct(struct ovs_cmdl_context *ctx) { struct ovsdb_column_set columns; struct ovsdb_table_schema *ts; struct ovsdb_table *table; struct do_query_distinct_row *rows; size_t n_rows; struct do_query_distinct_class *classes; size_t n_classes; struct json *json; int exit_code = 0; size_t i; destroy_on_ovsdb_error(&json); /* Parse table schema, create table. */ json = unbox_json(parse_json(ctx->argv[1])); check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); json_destroy(json); table = ovsdb_table_create(ts); /* Parse column set. */ json = parse_json(ctx->argv[4]); check_ovsdb_error(ovsdb_column_set_from_json(json, table->schema, &columns)); json_destroy(json); /* Parse rows, add to table. */ json = parse_json(ctx->argv[2]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "ROW argument is not JSON array"); } n_rows = json_array_size(json); rows = xmalloc(n_rows * sizeof *rows); classes = xmalloc(n_rows * sizeof *classes); n_classes = 0; for (i = 0; i < n_rows; i++) { struct ovsdb_row *row; size_t j; /* Parse row. */ row = ovsdb_row_create(table); uuid_generate(ovsdb_row_get_uuid_rw(row)); check_ovsdb_error(ovsdb_row_from_json(row, json_array_at(json, i), NULL, NULL, false)); /* Initialize row and find equivalence class. */ rows[i].uuid = *ovsdb_row_get_uuid(row); rows[i].class = NULL; for (j = 0; j < n_classes; j++) { if (ovsdb_row_equal_columns(row, classes[j].example, &columns)) { rows[i].class = &classes[j]; break; } } if (!rows[i].class) { rows[i].class = &classes[n_classes]; classes[n_classes].example = ovsdb_row_clone(row); n_classes++; } /* Add row to table. */ if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) { ovs_fatal(0, "duplicate UUID "UUID_FMT" in table", UUID_ARGS(ovsdb_row_get_uuid(row))); } put_row(table, row); } json_destroy(json); /* Parse conditions and execute queries. */ json = parse_json(ctx->argv[3]); if (json->type != JSON_ARRAY) { ovs_fatal(0, "CONDITION argument is not JSON array"); } for (i = 0; i < json_array_size(json); i++) { struct ovsdb_row_set results; struct ovsdb_condition cnd; size_t j; check_ovsdb_error(ovsdb_condition_from_json(ts, json_array_at(json, i), NULL, &cnd)); for (j = 0; j < n_classes; j++) { classes[j].count = 0; } ovsdb_row_set_init(&results); ovsdb_query_distinct(table, &cnd, &columns, &results, NULL); for (j = 0; j < results.n_rows; j++) { size_t k; for (k = 0; k < n_rows; k++) { if (uuid_equals(ovsdb_row_get_uuid(results.rows[j]), &rows[k].uuid)) { rows[k].class->count++; } } } ovsdb_row_set_destroy(&results); printf("query %2"PRIuSIZE":", i); for (j = 0; j < n_rows; j++) { int count = rows[j].class->count; if (j % 5 == 0) { putchar(' '); } if (count > 1) { /* Dup! */ printf("%d", count); exit_code = 1; } else if (count == 1) { putchar("abcdefghijklmnopqrstuvwxyz"[rows[j].class - classes]); } else { putchar('-'); } } putchar('\n'); ovsdb_condition_destroy(&cnd); } json_destroy(json); for (i = 0; i < n_classes; i++) { ovsdb_row_destroy(classes[i].example); } ovsdb_table_destroy(table); /* Also destroys 'ts'. */ free(rows); free(classes); exit(exit_code); } static void do_parse_schema(struct ovs_cmdl_context *ctx) { struct ovsdb_schema *schema; struct json *json; destroy_on_ovsdb_error(&json); json = parse_json(ctx->argv[1]); check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); json_destroy(json); print_and_free_json(ovsdb_schema_to_json(schema)); ovsdb_schema_destroy(schema); } static void do_execute__(struct ovs_cmdl_context *ctx, bool ro) { struct ovsdb_schema *schema; struct json *json; struct ovsdb *db; int i; destroy_on_ovsdb_error(&json); /* Create database. */ json = parse_json(ctx->argv[1]); check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); json_destroy(json); db = ovsdb_create(schema, ovsdb_storage_create_unbacked(NULL)); for (i = 2; i < ctx->argc; i++) { struct json *params, *result; char *s; params = parse_json(ctx->argv[i]); result = ovsdb_execute(db, NULL, params, ro, NULL, NULL, 0, NULL); s = json_to_string(result, JSSF_SORT); printf("%s\n", s); free(s); json_destroy(params); json_destroy(result); } ovsdb_destroy(db); } static void do_execute_ro(struct ovs_cmdl_context *ctx) { do_execute__(ctx, true); } static void do_execute(struct ovs_cmdl_context *ctx) { do_execute__(ctx, false); } struct test_trigger { struct ovsdb_trigger trigger; int number; }; static void do_trigger_dump(struct test_trigger *t, long long int now, const char *title) { struct jsonrpc_msg *reply; char *s; reply = ovsdb_trigger_steal_reply(&t->trigger); s = json_to_string(reply->result, JSSF_SORT); printf("t=%lld: trigger %d (%s): %s\n", now, t->number, title, s); free(s); jsonrpc_msg_destroy(reply); ovsdb_trigger_destroy(&t->trigger); free(t); } static void do_trigger(struct ovs_cmdl_context *ctx) { struct ovsdb_schema *schema; struct ovsdb_session session; struct ovsdb_server server; struct json *json; struct ovsdb *db; long long int now; int number; int i; destroy_on_ovsdb_error(&json); /* Create database. */ json = parse_json(ctx->argv[1]); check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); json_destroy(json); db = ovsdb_create(schema, ovsdb_storage_create_unbacked(NULL)); ovsdb_server_init(&server); ovsdb_server_add_db(&server, db); ovsdb_session_init(&session, &server); now = 0; number = 0; for (i = 2; i < ctx->argc; i++) { struct json *params = parse_json(ctx->argv[i]); if (params->type == JSON_ARRAY && json_array_size(params) == 2 && json_array_at(params, 0)->type == JSON_STRING && !strcmp(json_string(json_array_at(params, 0)), "advance") && json_array_at(params, 1)->type == JSON_INTEGER) { now += json_integer(json_array_at(params, 1)); json_destroy(params); } else { struct test_trigger *t = xmalloc(sizeof *t); ovsdb_trigger_init(&session, db, &t->trigger, jsonrpc_create_request("transact", params, NULL), now, false, NULL, NULL); t->number = number++; if (ovsdb_trigger_is_complete(&t->trigger)) { do_trigger_dump(t, now, "immediate"); } else { printf("t=%lld: new trigger %d\n", now, t->number); } } ovsdb_trigger_run(db, now); struct test_trigger *t; LIST_FOR_EACH_POP (t, trigger.node, &session.completions) { do_trigger_dump(t, now, "delayed"); ovsdb_trigger_run(db, now); } ovsdb_trigger_wait(db, now); poll_immediate_wake(); poll_block(); } ovsdb_server_destroy(&server); ovsdb_destroy(db); } static void do_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } /* "transact" command. */ static struct ovsdb *do_transact_db; static struct ovsdb_txn *do_transact_txn; static struct ovsdb_table *do_transact_table; static void do_transact_commit(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovsdb_error_destroy(ovsdb_txn_replay_commit(do_transact_txn)); do_transact_txn = NULL; } static void do_transact_abort(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovsdb_txn_abort(do_transact_txn); do_transact_txn = NULL; } static void uuid_from_integer(int integer, struct uuid *uuid) { uuid_zero(uuid); uuid->parts[3] = integer; } static const struct ovsdb_row * do_transact_find_row(const char *uuid_string) { const struct ovsdb_row *row; struct uuid uuid; uuid_from_integer(atoi(uuid_string), &uuid); row = ovsdb_table_get_row(do_transact_table, &uuid); if (!row) { ovs_fatal(0, "table does not contain row with UUID "UUID_FMT, UUID_ARGS(&uuid)); } return row; } static void do_transact_set_integer(struct ovsdb_row *row, const char *column_name, int integer) { if (integer != -1) { const struct ovsdb_column *column; column = ovsdb_table_schema_get_column(do_transact_table->schema, column_name); ovsdb_datum_unshare(&row->fields[column->index], &column->type); row->fields[column->index].keys[0].integer = integer; } } static int do_transact_get_integer(const struct ovsdb_row *row, const char *column_name) { const struct ovsdb_column *column; column = ovsdb_table_schema_get_column(do_transact_table->schema, column_name); return row->fields[column->index].keys[0].integer; } static void do_transact_set_i_j(struct ovsdb_row *row, const char *i_string, const char *j_string) { do_transact_set_integer(row, "i", atoi(i_string)); do_transact_set_integer(row, "j", atoi(j_string)); } static void do_transact_insert(struct ovs_cmdl_context *ctx) { struct ovsdb_row *row; struct uuid *uuid; row = ovsdb_row_create(do_transact_table); /* Set UUID. */ uuid = ovsdb_row_get_uuid_rw(row); uuid_from_integer(atoi(ctx->argv[1]), uuid); if (ovsdb_table_get_row(do_transact_table, uuid)) { ovs_fatal(0, "table already contains row with UUID "UUID_FMT, UUID_ARGS(uuid)); } do_transact_set_i_j(row, ctx->argv[2], ctx->argv[3]); /* Insert row. */ ovsdb_txn_row_insert(do_transact_txn, row); } static void do_transact_delete(struct ovs_cmdl_context *ctx) { const struct ovsdb_row *row = do_transact_find_row(ctx->argv[1]); ovsdb_txn_row_delete(do_transact_txn, row); } static void do_transact_modify(struct ovs_cmdl_context *ctx) { const struct ovsdb_row *row_ro; struct ovsdb_row *row_rw; row_ro = do_transact_find_row(ctx->argv[1]); ovsdb_txn_row_modify(do_transact_txn, row_ro, &row_rw, NULL); do_transact_set_i_j(row_rw, ctx->argv[2], ctx->argv[3]); } static int compare_rows_by_uuid(const void *a_, const void *b_) { struct ovsdb_row *const *ap = a_; struct ovsdb_row *const *bp = b_; return uuid_compare_3way(ovsdb_row_get_uuid(*ap), ovsdb_row_get_uuid(*bp)); } static void do_transact_print(struct ovs_cmdl_context *ctx OVS_UNUSED) { const struct ovsdb_row **rows; const struct ovsdb_row *row; size_t n_rows; size_t i; n_rows = hmap_count(&do_transact_table->rows); rows = xmalloc(n_rows * sizeof *rows); i = 0; HMAP_FOR_EACH (row, hmap_node, &do_transact_table->rows) { rows[i++] = row; } ovs_assert(i == n_rows); qsort(rows, n_rows, sizeof *rows, compare_rows_by_uuid); for (i = 0; i < n_rows; i++) { printf("\n%"PRId32": i=%d, j=%d", ovsdb_row_get_uuid(rows[i])->parts[3], do_transact_get_integer(rows[i], "i"), do_transact_get_integer(rows[i], "j")); } free(rows); } static void do_transact(struct ovs_cmdl_context *ctx) { static const struct ovs_cmdl_command do_transact_commands[] = { { "commit", NULL, 0, 0, do_transact_commit, OVS_RO }, { "abort", NULL, 0, 0, do_transact_abort, OVS_RO }, { "insert", NULL, 2, 3, do_transact_insert, OVS_RO }, { "delete", NULL, 1, 1, do_transact_delete, OVS_RO }, { "modify", NULL, 2, 3, do_transact_modify, OVS_RO }, { "print", NULL, 0, 0, do_transact_print, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; struct ovsdb_schema *schema; struct json *json; int i; destroy_on_ovsdb_error(&json); /* Create table. */ json = parse_json("{\"name\": \"testdb\", " " \"tables\": " " {\"mytable\": " " {\"columns\": " " {\"i\": {\"type\": \"integer\"}, " " \"j\": {\"type\": \"integer\"}}}}}"); check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); json_destroy(json); do_transact_db = ovsdb_create(schema, ovsdb_storage_create_unbacked(NULL)); do_transact_table = ovsdb_get_table(do_transact_db, "mytable"); ovs_assert(do_transact_table != NULL); for (i = 1; i < ctx->argc; i++) { struct json *command; size_t n_args; char **args; int j; struct ovs_cmdl_context transact_ctx = { .argc = 0, }; command = parse_json(ctx->argv[i]); if (command->type != JSON_ARRAY) { ovs_fatal(0, "transaction %d must be JSON array " "with at least 1 element", i); } n_args = json_array_size(command); args = xmalloc((n_args + 1) * sizeof *args); for (j = 0; j < n_args; j++) { const struct json *s = json_array_at(command, j); if (s->type != JSON_STRING) { ovs_fatal(0, "transaction %d argument %d must be JSON string", i, j); } args[j] = xstrdup(json_string(s)); } args[n_args] = NULL; if (!do_transact_txn) { do_transact_txn = ovsdb_txn_create(do_transact_db); } for (j = 0; j < n_args; j++) { if (j) { putchar(' '); } fputs(args[j], stdout); } fputs(":", stdout); transact_ctx.argc = n_args; transact_ctx.argv = args; ovs_cmdl_run_command(&transact_ctx, do_transact_commands); putchar('\n'); for (j = 0; j < n_args; j++) { free(args[j]); } free(args); json_destroy(command); } ovsdb_txn_abort(do_transact_txn); ovsdb_destroy(do_transact_db); /* Also destroys 'schema'. */ } static int compare_link1(const void *a_, const void *b_) { const struct idltest_link1 *const *ap = a_; const struct idltest_link1 *const *bp = b_; const struct idltest_link1 *a = *ap; const struct idltest_link1 *b = *bp; return a->i < b->i ? -1 : a->i > b->i; } static void OVS_PRINTF_FORMAT(1, 2) print_and_log(const char *format, ...) { va_list args; va_start(args, format); char *message = xvasprintf(format, args); va_end(args); printf("%s\n", message); VLOG_INFO("%s", message); free(message); } static char * format_idl_row(const struct ovsdb_idl_row *row, int step, const char *contents, bool terse) { const char *change_str = !ovsdb_idl_track_is_set(row->table) ? "" : ovsdb_idl_row_get_seqno(row, OVSDB_IDL_CHANGE_INSERT) > 0 && ovsdb_idl_row_get_seqno(row, OVSDB_IDL_CHANGE_DELETE) > 0 ? "inserted/deleted row: " : ovsdb_idl_row_get_seqno(row, OVSDB_IDL_CHANGE_INSERT) > 0 ? "inserted row: " : ovsdb_idl_row_get_seqno(row, OVSDB_IDL_CHANGE_DELETE) > 0 ? "deleted row: " : ""; if (terse) { return xasprintf("%03d: table %s", step, row->table->class_->name); } else { return xasprintf("%03d: table %s: %s%s uuid=" UUID_FMT, step, row->table->class_->name, change_str, contents, UUID_ARGS(&row->uuid)); } } static void print_idl_row_updated_simple(const struct idltest_simple *s, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_SIMPLE_N_COLUMNS; i++) { if (idltest_simple_is_updated(s, i)) { ds_put_format(&updates, " %s", idltest_simple_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, s->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_link1(const struct idltest_link1 *l1, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_LINK1_N_COLUMNS; i++) { if (idltest_link1_is_updated(l1, i)) { ds_put_format(&updates, " %s", idltest_link1_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, l1->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_link2(const struct idltest_link2 *l2, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_LINK2_N_COLUMNS; i++) { if (idltest_link2_is_updated(l2, i)) { ds_put_format(&updates, " %s", idltest_link2_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, l2->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_indexed(const struct idltest_indexed *ind, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_INDEXED_N_COLUMNS; i++) { if (idltest_indexed_is_updated(ind, i)) { ds_put_format(&updates, " %s", idltest_indexed_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, ind->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_simple3(const struct idltest_simple3 *s3, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_SIMPLE3_N_COLUMNS; i++) { if (idltest_simple3_is_updated(s3, i)) { ds_put_format(&updates, " %s", idltest_simple3_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, s3->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_simple4(const struct idltest_simple4 *s4, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_SIMPLE4_N_COLUMNS; i++) { if (idltest_simple4_is_updated(s4, i)) { ds_put_format(&updates, " %s", idltest_simple4_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, s4->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_simple6(const struct idltest_simple6 *s6, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_SIMPLE6_N_COLUMNS; i++) { if (idltest_simple6_is_updated(s6, i)) { ds_put_format(&updates, " %s", idltest_simple6_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, s6->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_updated_singleton(const struct idltest_singleton *sng, int step) { struct ds updates = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < IDLTEST_SINGLETON_N_COLUMNS; i++) { if (idltest_singleton_is_updated(sng, i)) { ds_put_format(&updates, " %s", idltest_singleton_columns[i].name); } } if (updates.length) { print_and_log("%03d: table %s: updated columns:%s", step, sng->header_.table->class_->name, ds_cstr(&updates)); ds_destroy(&updates); } } static void print_idl_row_simple(const struct idltest_simple *s, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "i=%"PRId64" r=%g b=%s s=%s u="UUID_FMT" ia=[", s->i, s->r, s->b ? "true" : "false", s->s, UUID_ARGS(&s->u)); for (size_t i = 0; i < s->n_ia; i++) { ds_put_format(&msg, "%s%"PRId64, i ? " " : "", s->ia[i]); } ds_put_cstr(&msg, "] ra=["); for (size_t i = 0; i < s->n_ra; i++) { ds_put_format(&msg, "%s%g", i ? " " : "", s->ra[i]); } ds_put_cstr(&msg, "] ba=["); for (size_t i = 0; i < s->n_ba; i++) { ds_put_format(&msg, "%s%s", i ? " " : "", s->ba[i] ? "true" : "false"); } ds_put_cstr(&msg, "] sa=["); for (size_t i = 0; i < s->n_sa; i++) { ds_put_format(&msg, "%s%s", i ? " " : "", s->sa[i]); } ds_put_cstr(&msg, "] ua=["); for (size_t i = 0; i < s->n_ua; i++) { ds_put_format(&msg, "%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s->ua[i])); } ds_put_cstr(&msg, "]"); char *row_msg = format_idl_row(&s->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_simple(s, step); } static void print_idl_row_link1(const struct idltest_link1 *l1, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "i=%"PRId64" k=", l1->i); if (l1->k) { ds_put_format(&msg, "%"PRId64, l1->k->i); } ds_put_cstr(&msg, " ka=["); struct idltest_link1 **links = xmemdup(l1->ka, l1->n_ka * sizeof *l1->ka); qsort(links, l1->n_ka, sizeof *links, compare_link1); for (size_t i = 0; i < l1->n_ka; i++) { ds_put_format(&msg, "%s%"PRId64, i ? " " : "", links[i]->i); } free(links); ds_put_cstr(&msg, "] l2="); if (l1->l2) { ds_put_format(&msg, "%"PRId64, l1->l2->i); } char *row_msg = format_idl_row(&l1->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_link1(l1, step); } static void print_idl_row_link2(const struct idltest_link2 *l2, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "i=%"PRId64" l1=", l2->i); if (l2->l1) { ds_put_format(&msg, "%"PRId64, l2->l1->i); } char *row_msg = format_idl_row(&l2->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_link2(l2, step); } static void print_idl_row_indexed(const struct idltest_indexed *ind, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "i=%"PRId64, ind->i); char *row_msg = format_idl_row(&ind->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_indexed(ind, step); } static void print_idl_row_simple3(const struct idltest_simple3 *s3, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; size_t i; ds_put_format(&msg, "name=%s uset=[", s3->name); for (i = 0; i < s3->n_uset; i++) { ds_put_format(&msg, UUID_FMT"%s", UUID_ARGS(&s3->uset[i]), i < s3->n_uset - 1 ? "," : ""); } ds_put_cstr(&msg, "] uref=["); for (i = 0; i < s3->n_uref; i++) { ds_put_format(&msg, UUID_FMT"%s", UUID_ARGS(&s3->uref[i]->header_.uuid), i < s3->n_uref -1 ? "," : ""); } ds_put_cstr(&msg, "]"); char *row_msg = format_idl_row(&s3->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_simple3(s3, step); } static void print_idl_row_simple4(const struct idltest_simple4 *s4, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "name=%s", s4->name); char *row_msg = format_idl_row(&s4->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_simple4(s4, step); } static void print_idl_row_simple6(const struct idltest_simple6 *s6, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "name=%s ", s6->name); ds_put_cstr(&msg, "weak_ref=["); for (size_t i = 0; i < s6->n_weak_ref; i++) { ds_put_format(&msg, "%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s6->weak_ref[i]->header_.uuid)); } ds_put_cstr(&msg, "]"); char *row_msg = format_idl_row(&s6->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_simple6(s6, step); } static void print_idl_row_singleton(const struct idltest_singleton *sng, int step, bool terse) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "name=%s", sng->name); char *row_msg = format_idl_row(&sng->header_, step, ds_cstr(&msg), terse); print_and_log("%s", row_msg); ds_destroy(&msg); free(row_msg); print_idl_row_updated_singleton(sng, step); } static void print_idl(struct ovsdb_idl *idl, int step, bool terse) { const struct idltest_indexed *ind; const struct idltest_simple3 *s3; const struct idltest_simple4 *s4; const struct idltest_simple6 *s6; const struct idltest_simple *s; const struct idltest_link1 *l1; const struct idltest_link2 *l2; const struct idltest_singleton *sng; int n = 0; IDLTEST_SIMPLE_FOR_EACH (s, idl) { print_idl_row_simple(s, step, terse); n++; } IDLTEST_LINK1_FOR_EACH (l1, idl) { print_idl_row_link1(l1, step, terse); n++; } IDLTEST_LINK2_FOR_EACH (l2, idl) { print_idl_row_link2(l2, step, terse); n++; } IDLTEST_SIMPLE3_FOR_EACH (s3, idl) { print_idl_row_simple3(s3, step, terse); n++; } IDLTEST_SIMPLE4_FOR_EACH (s4, idl) { print_idl_row_simple4(s4, step, terse); n++; } IDLTEST_SIMPLE6_FOR_EACH (s6, idl) { print_idl_row_simple6(s6, step, terse); n++; } IDLTEST_INDEXED_FOR_EACH (ind, idl) { print_idl_row_indexed(ind, step, terse); n++; } IDLTEST_SINGLETON_FOR_EACH (sng, idl) { print_idl_row_singleton(sng, step, terse); n++; } if (!n) { print_and_log("%03d: empty", step); } } static void print_idl_track(struct ovsdb_idl *idl, int step, bool terse) { const struct idltest_indexed *ind; const struct idltest_simple3 *s3; const struct idltest_simple4 *s4; const struct idltest_simple6 *s6; const struct idltest_simple *s; const struct idltest_link1 *l1; const struct idltest_link2 *l2; int n = 0; IDLTEST_SIMPLE_FOR_EACH_TRACKED (s, idl) { print_idl_row_simple(s, step, terse); n++; } IDLTEST_LINK1_FOR_EACH_TRACKED (l1, idl) { print_idl_row_link1(l1, step, terse); n++; } IDLTEST_LINK2_FOR_EACH_TRACKED (l2, idl) { print_idl_row_link2(l2, step, terse); n++; } IDLTEST_SIMPLE3_FOR_EACH_TRACKED (s3, idl) { print_idl_row_simple3(s3, step, terse); n++; } IDLTEST_SIMPLE4_FOR_EACH_TRACKED (s4, idl) { print_idl_row_simple4(s4, step, terse); n++; } IDLTEST_SIMPLE6_FOR_EACH_TRACKED (s6, idl) { print_idl_row_simple6(s6, step, terse); n++; } IDLTEST_INDEXED_FOR_EACH (ind, idl) { print_idl_row_indexed(ind, step, terse); n++; } if (!n) { print_and_log("%03d: empty", step); } } static void parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab, size_t *n) { struct uuid uuid; if (json->type == JSON_STRING && uuid_from_string(&uuid, json_string(json))) { char *name = xasprintf("#%"PRIuSIZE"#", *n); fprintf(stderr, "%s = "UUID_FMT"\n", name, UUID_ARGS(&uuid)); ovsdb_symbol_table_put(symtab, name, &uuid, false); free(name); *n += 1; } else if (json->type == JSON_ARRAY) { size_t i; for (i = 0; i < json_array_size(json); i++) { parse_uuids(json_array_at(json, i), symtab, n); } } else if (json->type == JSON_OBJECT) { const struct shash_node *node; SHASH_FOR_EACH (node, json_object(json)) { parse_uuids(node->data, symtab, n); } } } static void substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab) { if (json->type == JSON_STRING) { const struct ovsdb_symbol *symbol; symbol = ovsdb_symbol_table_get(symtab, json_string(json)); if (symbol) { if (json->storage_type == JSON_STRING_DYNAMIC) { free(json->str_ptr); } json->storage_type = JSON_STRING_DYNAMIC; json->str_ptr = xasprintf(UUID_FMT, UUID_ARGS(&symbol->uuid)); } } else if (json->type == JSON_ARRAY) { size_t i, n = json_array_size(json); for (i = 0; i < n; i++) { substitute_uuids(CONST_CAST(struct json *, json_array_at(json, i)), symtab); } } else if (json->type == JSON_OBJECT) { const struct shash_node *node; SHASH_FOR_EACH (node, json_object(json)) { substitute_uuids(node->data, symtab); } } } static const struct idltest_simple * idltest_find_simple(struct ovsdb_idl *idl, int i) { const struct idltest_simple *s; IDLTEST_SIMPLE_FOR_EACH (s, idl) { if (s->i == i) { return s; } } return NULL; } static bool idl_set(struct ovsdb_idl *idl, char *commands, int step, bool assert_read_only) { char *cmd, *save_ptr1 = NULL; struct ovsdb_idl_txn *txn; enum ovsdb_idl_txn_status status; bool increment = false; /* FIXME: ovsdb_idl_check_consistency() doesn't currently handle refs added * in the same txn, so if any op does this, we need to skip the check until * that is fixed. */ bool skip_pre_commit_consistency_check = false; txn = ovsdb_idl_txn_create(idl); ovsdb_idl_check_consistency(idl); ovsdb_idl_txn_assert_read_only(txn, assert_read_only); for (cmd = strtok_r(commands, ",", &save_ptr1); cmd; cmd = strtok_r(NULL, ",", &save_ptr1)) { char *save_ptr2 = NULL; char *name, *arg1, *arg2, *arg3; name = strtok_r(cmd, " ", &save_ptr2); arg1 = strtok_r(NULL, " ", &save_ptr2); arg2 = strtok_r(NULL, " ", &save_ptr2); arg3 = strtok_r(NULL, " ", &save_ptr2); if (!strcmp(name, "set")) { const struct idltest_simple *s; if (!arg3) { ovs_fatal(0, "\"set\" command requires 3 arguments"); } s = idltest_find_simple(idl, atoi(arg1)); if (!s) { ovs_fatal(0, "\"set\" command asks for nonexistent " "i=%d", atoi(arg1)); } if (!strcmp(arg2, "b")) { idltest_simple_set_b(s, atoi(arg3)); } else if (!strcmp(arg2, "s")) { idltest_simple_set_s(s, arg3); } else if (!strcmp(arg2, "u")) { struct uuid uuid; if (!uuid_from_string(&uuid, arg3)) { ovs_fatal(0, "\"%s\" is not a valid UUID", arg3); } idltest_simple_set_u(s, uuid); } else if (!strcmp(arg2, "r")) { idltest_simple_set_r(s, atof(arg3)); } else { ovs_fatal(0, "\"set\" command asks for unknown column %s", arg2); } } else if (!strcmp(name, "insert")) { struct idltest_simple *s; if (!arg1 || arg2) { ovs_fatal(0, "\"insert\" command requires 1 argument"); } s = idltest_simple_insert(txn); idltest_simple_set_i(s, atoi(arg1)); } else if (!strcmp(name, "insert_no_columns_changed")) { idltest_simple_insert(txn); } else if (!strcmp(name, "insert_uuid")) { struct idltest_simple *s; if (!arg1 || !arg2) { ovs_fatal(0, "\"insert\" command requires 2 arguments"); } struct uuid s_uuid; if (!uuid_from_string(&s_uuid, arg1)) { ovs_fatal(0, "\"insert_uuid\" command requires valid uuid"); } s = idltest_simple_insert_persist_uuid(txn, &s_uuid); idltest_simple_set_i(s, atoi(arg2)); } else if (!strcmp(name, "insert_uuid_uref")) { struct idltest_simple3 *s3; struct idltest_simple4 *s4; if (!arg1 || !arg2) { ovs_fatal(0, "\"insert_uuid_uref\" command requires 2 arguments"); } struct uuid s3_uuid; struct uuid s4_uuid; if (!uuid_from_string(&s3_uuid, arg1) || !uuid_from_string(&s4_uuid, arg2)) { ovs_fatal( 0, "\"insert_uuid_uref\" command requires 2 valid uuids"); } s4 = idltest_simple4_insert_persist_uuid(txn, &s4_uuid); s3 = idltest_simple3_insert_persist_uuid(txn, &s3_uuid); idltest_simple3_set_uref(s3, &s4, 1); skip_pre_commit_consistency_check = true; } else if (!strcmp(name, "delete")) { const struct idltest_simple *s; if (!arg1 || arg2) { ovs_fatal(0, "\"delete\" command requires 1 argument"); } s = idltest_find_simple(idl, atoi(arg1)); if (!s) { ovs_fatal(0, "\"delete\" command asks for nonexistent " "i=%d", atoi(arg1)); } idltest_simple_delete(s); } else if (!strcmp(name, "verify")) { const struct idltest_simple *s; if (!arg2 || arg3) { ovs_fatal(0, "\"verify\" command requires 2 arguments"); } s = idltest_find_simple(idl, atoi(arg1)); if (!s) { ovs_fatal(0, "\"verify\" command asks for nonexistent " "i=%d", atoi(arg1)); } if (!strcmp(arg2, "i")) { idltest_simple_verify_i(s); } else if (!strcmp(arg2, "b")) { idltest_simple_verify_b(s); } else if (!strcmp(arg2, "s")) { idltest_simple_verify_s(s); } else if (!strcmp(arg2, "u")) { idltest_simple_verify_s(s); } else if (!strcmp(arg2, "r")) { idltest_simple_verify_r(s); } else { ovs_fatal(0, "\"verify\" command asks for unknown column %s", arg2); } } else if (!strcmp(name, "increment")) { const struct idltest_simple *s; if (!arg1 || arg2) { ovs_fatal(0, "\"increment\" command requires 1 argument"); } s = idltest_find_simple(idl, atoi(arg1)); if (!s) { ovs_fatal(0, "\"set\" command asks for nonexistent " "i=%d", atoi(arg1)); } ovsdb_idl_txn_increment(txn, &s->header_, &idltest_simple_col_i, false); increment = true; } else if (!strcmp(name, "abort")) { ovsdb_idl_txn_abort(txn); ovsdb_idl_check_consistency(idl); break; } else if (!strcmp(name, "destroy")) { print_and_log("%03d: destroy", step); ovsdb_idl_txn_destroy(txn); ovsdb_idl_check_consistency(idl); return true; } else { ovs_fatal(0, "unknown command %s", name); } if (!skip_pre_commit_consistency_check) { ovsdb_idl_check_consistency(idl); } } status = ovsdb_idl_txn_commit_block(txn); struct ds s = DS_EMPTY_INITIALIZER; ds_put_format(&s, "%03d: commit, status=%s", step, ovsdb_idl_txn_status_to_string(status)); if (increment) { ds_put_format(&s, ", increment=%"PRId64, ovsdb_idl_txn_get_increment_new_value(txn)); } print_and_log("%s", ds_cstr(&s)); ds_destroy(&s); ovsdb_idl_txn_destroy(txn); ovsdb_idl_check_consistency(idl); return (status != TXN_ERROR); } static const struct ovsdb_idl_table_class * find_table_class(const char *name) { if (!strcmp(name, "simple")) { return &idltest_table_simple; } else if (!strcmp(name, "link1")) { return &idltest_table_link1; } else if (!strcmp(name, "link2")) { return &idltest_table_link2; } else if (!strcmp(name, "simple3")) { return &idltest_table_simple3; } else if (!strcmp(name, "simple4")) { return &idltest_table_simple4; } else if (!strcmp(name, "simple6")) { return &idltest_table_simple6; } return NULL; } static void parse_simple_json_clause(struct ovsdb_idl_condition *cond, enum ovsdb_function function, const char *column, const struct json *arg) { if (!strcmp(column, "b")) { idltest_simple_add_clause_b(cond, function, json_boolean(arg)); } else if (!strcmp(column, "i")) { idltest_simple_add_clause_i(cond, function, json_integer(arg)); } else if (!strcmp(column, "s")) { idltest_simple_add_clause_s(cond, function, json_string(arg)); } else if (!strcmp(column, "u")) { struct uuid uuid; if (!uuid_from_string(&uuid, json_string(arg))) { ovs_fatal(0, "\"%s\" is not a valid UUID", json_string(arg)); } idltest_simple_add_clause_u(cond, function, uuid); } else if (!strcmp(column, "r")) { idltest_simple_add_clause_r(cond, function, json_real(arg)); } else { ovs_fatal(0, "Unsupported columns name %s", column); } } static void parse_link1_json_clause(struct ovsdb_idl_condition *cond, enum ovsdb_function function, const char *column, const struct json *arg) { if (!strcmp(column, "i")) { idltest_link1_add_clause_i(cond, function, json_integer(arg)); } else { ovs_fatal(0, "Unsupported columns name %s", column); } } static void parse_link2_json_clause(struct ovsdb_idl_condition *cond, enum ovsdb_function function, const char *column, const struct json *arg) { if (!strcmp(column, "i")) { idltest_link2_add_clause_i(cond, function, json_integer(arg)); } else { ovs_fatal(0, "Unsupported columns name %s", column); } } static unsigned int update_conditions(struct ovsdb_idl *idl, char *commands, int step) { const struct ovsdb_idl_table_class *tc; unsigned int next_cond_seqno = 0; char *cmd, *save_ptr1 = NULL; for (cmd = strtok_r(commands, ";", &save_ptr1); cmd; cmd = strtok_r(NULL, ";", &save_ptr1)) { char *save_ptr2 = NULL; char *table_name = strtok_r(cmd, " ", &save_ptr2); struct json *json = parse_json(save_ptr2); int i; if (json->type != JSON_ARRAY) { ovs_fatal(0, "condition should be an array"); } tc = find_table_class(table_name); if (!tc) { ovs_fatal(0, "Table %s does not exist", table_name); } struct ovsdb_idl_condition cond = OVSDB_IDL_CONDITION_INIT(&cond); for (i = 0; i < json_array_size(json); i++) { const struct json *clause = json_array_at(json, i); if (clause->type == JSON_TRUE) { ovsdb_idl_condition_add_clause_true(&cond); } else if (clause->type != JSON_ARRAY || json_array_size(clause) != 3 || json_array_at(clause, 0)->type != JSON_STRING || json_array_at(clause, 1)->type != JSON_STRING) { ovs_fatal(0, "Error parsing condition"); } else { enum ovsdb_function function; const char *function_s = json_string(json_array_at(clause, 1)); struct ovsdb_error *error = ovsdb_function_from_string( function_s, &function); if (error) { ovs_fatal(0, "unknown clause function %s", function_s); } const char *column = json_string(json_array_at(clause, 0)); const struct json *arg = json_array_at(clause, 2); if (!strcmp(table_name, "simple")) { parse_simple_json_clause(&cond, function, column, arg); } else if (!strcmp(table_name, "link1")) { parse_link1_json_clause(&cond, function, column, arg); } else if (!strcmp(table_name, "link2")) { parse_link2_json_clause(&cond, function, column, arg); } } } unsigned int seqno = ovsdb_idl_get_condition_seqno(idl); unsigned int next_seqno = ovsdb_idl_set_condition(idl, tc, &cond); if (seqno == next_seqno ) { print_and_log("%03d: %s: conditions unchanged", step, table_name); } else { print_and_log("%03d: %s: change conditions", step, table_name); } unsigned int new_next_seqno = ovsdb_idl_set_condition(idl, tc, &cond); if (next_seqno != new_next_seqno) { ovs_fatal(0, "condition expected seqno changed"); } next_cond_seqno = MAX(next_cond_seqno, next_seqno); ovsdb_idl_condition_destroy(&cond); json_destroy(json); } return next_cond_seqno; } static void do_idl(struct ovs_cmdl_context *ctx) { struct test_ovsdb_pvt_context *pvt = ctx->pvt; struct jsonrpc *rpc; struct ovsdb_idl *idl; unsigned int next_cond_seqno = 0; unsigned int seqno = 0; struct ovsdb_symbol_table *symtab; size_t n_uuids = 0; int step = 0; int error; int i; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true); ovsdb_idl_set_leader_only(idl, false); if (ctx->argc > 2) { struct stream *stream; error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream, DSCP_DEFAULT), -1, &stream); if (error) { ovs_fatal(error, "failed to connect to \"%s\"", ctx->argv[1]); } rpc = jsonrpc_open(stream); } else { rpc = NULL; } if (pvt->track) { ovsdb_idl_track_add_all(idl); } if (pvt->write_changed_only) { ovsdb_idl_set_write_changed_only_all(idl, true); } setvbuf(stdout, NULL, _IONBF, 0); symtab = ovsdb_symbol_table_create(); const char remote_s[] = "set-remote "; const char cond_s[] = "condition "; if (ctx->argc > 2 && strstr(ctx->argv[2], cond_s)) { next_cond_seqno = update_conditions(idl, ctx->argv[2] + strlen(cond_s), step++); i = 3; } else { i = 2; } for (; i < ctx->argc; i++) { char *arg = ctx->argv[i]; struct jsonrpc_msg *request, *reply; bool terse = false; if (*arg == '?') { /* We're only interested in terse table contents. */ terse = true; arg++; } if (*arg == '+') { /* The previous transaction didn't change anything. */ arg++; } else if (*arg == '^') { /* Wait for condition change to be acked by the server. */ arg++; for (;;) { ovsdb_idl_run(idl); ovsdb_idl_check_consistency(idl); if (ovsdb_idl_get_condition_seqno(idl) == next_cond_seqno) { break; } jsonrpc_run(rpc); ovsdb_idl_wait(idl); jsonrpc_wait(rpc); poll_block(); } } else { /* Wait for update. */ for (;;) { ovsdb_idl_run(idl); ovsdb_idl_check_consistency(idl); if (ovsdb_idl_get_seqno(idl) != seqno) { break; } jsonrpc_run(rpc); ovsdb_idl_wait(idl); jsonrpc_wait(rpc); poll_block(); } /* Print update. */ if (pvt->track) { print_idl_track(idl, step++, terse); ovsdb_idl_track_clear(idl); } else { print_idl(idl, step++, terse); } /* Just run IDL forever for a simple monitoring. */ if (!strcmp(arg, "monitor")) { seqno = ovsdb_idl_get_seqno(idl); i--; continue; } } seqno = ovsdb_idl_get_seqno(idl); if (!strcmp(arg, "reconnect")) { print_and_log("%03d: reconnect", step++); ovsdb_idl_force_reconnect(idl); } else if (!strcmp(arg, "sleep")) { print_and_log("%03d: sleep", step++); xsleep(1); } else if (!strncmp(arg, remote_s, strlen(remote_s))) { ovsdb_idl_set_remote(idl, arg + strlen(remote_s), true); print_and_log("%03d: new remotes: %s, is connected: %s", step++, arg + strlen(remote_s), ovsdb_idl_is_connected(idl) ? "true" : "false"); } else if (!strncmp(arg, cond_s, strlen(cond_s))) { next_cond_seqno = update_conditions(idl, arg + strlen(cond_s), step++); } else if (arg[0] != '[') { if (!idl_set(idl, arg, step++, pvt->assert_read_only)) { /* If idl_set() returns false, then no transaction * was sent to the server and most likely 'seqno' * would remain the same. And the above 'Wait for update' * for loop poll_block() would never return. * So set seqno to 0. */ seqno = 0; } } else { struct json *json = parse_json(arg); substitute_uuids(json, symtab); request = jsonrpc_create_request("transact", json, NULL); error = jsonrpc_transact_block(rpc, request, &reply); if (error || reply->error) { ovs_fatal(error, "jsonrpc transaction failed"); } if (reply->result) { parse_uuids(reply->result, symtab, &n_uuids); } json_destroy(reply->id); reply->id = NULL; struct json *msg_json = jsonrpc_msg_to_json(reply); char *msg_s = json_to_string(msg_json, JSSF_SORT); json_destroy(msg_json); print_and_log("%03d: %s", step++, msg_s); free(msg_s); } } ovsdb_symbol_table_destroy(symtab); if (rpc) { jsonrpc_close(rpc); } for (;;) { ovsdb_idl_run(idl); ovsdb_idl_check_consistency(idl); if (ovsdb_idl_get_seqno(idl) != seqno) { break; } ovsdb_idl_wait(idl); poll_block(); } print_idl(idl, step++, false); ovsdb_idl_track_clear(idl); ovsdb_idl_destroy(idl); print_and_log("%03d: done", step); } static void print_idl_row_simple2(const struct idltest_simple2 *s, int step) { size_t i; const struct ovsdb_datum *smap, *imap; smap = idltest_simple2_get_smap(s, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); imap = idltest_simple2_get_imap(s, OVSDB_TYPE_INTEGER, OVSDB_TYPE_STRING); printf("%03d: name=%s smap=[", step, s->name); for (i = 0; i < smap->n; i++) { printf("[%s : %s]%s", json_string(smap->keys[i].s), json_string(smap->values[i].s), i < smap->n - 1 ? "," : ""); } printf("] imap=["); for (i = 0; i < imap->n; i++) { printf("[%"PRId64" : %s]%s", imap->keys[i].integer, json_string(imap->values[i].s), i < imap->n - 1 ? "," : ""); } printf("]\n"); } static void dump_simple2(struct ovsdb_idl *idl, const struct idltest_simple2 *myRow, int step) { IDLTEST_SIMPLE2_FOR_EACH(myRow, idl) { print_idl_row_simple2(myRow, step); } } static void do_idl_partial_update_map_column(struct ovs_cmdl_context *ctx) { struct ovsdb_idl *idl; struct ovsdb_idl_txn *myTxn; const struct idltest_simple2 *myRow; const struct ovsdb_datum *smap, *imap OVS_UNUSED; int step = 0; char key_to_delete[100]; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true); ovsdb_idl_add_table(idl, &idltest_table_simple2); ovsdb_idl_add_column(idl, &idltest_simple2_col_name); ovsdb_idl_add_column(idl, &idltest_simple2_col_smap); ovsdb_idl_add_column(idl, &idltest_simple2_col_imap); ovsdb_idl_get_initial_snapshot(idl); setvbuf(stdout, NULL, _IONBF, 0); ovsdb_idl_run(idl); /* Display original data in table. */ myRow = NULL; printf("%03d: Getting records\n", step++); dump_simple2(idl, myRow, step++); /* Insert new elements in different map columns. */ myRow = idltest_simple2_first(idl); myTxn = ovsdb_idl_txn_create(idl); idltest_simple2_get_smap(myRow, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); idltest_simple2_update_smap_setkey(myRow, "key1", "myList1"); imap = idltest_simple2_get_imap(myRow, OVSDB_TYPE_INTEGER, OVSDB_TYPE_STRING); idltest_simple2_update_imap_setkey(myRow, 3, "myids2"); idltest_simple2_set_name(myRow, "String2"); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After insert element\n", step++); dump_simple2(idl, myRow, step++); /* Insert duplicate element. */ myTxn = ovsdb_idl_txn_create(idl); idltest_simple2_update_smap_setkey(myRow, "key1", "myList1"); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After insert duplicated element\n", step++); dump_simple2(idl, myRow, step++); /* Deletes an element of a map column. */ myRow = idltest_simple2_first(idl); myTxn = ovsdb_idl_txn_create(idl); smap = idltest_simple2_get_smap(myRow, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); ovs_strlcpy(key_to_delete, json_string(smap->keys[0].s), sizeof key_to_delete); idltest_simple2_update_smap_delkey(myRow, json_string(smap->keys[0].s)); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After delete element\n", step++); dump_simple2(idl, myRow, step++); /* Try to delete a deleted element of a map column. */ myTxn = ovsdb_idl_txn_create(idl); idltest_simple2_update_smap_delkey(myRow, key_to_delete); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After trying to delete a deleted element\n", step++); dump_simple2(idl, myRow, step++); myTxn = ovsdb_idl_txn_create(idl); myRow = idltest_simple2_insert(myTxn); idltest_simple2_update_smap_setkey(myRow, "key3", "myList3"); idltest_simple2_set_name(myRow, "String2"); idltest_simple2_delete(myRow); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After Create element, update smap and Delete element\n", step++); dump_simple2(idl, myRow, step++); myTxn = ovsdb_idl_txn_create(idl); myRow = idltest_simple2_first(idl); idltest_simple2_update_smap_setkey(myRow, "key4", "myList4"); idltest_simple2_set_name(myRow, "String3"); idltest_simple2_delete(myRow); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After update smap and Delete element\n", step++); dump_simple2(idl, myRow, step++); ovsdb_idl_destroy(idl); printf("%03d: End test\n", step); } static void dump_simple3(struct ovsdb_idl *idl, const struct idltest_simple3 *myRow, int step) { IDLTEST_SIMPLE3_FOR_EACH(myRow, idl) { print_idl_row_simple3(myRow, step, false); } } static void do_idl_partial_update_set_column(struct ovs_cmdl_context *ctx) { struct ovsdb_idl *idl; struct ovsdb_idl_txn *myTxn; const struct idltest_simple3 *myRow; struct idltest_simple4 *myRow2; const struct ovsdb_datum *uset OVS_UNUSED; const struct ovsdb_datum *uref OVS_UNUSED; int step = 0; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true); ovsdb_idl_add_table(idl, &idltest_table_simple3); ovsdb_idl_add_column(idl, &idltest_simple3_col_name); ovsdb_idl_add_column(idl, &idltest_simple3_col_uset); ovsdb_idl_add_column(idl, &idltest_simple3_col_uref); ovsdb_idl_add_table(idl, &idltest_table_simple4); ovsdb_idl_add_column(idl, &idltest_simple4_col_name); ovsdb_idl_get_initial_snapshot(idl); setvbuf(stdout, NULL, _IONBF, 0); ovsdb_idl_run(idl); /* Display original data in table. */ myRow = NULL; printf("%03d: Getting records\n", step++); dump_simple3(idl, myRow, step++); /* Insert new elements in different map columns. */ myRow = idltest_simple3_first(idl); myTxn = ovsdb_idl_txn_create(idl); idltest_simple3_get_uset(myRow, OVSDB_TYPE_UUID); struct uuid uuid_to_add; ovs_assert(uuid_from_string(&uuid_to_add, "001e43d2-dd3f-4616-ab6a-83a490bb0991")); idltest_simple3_update_uset_addvalue(myRow, uuid_to_add); idltest_simple3_set_name(myRow, "String2"); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After rename+add new value\n", step++); dump_simple3(idl, myRow, step++); /* Insert duplicate element. */ myTxn = ovsdb_idl_txn_create(idl); struct uuid uuid_to_add2; ovs_assert(uuid_from_string(&uuid_to_add2, "0026b3ba-571b-4729-8227-d860a5210ab8")); idltest_simple3_update_uset_addvalue(myRow, uuid_to_add2); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After add new value\n", step++); dump_simple3(idl, myRow, step++); /* Deletes an element of a set column. */ myRow = idltest_simple3_first(idl); myTxn = ovsdb_idl_txn_create(idl); uset = idltest_simple3_get_uset(myRow, OVSDB_TYPE_UUID); idltest_simple3_update_uset_delvalue(myRow, uuid_to_add); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After delete value\n", step++); dump_simple3(idl, myRow, step++); /* Try to delete a deleted element of a map column. */ myRow = idltest_simple3_first(idl); myTxn = ovsdb_idl_txn_create(idl); idltest_simple3_update_uset_delvalue(myRow, uuid_to_add); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After trying to delete a deleted value\n", step++); dump_simple3(idl, myRow, step++); /* Adds to a table and update a strong reference in another table. */ myRow = idltest_simple3_first(idl); myTxn = ovsdb_idl_txn_create(idl); myRow2 = idltest_simple4_insert(myTxn); idltest_simple4_set_name(myRow2, "test"); idltest_simple3_update_uref_addvalue(myRow, myRow2); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After add to other table + set of strong ref\n", step++); dump_simple3(idl, myRow, step++); /* create row, insert key, delete row */ myTxn = ovsdb_idl_txn_create(idl); myRow = idltest_simple3_insert(myTxn); ovs_assert(uuid_from_string(&uuid_to_add, "12345678-dd3f-4616-ab6a-83a490bb0991")); idltest_simple3_update_uset_addvalue(myRow, uuid_to_add); idltest_simple3_set_name(myRow, "String2"); idltest_simple3_delete(myRow); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After Create element, update set and Delete element\n", step++); dump_simple3(idl, myRow, step++); ovsdb_idl_destroy(idl); printf("%03d: End test\n", step); } static void do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx) { struct ovsdb_idl *idl; struct ovsdb_idl_txn *myTxn; const struct idltest_simple3 *myRow; struct idltest_simple4 *myRow2; const struct ovsdb_datum *uset OVS_UNUSED; const struct ovsdb_datum *uref OVS_UNUSED; int step = 0; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true); ovsdb_idl_add_table(idl, &idltest_table_simple3); ovsdb_idl_add_column(idl, &idltest_simple3_col_name); ovsdb_idl_add_column(idl, &idltest_simple3_col_uset); ovsdb_idl_add_column(idl, &idltest_simple3_col_uref); ovsdb_idl_add_table(idl, &idltest_table_simple4); ovsdb_idl_add_column(idl, &idltest_simple4_col_name); struct ovsdb_idl_index *index = ovsdb_idl_index_create1( idl, &idltest_simple3_col_uref); ovsdb_idl_get_initial_snapshot(idl); setvbuf(stdout, NULL, _IONBF, 0); ovsdb_idl_run(idl); /* Adds to a table and update a strong reference in another table. */ myTxn = ovsdb_idl_txn_create(idl); myRow = idltest_simple3_insert(myTxn); myRow2 = idltest_simple4_insert(myTxn); idltest_simple4_set_name(myRow2, "test"); idltest_simple3_update_uref_addvalue(myRow, myRow2); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After add to other table + set of strong ref\n", step++); dump_simple3(idl, myRow, step++); myRow2 = (struct idltest_simple4 *) idltest_simple4_first(idl); printf("%03d: check simple4: %s\n", step++, myRow2 ? "not empty" : "empty"); /* Use index to query the row with reference */ struct idltest_simple3 *equal = idltest_simple3_index_init_row(index); myRow2 = (struct idltest_simple4 *) idltest_simple4_first(idl); idltest_simple3_index_set_uref(equal, &myRow2, 1); printf("%03d: Query using index with reference\n", step++); IDLTEST_SIMPLE3_FOR_EACH_EQUAL (myRow, equal, index) { print_idl_row_simple3(myRow, step++, false); } idltest_simple3_index_destroy_row(equal); /* Delete the row with reference */ myTxn = ovsdb_idl_txn_create(idl); myRow = idltest_simple3_first(idl); idltest_simple3_delete(myRow); ovsdb_idl_txn_commit_block(myTxn); ovsdb_idl_txn_destroy(myTxn); ovsdb_idl_get_initial_snapshot(idl); printf("%03d: After delete\n", step++); dump_simple3(idl, myRow, step++); myRow2 = (struct idltest_simple4 *) idltest_simple4_first(idl); printf("%03d: check simple4: %s\n", step++, myRow2 ? "not empty" : "empty"); ovsdb_idl_destroy(idl); printf("%03d: End test\n", step); } static int test_idl_compound_index_single_column(struct ovsdb_idl *idl, struct ovsdb_idl_index *s_index, struct ovsdb_idl_index *i_index) { const struct idltest_simple *myRow; struct ovsdb_idl_txn *txn; int step = 0; /* Display records by string index. */ ++step; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, s_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records by integer index. */ ++step; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Display records by string index -> s_index with filtering * where s=\"List001\ */ ++step; struct idltest_simple *equal = idltest_simple_index_init_row(s_index); idltest_simple_index_set_s(equal, "List001"); ovs_assert(strcmp(equal->s, "List001") == 0); IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, s_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records by integer index -> i_index with filtering where i=5 */ ++step; idltest_simple_index_set_i(equal, 5); ovs_assert(equal->i == 5); IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Display records by integer index -> i_index in range i=[3,7] */ ++step; struct idltest_simple *from, *to; from = idltest_simple_index_init_row(i_index); idltest_simple_index_set_i(from, 3); ovs_assert(from->i == 3); to = idltest_simple_index_init_row(i_index); idltest_simple_index_set_i(to, 7); ovs_assert(to->i == 7); IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Delete record i=4 and insert i=54 by integer index -> i_index */ ++step; struct idltest_simple *toDelete, *toInsert; toDelete = idltest_simple_index_init_row(i_index); idltest_simple_index_set_i(toDelete, 4); ovs_assert(toDelete->i == 4); myRow = idltest_simple_index_find(i_index, toDelete); ovs_assert(myRow); ovs_assert(myRow->i == 4); txn = ovsdb_idl_txn_create(idl); idltest_simple_delete(myRow); myRow = idltest_simple_index_find(i_index, toDelete); ovs_assert(!myRow); myRow = idltest_simple_insert(txn); idltest_simple_set_i(myRow, 54); idltest_simple_set_s(myRow, "Lista054"); toInsert = idltest_simple_index_init_row(i_index); idltest_simple_index_set_i(toInsert, 54); myRow = idltest_simple_index_find(i_index, toInsert); ovs_assert(myRow); ovs_assert(myRow->i == 54); ovs_assert(!strcmp(myRow->s, "Lista054")); ovsdb_idl_txn_commit_block(txn); ovsdb_idl_txn_destroy(txn); idltest_simple_index_set_i(to, 60); printf("Expected 60, stored %"PRId64"\n", to->i); ovs_assert(to->i == 60); IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Update record i=10 to i=30, make sure index is updated accordingly */ ++step; struct idltest_simple *toUpdate; toUpdate = idltest_simple_index_init_row(i_index); idltest_simple_index_set_i(toUpdate, 10); ovs_assert(toUpdate->i == 10); myRow = idltest_simple_index_find(i_index, toUpdate); ovs_assert(myRow); ovs_assert(myRow->i == 10); txn = ovsdb_idl_txn_create(idl); idltest_simple_set_i(myRow, 30); myRow = idltest_simple_index_find(i_index, toUpdate); ovs_assert(!myRow); ovsdb_idl_txn_commit_block(txn); ovsdb_idl_txn_destroy(txn); idltest_simple_index_set_i(to, 60); printf("Expected 60, stored %"PRId64"\n", to->i); ovs_assert(to->i == 60); IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Test special-case range, "from" and "to" are both NULL, * which is interpreted as the range from -infinity to +infinity. */ ++step; IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, NULL, NULL, i_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Free the temporal rows */ idltest_simple_index_destroy_row(from); idltest_simple_index_destroy_row(to); idltest_simple_index_destroy_row(equal); idltest_simple_index_destroy_row(toDelete); idltest_simple_index_destroy_row(toInsert); idltest_simple_index_destroy_row(toUpdate); return step; } static int test_idl_compound_index_double_column(struct ovsdb_idl_index *si_index, struct ovsdb_idl_index *sid_index, struct ovsdb_idl_index *is_index, struct ovsdb_idl_index *ids_index) { const struct idltest_simple *myRow; int step = 0; /* Display records by string-integer index -> si_index */ step++; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, si_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records by string-integer(down order) index -> sid_index */ step++; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sid_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records by string-integer index -> si_index with filtering * where s="List000" and i=10 */ step++; struct idltest_simple *equal = idltest_simple_index_init_row(si_index); idltest_simple_index_set_s(equal, "List000"); ovs_assert(strcmp(equal->s, "List000") == 0); idltest_simple_index_set_i(equal, 10); ovs_assert(equal->i == 10); IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, si_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records by string-integer index -> si_index in range i=[0,100] * and s=[\"List002\",\"List003\"] */ step++; struct idltest_simple *from = idltest_simple_index_init_row(si_index); struct idltest_simple *to = idltest_simple_index_init_row(si_index); idltest_simple_index_set_i(from, 0); ovs_assert(from->i == 0); idltest_simple_index_set_s(from, "List001"); ovs_assert(strcmp(from->s, "List001") == 0); idltest_simple_index_set_i(to, 100); ovs_assert(to->i == 100); idltest_simple_index_set_s(to, "List005"); ovs_assert(strcmp(to->s, "List005")==0); IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, si_index) { printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i, myRow->b?"True":"False", myRow->r); } /* Display records using integer-string index. */ step++; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, is_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } /* Display records using integer(descend)-string index. */ step++; IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, ids_index) { printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s, myRow->b?"True":"False", myRow->r); } idltest_simple_index_destroy_row(to); idltest_simple_index_destroy_row(from); idltest_simple_index_destroy_row(equal); return step; } static void do_idl_compound_index(struct ovs_cmdl_context *ctx) { struct ovsdb_idl *idl; enum TESTS { IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN, IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN }; int step = 0; int i; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true); /* Add tables/columns and initialize index data needed for tests */ ovsdb_idl_add_table(idl, &idltest_table_simple); ovsdb_idl_add_column(idl, &idltest_simple_col_s); ovsdb_idl_add_column(idl, &idltest_simple_col_i); ovsdb_idl_add_column(idl, &idltest_simple_col_r); ovsdb_idl_add_column(idl, &idltest_simple_col_b); struct ovsdb_idl_index *s_index = ovsdb_idl_index_create1(idl, &idltest_simple_col_s); struct ovsdb_idl_index *i_index = ovsdb_idl_index_create1(idl, &idltest_simple_col_i); struct ovsdb_idl_index *si_index = ovsdb_idl_index_create2(idl, &idltest_simple_col_s, &idltest_simple_col_i); const struct ovsdb_idl_index_column sid_columns[] = { { .column = &idltest_simple_col_s }, { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC }, }; struct ovsdb_idl_index *sid_index = ovsdb_idl_index_create(idl, sid_columns, ARRAY_SIZE(sid_columns)); struct ovsdb_idl_index *is_index = ovsdb_idl_index_create2(idl, &idltest_simple_col_i, &idltest_simple_col_s); const struct ovsdb_idl_index_column ids_columns[] = { { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC }, { .column = &idltest_simple_col_s }, }; struct ovsdb_idl_index *ids_index = ovsdb_idl_index_create(idl, ids_columns, ARRAY_SIZE(sid_columns)); /* wait for replica to be updated */ ovsdb_idl_get_initial_snapshot(idl); setvbuf(stdout, NULL, _IONBF, 0); int test_to_run = -1; for (i = 2; i < ctx->argc; i++) { char *arg = ctx->argv[i]; if (strcmp(arg,"idl_compound_index_single_column") == 0) { test_to_run = IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN; } else if (strcmp(arg, "idl_compound_index_double_column") == 0) { test_to_run = IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN; } switch (test_to_run) { case IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN: test_idl_compound_index_single_column(idl, s_index, i_index); break; case IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN: test_idl_compound_index_double_column(si_index, sid_index, is_index, ids_index); break; default: printf("%03d: Test %s not implemented.\n", step++, arg); } } ovsdb_idl_destroy(idl); printf("%03d: done\n", step); } static void do_idl_table_column_check(struct ovs_cmdl_context *ctx) { struct jsonrpc *rpc; struct ovsdb_idl *idl; unsigned int seqno = 0; int error; idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true); ovsdb_idl_omit(idl, &idltest_link1_col_i); ovsdb_idl_omit(idl, &idltest_simple7_col_id); ovsdb_idl_set_leader_only(idl, false); struct stream *stream; error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream, DSCP_DEFAULT), -1, &stream); if (error) { ovs_fatal(error, "failed to connect to \"%s\"", ctx->argv[1]); } rpc = jsonrpc_open(stream); for (int r = 1; r <= 2; r++) { ovsdb_idl_set_remote(idl, ctx->argv[r], true); ovsdb_idl_force_reconnect(idl); /* Wait for update. */ for (;;) { ovsdb_idl_run(idl); ovsdb_idl_check_consistency(idl); if (ovsdb_idl_get_seqno(idl) != seqno) { break; } jsonrpc_run(rpc); ovsdb_idl_wait(idl); jsonrpc_wait(rpc); poll_block(); } seqno = ovsdb_idl_get_seqno(idl); bool has_table = idltest_server_has_simple_table(idl); printf("%s remote %s table simple\n", ctx->argv[r], has_table ? "has" : "doesn't have"); has_table = idltest_server_has_link1_table(idl); printf("%s remote %s table link1\n", ctx->argv[r], has_table ? "has" : "doesn't have"); has_table = idltest_server_has_link2_table(idl); printf("%s remote %s table link2\n", ctx->argv[r], has_table ? "has" : "doesn't have"); has_table = idltest_server_has_simple5_table(idl); printf("%s remote %s table simple5\n", ctx->argv[r], has_table ? "has" : "doesn't have"); bool has_col = idltest_server_has_simple5_table_col_irefmap(idl); const struct ovsdb_type *type = idltest_simple5_irefmap_server_type(idl); char *type_s = type ? ovsdb_type_to_english(type) : NULL; printf("%s remote %s col irefmap in table simple5%s%s\n", ctx->argv[r], has_col ? "has" : "doesn't have", type ? ", type: " : "", type ? type_s : ""); free(type_s); has_col = idltest_server_has_link1_table_col_l2(idl); type = idltest_link1_l2_server_type(idl); type_s = type ? ovsdb_type_to_english(type) : NULL; printf("%s remote %s col l2 in table link1%s%s\n", ctx->argv[r], has_col ? "has" : "doesn't have", type ? ", type: " : "", type ? type_s : ""); free(type_s); has_col = idltest_server_has_link1_table_col_i(idl); type = idltest_link1_i_server_type(idl); type_s = type ? ovsdb_type_to_english(type) : NULL; printf("%s remote %s col i in table link1%s%s\n", ctx->argv[r], has_col ? "has" : "doesn't have", type ? ", type: " : "", type ? type_s : ""); free(type_s); has_col = idltest_server_has_simple7_table_col_id(idl); type = idltest_simple7_id_server_type(idl); type_s = type ? ovsdb_type_to_english(type) : NULL; printf("%s remote %s col id in table simple7%s%s\n", ctx->argv[r], has_col ? "has" : "doesn't have", type ? ", type: " : "", type ? type_s : ""); free(type_s); printf("--- remote %s done ---\n", ctx->argv[r]); } jsonrpc_close(rpc); ovsdb_idl_destroy(idl); } static struct ovs_cmdl_command all_commands[] = { { "log-io", NULL, 2, INT_MAX, do_log_io, OVS_RO }, { "default-atoms", NULL, 0, 0, do_default_atoms, OVS_RO }, { "default-data", NULL, 0, 0, do_default_data, OVS_RO }, { "diff-data", NULL, 3, INT_MAX, do_diff_data, OVS_RO }, { "parse-atomic-type", NULL, 1, 1, do_parse_atomic_type, OVS_RO }, { "parse-base-type", NULL, 1, 1, do_parse_base_type, OVS_RO }, { "parse-type", NULL, 1, 1, do_parse_type, OVS_RO }, { "parse-atoms", NULL, 2, INT_MAX, do_parse_atoms, OVS_RO }, { "parse-atom-strings", NULL, 2, INT_MAX, do_parse_atom_strings, OVS_RO }, { "parse-data", NULL, 2, INT_MAX, do_parse_data, OVS_RO }, { "parse-data-strings", NULL, 2, INT_MAX, do_parse_data_strings, OVS_RO }, { "sort-atoms", NULL, 2, 2, do_sort_atoms, OVS_RO }, { "parse-column", NULL, 2, 2, do_parse_column, OVS_RO }, { "parse-table", NULL, 2, 3, do_parse_table, OVS_RO }, { "parse-rows", NULL, 2, INT_MAX, do_parse_rows, OVS_RO }, { "compare-rows", NULL, 2, INT_MAX, do_compare_rows, OVS_RO }, { "parse-conditions", NULL, 2, INT_MAX, do_parse_conditions, OVS_RO }, { "evaluate-conditions", NULL, 3, 3, do_evaluate_conditions, OVS_RO }, { "evaluate-conditions-any", NULL, 3, 3, do_evaluate_conditions_any, OVS_RO }, { "compare-conditions", NULL, 2, 2, do_compare_conditions, OVS_RO }, { "parse-mutations", NULL, 2, INT_MAX, do_parse_mutations, OVS_RO }, { "execute-mutations", NULL, 3, 3, do_execute_mutations, OVS_RO }, { "query", NULL, 3, 3, do_query, OVS_RO }, { "query-distinct", NULL, 4, 4, do_query_distinct, OVS_RO }, { "transact", NULL, 1, INT_MAX, do_transact, OVS_RO }, { "parse-schema", NULL, 1, 1, do_parse_schema, OVS_RO }, { "execute", NULL, 2, INT_MAX, do_execute, OVS_RO }, { "execute-readonly", NULL, 2, INT_MAX, do_execute_ro, OVS_RO }, { "trigger", NULL, 2, INT_MAX, do_trigger, OVS_RO }, { "idl", NULL, 1, INT_MAX, do_idl, OVS_RO }, { "idl-compound-index", NULL, 2, 2, do_idl_compound_index, OVS_RW }, { "idl-compound-index-with-ref", NULL, 1, INT_MAX, do_idl_compound_index_with_ref, OVS_RO }, { "idl-partial-update-map-column", NULL, 1, INT_MAX, do_idl_partial_update_map_column, OVS_RO }, { "idl-partial-update-set-column", NULL, 1, INT_MAX, do_idl_partial_update_set_column, OVS_RO }, { "idl-table-column-check", NULL, 2, 2, do_idl_table_column_check, OVS_RO }, { "help", NULL, 0, INT_MAX, do_help, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static struct ovs_cmdl_command * get_all_commands(void) { return all_commands; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-ovsdb.py000066400000000000000000001235401514270232600225730ustar00rootroot00000000000000# Copyright (c) 2009, 2010, 2011, 2012, 2016 Nicira, Inc. # # 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. import getopt import os import re import sys import uuid import ovs.db.idl import ovs.db.schema import ovs.db.types import ovs.ovsuuid import ovs.poller import ovs.stream import ovs.util import ovs.vlog from ovs.db import data from ovs.db import error from ovs.db.idl import _row_to_uuid as row_to_uuid from ovs.fatal_signal import signal_alarm vlog = ovs.vlog.Vlog("test-ovsdb") vlog.set_levels_from_string("console:dbg") vlog.init(None) def substitute_object_text(data, quotechar='"', obj_chars=("{}", "[]"), tag_format="_OBJECT_{}_"): """Replace objects in strings with tags that can later be retrieved Given data like: 'cmd1 1, cmd2 {"a": {"a": "b"}}, cmd3 1 2, cmd4 ["a", "b"]' Return an output string: 'cmd1 1, cmd2 _OBJECT_0_, cmd3 1 2, cmd4 _OBJECT_1_' and a dictionary of replaced text: {'_OBJECT_0_': '{"a": {"a": "b"}}', '_OBJECT_1_': '["a", "b"]'} """ obj_chars = dict(obj_chars) in_quote = False in_object = [] # Stack of nested outer object opening characters. replaced_text = {} output = "" start = end = 0 for i, c in enumerate(data): if not in_object: if not in_quote and c in obj_chars: # This is the start of a non-quoted outer object that will # be replaced by a tag. in_object.append(c) start = i else: # Regular output. output += c if c == quotechar: in_quote = not in_quote elif not in_quote: # Unquoted object. if c == in_object[0]: # Record on the stack that we are in a nested object of the # same type as the outer object, this object will not be # substituted with a tag. in_object.append(c) elif c == obj_chars[in_object[0]]: # This is the closing character to this potentially nested # object's opening character, so pop it off the stack. in_object.pop() if not in_object: # This is the outer object's closing character, so record # the substituted text and generate the tagged text. end = i + 1 tag = tag_format.format(len(replaced_text)) replaced_text[tag] = data[start:end] output += tag return output, replaced_text def recover_object_text_from_list(words, json): if not json: return words # NOTE(twilson) This does not handle the case of having multiple replaced # objects in the same word, e.g. two json adjacent json strings. return [json.get(word, word) for word in words] def unbox_json(json): if type(json) is list and len(json) == 1: return json[0] else: return json def do_default_atoms(): for type_ in ovs.db.types.ATOMIC_TYPES: if type_ == ovs.db.types.VoidType: continue sys.stdout.write("%s: " % type_.to_string()) atom = data.Atom.default(type_) if atom != data.Atom.default(type_): sys.stdout.write("wrong\n") sys.exit(1) sys.stdout.write("OK\n") def do_default_data(): any_errors = False for n_min in 0, 1: for key in ovs.db.types.ATOMIC_TYPES: if key == ovs.db.types.VoidType: continue for value in ovs.db.types.ATOMIC_TYPES: if value == ovs.db.types.VoidType: valueBase = None else: valueBase = ovs.db.types.BaseType(value) type_ = ovs.db.types.Type(ovs.db.types.BaseType(key), valueBase, n_min, 1) assert type_.is_valid() sys.stdout.write("key %s, value %s, n_min %d: " % (key.to_string(), value.to_string(), n_min)) datum = data.Datum.default(type_) if datum != data.Datum.default(type_): sys.stdout.write("wrong\n") any_errors = True else: sys.stdout.write("OK\n") if any_errors: sys.exit(1) def do_parse_atomic_type(type_string): type_json = unbox_json(ovs.json.from_string(type_string)) atomic_type = ovs.db.types.AtomicType.from_json(type_json) print(ovs.json.to_string(atomic_type.to_json(), sort_keys=True)) def do_parse_base_type(type_string): type_json = unbox_json(ovs.json.from_string(type_string)) base_type = ovs.db.types.BaseType.from_json(type_json) print(ovs.json.to_string(base_type.to_json(), sort_keys=True)) def do_parse_type(type_string): type_json = unbox_json(ovs.json.from_string(type_string)) type_ = ovs.db.types.Type.from_json(type_json) print(ovs.json.to_string(type_.to_json(), sort_keys=True)) def do_parse_atoms(type_string, *atom_strings): type_json = unbox_json(ovs.json.from_string(type_string)) base = ovs.db.types.BaseType.from_json(type_json) for atom_string in atom_strings: atom_json = unbox_json(ovs.json.from_string(atom_string)) try: atom = data.Atom.from_json(base, atom_json) print(ovs.json.to_string(atom.to_json())) except error.Error as e: print(e.args[0]) def do_parse_data(type_string, *data_strings): type_json = unbox_json(ovs.json.from_string(type_string)) type_ = ovs.db.types.Type.from_json(type_json) for datum_string in data_strings: datum_json = unbox_json(ovs.json.from_string(datum_string)) datum = data.Datum.from_json(type_, datum_json) print(ovs.json.to_string(datum.to_json())) def do_sort_atoms(type_string, atom_strings): type_json = unbox_json(ovs.json.from_string(type_string)) base = ovs.db.types.BaseType.from_json(type_json) atoms = [data.Atom.from_json(base, atom_json) for atom_json in unbox_json(ovs.json.from_string(atom_strings))] print(ovs.json.to_string([data.Atom.to_json(atom) for atom in sorted(atoms)])) def do_parse_column(name, column_string): column_json = unbox_json(ovs.json.from_string(column_string)) column = ovs.db.schema.ColumnSchema.from_json(column_json, name) print(ovs.json.to_string(column.to_json(), sort_keys=True)) def do_parse_table(name, table_string, default_is_root_string='false'): default_is_root = default_is_root_string == 'true' table_json = unbox_json(ovs.json.from_string(table_string)) table = ovs.db.schema.TableSchema.from_json(table_json, name) print(ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)) def do_parse_schema(schema_string): schema_json = unbox_json(ovs.json.from_string(schema_string)) schema = ovs.db.schema.DbSchema.from_json(schema_json) print(ovs.json.to_string(schema.to_json(), sort_keys=True)) def get_simple_printable_row_string(row, columns): # NOTE(twilson):This turns out to be a particularly good place to test that # Row object stringification doesn't crash on a large variety of row types. assert str(row) s = "" for column in columns: if hasattr(row, column) and not (type(getattr(row, column)) is ovs.db.data.Atom): value = getattr(row, column) if isinstance(value, dict): value = sorted((row_to_uuid(k), row_to_uuid(v)) for k, v in value.items()) if isinstance(value, (list, tuple)): value = sorted((row_to_uuid(v) for v in value)) elif isinstance(value, list): value = sorted(row_to_uuid(v) for v in value) s += "%s=%s " % (column, value) s = s.strip() s = re.sub('""|,|u?\'', "", s) s = re.sub(r'UUID\(([^)]+)\)', r'\1', s) s = re.sub('False', 'false', s) s = re.sub('True', 'true', s) s = re.sub(r'(ba)=([^[][^ ]*) ', r'\1=[\2] ', s) return s def get_simple_table_printable_row(row, *additional_columns): simple_columns = ["i", "r", "b", "s", "u", "ia", "ra", "ba", "sa", "ua"] simple_columns.extend(additional_columns) return get_simple_printable_row_string(row, simple_columns) def get_simple2_table_printable_row(row): simple2_columns = ["name", "smap", "imap"] return get_simple_printable_row_string(row, simple2_columns) def get_simple3_table_printable_row(row): simple3_columns = ["name", "uset", "uref"] return get_simple_printable_row_string(row, simple3_columns) def get_simple4_table_printable_row(row): simple4_columns = ["name"] return get_simple_printable_row_string(row, simple4_columns) def get_simple5_table_printable_row(row): simple5_columns = ["name", "irefmap"] return get_simple_printable_row_string(row, simple5_columns) def get_simple6_table_printable_row(row): simple6_columns = ["name", "weak_ref"] return get_simple_printable_row_string(row, simple6_columns) def get_link1_table_printable_row(row): s = ["i=%s k=" % row.i] if hasattr(row, "k") and row.k: s.append(str(row.k.i)) if hasattr(row, "ka"): s.append(" ka=[") s.append(' '.join(sorted(str(ka.i) for ka in row.ka))) s.append("] l2=") if hasattr(row, "l2") and row.l2: s.append(str(row.l2[0].i)) return ''.join(s) def get_link2_table_printable_row(row): s = "i=%s l1=" % row.i if hasattr(row, "l1") and row.l1: s += str(row.l1[0].i) return s def get_indexed_table_printable_row(row): return "i=%s" % row.i def get_singleton_table_printable_row(row): return "name=%s" % row.name def print_row(table, row, step, contents, terse): if terse: s = "%03d: table %s" % (step, table) else: s = "%03d: table %s: %s " % (step, table, contents) s += get_simple_printable_row_string(row, ["uuid"]) print(s) def print_idl(idl, step, terse=False): n = 0 if "simple" in idl.tables: simple = idl.tables["simple"].rows for row in simple.values(): print_row("simple", row, step, get_simple_table_printable_row(row), terse) n += 1 if "simple2" in idl.tables: simple2 = idl.tables["simple2"].rows for row in simple2.values(): print_row("simple2", row, step, get_simple2_table_printable_row(row), terse) n += 1 if "simple3" in idl.tables: simple3 = idl.tables["simple3"].rows for row in simple3.values(): print_row("simple3", row, step, get_simple3_table_printable_row(row), terse) n += 1 if "simple4" in idl.tables: simple4 = idl.tables["simple4"].rows for row in simple4.values(): print_row("simple4", row, step, get_simple4_table_printable_row(row), terse) n += 1 if "simple5" in idl.tables: simple5 = idl.tables["simple5"].rows for row in simple5.values(): print_row("simple5", row, step, get_simple5_table_printable_row(row), terse) n += 1 if "simple6" in idl.tables: simple6 = idl.tables["simple6"].rows for row in simple6.values(): print_row("simple6", row, step, get_simple6_table_printable_row(row), terse) n += 1 if "link1" in idl.tables: l1 = idl.tables["link1"].rows for row in l1.values(): print_row("link1", row, step, get_link1_table_printable_row(row), terse) n += 1 if "link2" in idl.tables: l2 = idl.tables["link2"].rows for row in l2.values(): print_row("link2", row, step, get_link2_table_printable_row(row), terse) n += 1 if "indexed" in idl.tables: ind = idl.tables["indexed"].rows for row in ind.values(): print_row("indexed", row, step, get_indexed_table_printable_row(row), terse) n += 1 if "singleton" in idl.tables: sng = idl.tables["singleton"].rows for row in sng.values(): print_row("singleton", row, step, get_singleton_table_printable_row(row), terse) n += 1 if not n: print("%03d: empty" % step) sys.stdout.flush() def substitute_uuids(json, symtab): if isinstance(json, str): symbol = symtab.get(json) if symbol: return str(symbol) elif type(json) is list: return [substitute_uuids(element, symtab) for element in json] elif type(json) is dict: d = {} for key, value in json.items(): d[key] = substitute_uuids(value, symtab) return d return json def parse_uuids(json, symtab): if (isinstance(json, str) and ovs.ovsuuid.is_valid_string(json)): name = "#%d#" % len(symtab) sys.stderr.write("%s = %s\n" % (name, json)) symtab[name] = json elif type(json) is list: for element in json: parse_uuids(element, symtab) elif type(json) is dict: for value in json.values(): parse_uuids(value, symtab) def idltest_find_simple(idl, i): for row in idl.tables["simple"].rows.values(): if row.i == i: return row return None def idltest_find_simple2(idl, i): for row in idl.tables["simple2"].rows.values(): if row.name == i: return row return None def idltest_find_simple3(idl, i): return next(idl.index_equal("simple3", "simple3_by_name", i), None) def idltest_find(idl, table, col, match): return next((r for r in idl.tables[table].rows.values() if getattr(r, col) == match), None) def idl_set(idl, commands, step): txn = ovs.db.idl.Transaction(idl) increment = False fetch_cmds = [] events = [] # `commands` is a comma-separated list of space-separated arguments. To # handle commands that take arguments that may contain spaces or commas, # e.g. JSON, it is necessary to process `commands` to extract those # arguments before splitting by ',' or ' ' below, and then re-insert them # after the arguments are split. commands, data = substitute_object_text(commands) for command in commands.split(','): words = command.split() words = recover_object_text_from_list(words, data) name = words[0] args = words[1:] if name == "notifytest": name = args[0] args = args[1:] old_notify = idl.notify def notify(event, row, updates=None): if updates: upcol = list(updates._data.keys())[0] else: upcol = None events.append("%s|%s|%s" % (event, row.i, upcol)) idl.notify = old_notify idl.notify = notify if name == "set": if len(args) != 3: sys.stderr.write('"set" command requires 3 arguments\n') sys.exit(1) s = idltest_find_simple(idl, int(args[0])) if not s: sys.stderr.write('"set" command asks for nonexistent i=%d\n' % int(args[0])) sys.exit(1) if args[1] == "b": s.b = args[2] == "1" elif args[1] == "s": s.s = args[2].encode(sys.getfilesystemencoding(), 'surrogateescape') \ .decode('utf-8', 'replace') elif args[1] == "u": s.u = uuid.UUID(args[2]) elif args[1] == "r": s.r = float(args[2]) else: sys.stderr.write('"set" comamnd asks for unknown column %s\n' % args[2]) sys.stderr.exit(1) elif name == "insert": if len(args) != 1: sys.stderr.write('"set" command requires 1 argument\n') sys.exit(1) s = txn.insert(idl.tables["simple"]) s.i = int(args[0]) elif name == "insert_no_columns_changed": txn.insert(idl.tables["simple"]) elif name == "insert_uuid": if len(args) != 2: sys.stderr.write('"set" command requires 2 argument\n') sys.exit(1) s = txn.insert(idl.tables["simple"], new_uuid=uuid.UUID(args[0]), persist_uuid=True) s.i = int(args[1]) elif name == "insert_uuid_uref": if len(args) != 2: sys.stderr.write('"set" command requires 2 argument\n') sys.exit(1) s4 = txn.insert(idl.tables["simple4"], new_uuid=uuid.UUID(args[1]), persist_uuid=True) s3 = txn.insert(idl.tables["simple3"], new_uuid=uuid.UUID(args[0]), persist_uuid=True) s3.uref = s4 elif name == "add_op": if len(args) != 1: sys.stderr.write('"add_op" command requires 1 argument\n') sys.stderr.write(f"args={args}\n") sys.exit(1) txn.add_op(ovs.json.from_string(args[0])) elif name == "delete": if len(args) != 1: sys.stderr.write('"delete" command requires 1 argument\n') sys.exit(1) s = idltest_find_simple(idl, int(args[0])) if not s: sys.stderr.write('"delete" command asks for nonexistent i=%d\n' % int(args[0])) sys.exit(1) s.delete() elif name == "verify": if len(args) != 2: sys.stderr.write('"verify" command requires 2 arguments\n') sys.exit(1) s = idltest_find_simple(idl, int(args[0])) if not s: sys.stderr.write('"verify" command asks for nonexistent i=%d\n' % int(args[0])) sys.exit(1) if args[1] in ("i", "b", "s", "u", "r"): s.verify(args[1]) else: sys.stderr.write('"verify" command asks for unknown column ' '"%s"\n' % args[1]) sys.exit(1) elif name == "fetch": if len(args) != 2: sys.stderr.write('"fetch" command requires 2 argument\n') sys.exit(1) row = idltest_find_simple(idl, int(args[0])) if not row: sys.stderr.write('"fetch" command asks for nonexistent i=%d\n' % int(args[0])) sys.exit(1) column = args[1] row.fetch(column) fetch_cmds.append([row, column]) elif name == "increment": if len(args) != 1: sys.stderr.write('"increment" command requires 1 argument\n') sys.exit(1) s = idltest_find_simple(idl, int(args[0])) if not s: sys.stderr.write('"set" command asks for nonexistent i=%d\n' % int(args[0])) sys.exit(1) s.increment("i") increment = True elif name == "abort": txn.abort() break elif name == "destroy": print("%03d: destroy" % step) sys.stdout.flush() txn.abort() return True elif name == "linktest": l1_0 = txn.insert(idl.tables["link1"]) l1_0.i = 1 l1_0.k = [l1_0] l1_0.ka = [l1_0] l1_1 = txn.insert(idl.tables["link1"]) l1_1.i = 2 l1_1.k = [l1_0] l1_1.ka = [l1_0, l1_1] elif name == 'getattrtest': l1 = txn.insert(idl.tables["link1"]) i = getattr(l1, 'i', 1) assert i == 1 l1.i = 2 i = getattr(l1, 'i', 1) assert i == 2 l1.k = [l1] elif name == 'partialmapinsertelement': row = idltest_find_simple2(idl, 'myString1') len_smap = len(getattr(row, 'smap')) row.setkey('smap', 'key1', 'myList1') len_imap = len(getattr(row, 'imap')) row.setkey('imap', 3, 'myids2') row.__setattr__('name', 'String2') assert len(getattr(row, 'smap')) == len_smap assert len(getattr(row, 'imap')) == len_imap + 1 elif name == 'partialmapinsertmultipleelements': row = idltest_find_simple2(idl, 'String2') len_smap = len(getattr(row, 'smap')) row.setkey('smap', 'key2', 'myList2') row.setkey('smap', 'key3', 'myList3') row.setkey('smap', 'key4', 'myList4') assert len(getattr(row, 'smap')) == len_smap + 2 elif name == 'partialmapdelelements': row = idltest_find_simple2(idl, 'String2') len_smap = len(getattr(row, 'smap')) row.delkey('smap', 'key1', 'myList1') row.delkey('smap', 'key2', 'wrongvalue') row.delkey('smap', 'key3') row.delkey('smap', 'key4') assert len(getattr(row, 'smap')) == len_smap - 3 elif name == 'partialmapmutatenew': new_row2 = txn.insert(idl.tables["simple2"]) setattr(new_row2, 'name', 'String2New') new_row2.setkey('smap', 'key1', 'newList1') assert len(getattr(new_row2, 'smap')) == 1 new_row2.setkey('smap', 'key2', 'newList2') assert len(getattr(new_row2, 'smap')) == 2 elif name == 'partialrenamesetadd': row = idltest_find_simple3(idl, 'mySet1') old_size = len(getattr(row, 'uset', [])) row.addvalue('uset', uuid.UUID("001e43d2-dd3f-4616-ab6a-83a490bb0991")) row.__setattr__('name', 'String2') assert len(getattr(row, 'uset', [])) == old_size + 1 elif name == 'partialduplicateadd': row = idltest_find_simple3(idl, 'String2') old_size = len(getattr(row, 'uset', [])) row.addvalue('uset', uuid.UUID("0026b3ba-571b-4729-8227-d860a5210ab8")) row.addvalue('uset', uuid.UUID("0026b3ba-571b-4729-8227-d860a5210ab8")) assert len(getattr(row, 'uset', [])) == old_size + 1 elif name == 'partialsetdel': row = idltest_find_simple3(idl, 'String2') old_size = len(getattr(row, 'uset', [])) row.delvalue('uset', uuid.UUID("001e43d2-dd3f-4616-ab6a-83a490bb0991")) assert len(getattr(row, 'uset', [])) == old_size - 1 elif name == 'partialsetref': new_row = txn.insert(idl.tables["simple4"]) new_row.__setattr__('name', 'test') row = idltest_find_simple3(idl, 'String2') old_size = len(getattr(row, 'uref', [])) row.addvalue('uref', new_row.uuid) assert len(getattr(row, 'uref', [])) == old_size + 1 elif name == 'partialsetoverrideops': row = idltest_find_simple3(idl, 'String2') row.addvalue('uset', uuid.UUID("579e978d-776c-4f19-a225-268e5890e670")) row.delvalue('uset', uuid.UUID("0026b3ba-571b-4729-8227-d860a5210ab8")) row.__setattr__('uset', [uuid.UUID("0026b3ba-571b-4729-8227-d860a5210ab8")]) assert len(getattr(row, 'uset', [])) == 1 elif name == 'partialsetadddelete': row = idltest_find_simple3(idl, 'String2') row.addvalue('uset', uuid.UUID('b6272353-af9c-40b7-90fe-32a43e6518a1')) row.addvalue('uset', uuid.UUID('1d6a71a2-dffb-426e-b2fa-b727091f9901')) row.delvalue('uset', uuid.UUID('0026b3ba-571b-4729-8227-d860a5210ab8')) assert len(getattr(row, 'uset', [])) == 2 elif name == 'partialsetmutatenew': new_row41 = txn.insert(idl.tables["simple4"]) new_row41.__setattr__('name', 'new_row41') new_row3 = txn.insert(idl.tables["simple3"]) setattr(new_row3, 'name', 'String3') new_row3.addvalue('uset', new_row41.uuid) assert len(getattr(new_row3, 'uset', [])) == 1 elif name == 'partialmapmutateirefmap': row3 = idltest_find_simple3(idl, "myString1") row5 = idltest_find(idl, "simple5", "name", "myString2") row5.setkey('irefmap', 1, row3.uuid) maplen = len(row5.irefmap) assert maplen == 1, "expected 1, got %d" % maplen else: sys.stderr.write("unknown command %s\n" % name) sys.exit(1) status = txn.commit_block() sys.stdout.write("%03d: commit, status=%s" % (step, ovs.db.idl.Transaction.status_to_string(status))) if increment and status == ovs.db.idl.Transaction.SUCCESS: sys.stdout.write(", increment=%d" % txn.get_increment_new_value()) if events: # Event notifications from operations in a single transaction are # not in a gauranteed order due to update messages being dicts sys.stdout.write(", events=" + ", ".join(sorted(events))) sys.stdout.write("\n") sys.stdout.flush() return status != ovs.db.idl.Transaction.ERROR def update_condition(idl, commands, step): next_cond_seqno = 0 commands = commands[len("condition "):].split(";") for command in commands: command = command.split(" ") if len(command) != 2: sys.stderr.write("Error parsing condition %s\n" % command) sys.exit(1) table = command[0] cond = ovs.json.from_string(command[1]) next_seqno = idl.cond_change(table, cond) if idl.cond_seqno == next_seqno: sys.stdout.write("%03d: %s: conditions unchanged\n" % (step, table)) else: sys.stdout.write("%03d: %s: change conditions\n" % (step, table)) sys.stdout.flush() assert next_seqno == idl.cond_change(table, cond), \ "condition expected seqno changed" next_cond_seqno = max(next_cond_seqno, next_seqno) return next_cond_seqno def do_idl(schema_file, remote, *commands): schema_helper = ovs.db.idl.SchemaHelper(schema_file) track_notify = False if remote.startswith("ssl:"): if len(commands) < 3: sys.stderr.write("SSL/TLS connection requires private key, " "certificate for private key, and peer CA " "certificate as arguments\n") sys.exit(1) ovs.stream.Stream.ssl_set_private_key_file(commands[0]) ovs.stream.Stream.ssl_set_certificate_file(commands[1]) ovs.stream.Stream.ssl_set_ca_cert_file(commands[2]) commands = commands[3:] if commands and commands[0] == "track-notify": commands = commands[1:] track_notify = True if commands and commands[0].startswith("?"): readonly = {} for x in commands[0][1:].split("?"): readonly = [] table, columns = x.split(":") columns = columns.split(",") for index, column in enumerate(columns): if column[-1] == '!': columns[index] = columns[index][:-1] readonly.append(columns[index]) schema_helper.register_columns(table, columns, readonly) commands = commands[1:] else: schema_helper.register_all() idl = ovs.db.idl.Idl(remote, schema_helper, leader_only=False) if "simple3" in idl.tables: idl.index_create("simple3", "simple3_by_name") if "indexed" in idl.tables: idx = idl.index_create("indexed", "indexed_by_i") idx.add_column("i") if commands: remotes = remote.split(',') stream = None for r in remotes: error, stream = ovs.stream.Stream.open_block( ovs.stream.Stream.open(r), 2000) if not error and stream: break stream = None if not stream: sys.stderr.write("failed to connect to \"%s\"" % remote) sys.exit(1) rpc = ovs.jsonrpc.Connection(stream) else: rpc = None next_cond_seqno = 0 symtab = {} seqno = 0 step = 0 def mock_notify(event, row, updates=None): output = "%03d: " % step output += "event:" + str(event) + ", row={" output += get_simple_table_printable_row(row, 'l2', 'l1') + "}, " output += get_simple_printable_row_string(row, ["uuid"]) + ", updates=" if updates is None: output += "None" else: output += "{" + get_simple_table_printable_row(updates) + "}" output += '\n' sys.stdout.write(output) sys.stdout.flush() if track_notify and "simple" in idl.tables: idl.notify = mock_notify commands = list(commands) if len(commands) >= 1 and "condition" in commands[0]: next_cond_seqno = update_condition(idl, commands.pop(0), step) step += 1 for command in commands: terse = False if command.startswith("?"): # We're only interested in terse table contents. terse = True command = command[1:] if command.startswith("+"): # The previous transaction didn't change anything. command = command[1:] elif command.startswith("^"): # Wait for condition change to be acked by the server. command = command[1:] while idl.cond_seqno != next_cond_seqno and not idl.run(): rpc.run() poller = ovs.poller.Poller() idl.wait(poller) rpc.wait(poller) poller.block() else: # Wait for update. while True: while idl.change_seqno == seqno and not idl.run(): rpc.run() poller = ovs.poller.Poller() idl.wait(poller) rpc.wait(poller) poller.block() print_idl(idl, step, terse) step += 1 # Run IDL forever in case of a simple monitor, otherwise # break and execute the command. seqno = idl.change_seqno if command != "monitor": break seqno = idl.change_seqno if command == "reconnect": print("%03d: reconnect" % step) sys.stdout.flush() step += 1 idl.force_reconnect() elif "condition" in command: next_cond_seqno = update_condition(idl, command, step) step += 1 elif not command.startswith("["): if not idl_set(idl, command, step): # If idl_set() returns false, then no transaction # was sent to the server and most likely seqno # would remain the same. And the above 'Wait for update' # for loop poller.block() would never return. # So set seqno to 0. seqno = 0 step += 1 else: json = ovs.json.from_string(command) if isinstance(json, str): sys.stderr.write("\"%s\": %s\n" % (command, json)) sys.exit(1) json = substitute_uuids(json, symtab) request = ovs.jsonrpc.Message.create_request("transact", json) error, reply = rpc.transact_block(request) if error: sys.stderr.write("jsonrpc transaction failed: %s\n" % os.strerror(error)) sys.exit(1) elif reply.error is not None: sys.stderr.write("jsonrpc transaction failed: %s\n" % reply.error) sys.exit(1) sys.stdout.write("%03d: " % step) sys.stdout.flush() step += 1 if reply.result is not None: parse_uuids(reply.result, symtab) reply.id = None sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json())) sys.stdout.flush() if rpc: rpc.close() while idl.change_seqno == seqno and not idl.run(): poller = ovs.poller.Poller() idl.wait(poller) poller.block() print_idl(idl, step) step += 1 idl.close() print("%03d: done" % step) def do_idl_passive(schema_file, remote, *commands): symtab = {} step = 0 schema_helper = ovs.db.idl.SchemaHelper(schema_file) schema_helper.register_all() idl = ovs.db.idl.Idl(remote, schema_helper) while idl._session.rpc is None: idl.run() rpc = idl._session.rpc print_idl(idl, step) step += 1 for command in commands: json = ovs.json.from_string(command) if isinstance(json, str): sys.stderr.write("\"%s\": %s\n" % (command, json)) sys.exit(1) json = substitute_uuids(json, symtab) request = ovs.jsonrpc.Message.create_request("transact", json) error, reply = rpc.transact_block(request) if error: sys.stderr.write("jsonrpc transaction failed: %s\n" % os.strerror(error)) sys.exit(1) elif reply.error is not None: sys.stderr.write("jsonrpc transaction failed: %s\n" % reply.error) sys.exit(1) sys.stdout.write("%03d: " % step) sys.stdout.flush() step += 1 if reply.result is not None: parse_uuids(reply.result, symtab) reply.id = None sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json())) sys.stdout.flush() idl.close() print("%03d: done" % step) def do_idl_cluster(schema_file, remote, pid, *commands): schema_helper = ovs.db.idl.SchemaHelper(schema_file) if remote.startswith("ssl:"): if len(commands) < 3: sys.stderr.write("SSL/TLS connection requires private key, " "certificate for private key, and peer CA " "certificate as arguments\n") sys.exit(1) ovs.stream.Stream.ssl_set_private_key_file(commands[0]) ovs.stream.Stream.ssl_set_certificate_file(commands[1]) ovs.stream.Stream.ssl_set_ca_cert_file(commands[2]) commands = commands[3:] schema_helper.register_all() idl = ovs.db.idl.Idl(remote, schema_helper) step = 0 seqno = 0 commands = list(commands) for command in commands: if command.startswith("+"): # The previous transaction didn't change anything. command = command[1:] else: # Wait for update. while idl.change_seqno == seqno and not idl.run(): poller = ovs.poller.Poller() idl.wait(poller) poller.block() step += 1 seqno = idl.change_seqno if command == "reconnect": print("%03d: reconnect" % step) sys.stdout.flush() step += 1 idl.force_reconnect() elif command == "remote": print("%03d: %s" % (step, idl.session_name())) sys.stdout.flush() step += 1 elif command == "remotestop": r = idl.session_name() remotes = remote.split(',') i = remotes.index(r) pids = pid.split(',') command = None try: command = "kill %s" % pids[i] except ValueError as error: sys.stderr.write("Cannot find pid of remote: %s\n" % os.strerror(error)) sys.exit(1) os.popen(command) print("%03d: stop %s" % (step, pids[i])) sys.stdout.flush() step += 1 idl.close() print("%03d: done" % step) def usage(): print("""\ %(program_name)s: test utility for Open vSwitch database Python bindings usage: %(program_name)s [OPTIONS] COMMAND ARG... The following commands are supported: default-atoms test ovsdb_atom_default() default-data test ovsdb_datum_default() parse-atomic-type TYPE parse TYPE as OVSDB atomic type, and re-serialize parse-base-type TYPE parse TYPE as OVSDB base type, and re-serialize parse-type JSON parse JSON as OVSDB type, and re-serialize parse-atoms TYPE ATOM... parse JSON ATOMs as atoms of TYPE, and re-serialize parse-atom-strings TYPE ATOM... parse string ATOMs as atoms of given TYPE, and re-serialize sort-atoms TYPE ATOM... print JSON ATOMs in sorted order parse-data TYPE DATUM... parse JSON DATUMs as data of given TYPE, and re-serialize parse-column NAME OBJECT parse column NAME with info OBJECT, and re-serialize parse-table NAME OBJECT [DEFAULT-IS-ROOT] parse table NAME with info OBJECT parse-schema JSON parse JSON as an OVSDB schema, and re-serialize idl SCHEMA SERVER [?T1:C1,C2...[?T2:C1,C2,...]...] [TRANSACTION...] connect to SERVER (which has the specified SCHEMA) and dump the contents of the database as seen initially by the IDL implementation and after executing each TRANSACTION. (Each TRANSACTION must modify the database or this command will hang.) By default, all columns of all tables are monitored. The "?" option can be used to monitor specific Table:Column(s). The table and their columns are listed as a string of the form starting with "?": ?:,,... e.g.: ?simple:b - Monitor column "b" in table "simple" Entries for multiple tables are seperated by "?": ?:,...?:,... e.g.: ?simple:b?link1:i,k - Monitor column "b" in table "simple", and column "i", "k" in table "link1" Readonly columns: Suffixing a "!" after a column indicates that the column is to be registered "readonly". e.g.: ?simple:i,b! - Register interest in column "i" (monitoring) and column "b" (readonly). The following options are also available: -t, --timeout=SECS give up after SECS seconds -h, --help display this help message\ """ % {'program_name': ovs.util.PROGRAM_NAME}) sys.exit(0) def main(argv): try: options, args = getopt.gnu_getopt(argv[1:], 't:h', ['timeout', 'help']) except getopt.GetoptError as geo: sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg)) sys.exit(1) timeout = None for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-t', '--timeout']: try: timeout = int(value) if timeout < 1: raise TypeError except TypeError: raise error.Error("value %s on -t or --timeout is not at " "least 1" % value) else: sys.exit(0) signal_alarm(timeout) if not args: sys.stderr.write("%s: missing command argument " "(use --help for help)\n" % ovs.util.PROGRAM_NAME) sys.exit(1) commands = {"default-atoms": (do_default_atoms, 0), "default-data": (do_default_data, 0), "parse-atomic-type": (do_parse_atomic_type, 1), "parse-base-type": (do_parse_base_type, 1), "parse-type": (do_parse_type, 1), "parse-atoms": (do_parse_atoms, (2,)), "parse-data": (do_parse_data, (2,)), "sort-atoms": (do_sort_atoms, 2), "parse-column": (do_parse_column, 2), "parse-table": (do_parse_table, (2, 3)), "parse-schema": (do_parse_schema, 1), "idl": (do_idl, (2,)), "idl_passive": (do_idl_passive, (2,)), "idl-cluster": (do_idl_cluster, (3,))} command_name = args[0] args = args[1:] if command_name not in commands: sys.stderr.write("%s: unknown command \"%s\" " "(use --help for help)\n" % (ovs.util.PROGRAM_NAME, command_name)) sys.exit(1) func, n_args = commands[command_name] if type(n_args) is tuple: if len(args) < n_args[0]: sys.stderr.write("%s: \"%s\" requires at least %d arguments but " "only %d provided\n" % (ovs.util.PROGRAM_NAME, command_name, n_args[0], len(args))) sys.exit(1) elif type(n_args) is int: if len(args) != n_args: sys.stderr.write("%s: \"%s\" requires %d arguments but %d " "provided\n" % (ovs.util.PROGRAM_NAME, command_name, n_args, len(args))) sys.exit(1) else: assert False func(*args) if __name__ == '__main__': try: main(sys.argv) except error.Error as e: sys.stderr.write("%s\n" % e) sys.exit(1) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-packets.c000066400000000000000000000126511514270232600227020ustar00rootroot00000000000000/* * Copyright (c) 2011, 2014, 2017 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "packets.h" #include #include #include #include #include "ovstest.h" static void test_ipv4_cidr(void) { assert(ip_is_cidr(htonl(0x00000000))); assert(ip_is_cidr(htonl(0x80000000))); assert(ip_is_cidr(htonl(0xf0000000))); assert(ip_is_cidr(htonl(0xffffffe0))); assert(ip_is_cidr(htonl(0xffffffff))); assert(!ip_is_cidr(htonl(0x00000001))); assert(!ip_is_cidr(htonl(0x40000000))); assert(!ip_is_cidr(htonl(0x0fffffff))); assert(!ip_is_cidr(htonl(0xffffffd0))); } static void test_ipv6_static_masks(void) { /* The 'exact' and 'any' addresses should be identical to * 'in6addr_exact' and 'in6addr_any' definitions, but we redefine * them here since the pre-defined ones are used in the functions * we're testing. */ struct in6_addr exact = {{{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }}}; struct in6_addr any = {{{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }}}; struct in6_addr neither = {{{ 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, \ 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef }}}; assert(ipv6_mask_is_exact(&exact)); assert(!ipv6_mask_is_exact(&any)); assert(!ipv6_mask_is_exact(&neither)); assert(!ipv6_mask_is_any(&exact)); assert(ipv6_mask_is_any(&any)); assert(!ipv6_mask_is_any(&neither)); } static void test_ipv6_cidr(void) { struct in6_addr dest; struct in6_addr src = {{{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }}}; dest = ipv6_create_mask(0); assert(ipv6_mask_is_any(&dest)); assert(ipv6_count_cidr_bits(&dest) == 0); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(128); assert(ipv6_mask_is_exact(&dest)); assert(ipv6_count_cidr_bits(&dest) == 128); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(1); assert(ipv6_count_cidr_bits(&dest) == 1); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(13); assert(ipv6_count_cidr_bits(&dest) == 13); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(64); assert(ipv6_count_cidr_bits(&dest) == 64); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(95); assert(ipv6_count_cidr_bits(&dest) == 95); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(96); assert(ipv6_count_cidr_bits(&dest) == 96); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(97); assert(ipv6_count_cidr_bits(&dest) == 97); assert(ipv6_is_cidr(&dest)); dest = ipv6_create_mask(127); assert(ipv6_count_cidr_bits(&dest) == 127); assert(ipv6_is_cidr(&dest)); src.s6_addr[8] = 0xf0; assert(ipv6_is_cidr(&src)); assert(ipv6_count_cidr_bits(&src) == 68); src.s6_addr[15] = 0x01; assert(!ipv6_is_cidr(&src)); src.s6_addr[15] = 0x00; assert(ipv6_is_cidr(&src)); src.s6_addr[8] = 0x0f; assert(!ipv6_is_cidr(&src)); } static void test_ipv6_masking(void) { struct in6_addr dest; struct in6_addr mask; mask = ipv6_create_mask(0); dest = ipv6_addr_bitand(&in6addr_exact, &mask); assert(ipv6_count_cidr_bits(&dest) == 0); mask = ipv6_create_mask(1); dest = ipv6_addr_bitand(&in6addr_exact, &mask); assert(ipv6_count_cidr_bits(&dest) == 1); mask = ipv6_create_mask(13); dest = ipv6_addr_bitand(&in6addr_exact, &mask); assert(ipv6_count_cidr_bits(&dest) == 13); mask = ipv6_create_mask(127); dest = ipv6_addr_bitand(&in6addr_exact, &mask); assert(ipv6_count_cidr_bits(&dest) == 127); mask = ipv6_create_mask(128); dest = ipv6_addr_bitand(&in6addr_exact, &mask); assert(ipv6_count_cidr_bits(&dest) == 128); } static void test_ipv6_parsing(void) { struct in6_addr o_ipv6, p_ipv6; struct in6_addr mask; assert(inet_pton(AF_INET6, "2001:db8:0:0:0:0:2:1", &o_ipv6) == 1); assert(!ipv6_parse_masked("2001:db8:0:0:0:0:2:1/64", &p_ipv6, &mask)); assert(ipv6_addr_equals(&o_ipv6, &p_ipv6)); assert(ipv6_count_cidr_bits(&mask) == 64); assert(!ipv6_parse_masked("2001:db8:0:0:0:0:2:1/ffff:ffff:ffff:ffff::", &p_ipv6, &mask)); assert(ipv6_addr_equals(&o_ipv6, &p_ipv6)); assert(ipv6_count_cidr_bits(&mask) == 64); assert(!ipv6_parse_masked("2001:db8:0:0:0:0:2:1", &p_ipv6, &mask)); assert(ipv6_addr_equals(&o_ipv6, &p_ipv6)); assert(ipv6_count_cidr_bits(&mask) == 128); } static void test_packets_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { test_ipv4_cidr(); test_ipv6_static_masks(); test_ipv6_cidr(); test_ipv6_masking(); test_ipv6_parsing(); } OVSTEST_REGISTER("test-packets", test_packets_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-psample.c000066400000000000000000000175541514270232600227200ustar00rootroot00000000000000/* * Copyright (c) 2024 Red Hat, Inc. * * 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. */ #include #undef NDEBUG #include #include #include #include #include #include #include "command-line.h" #include "dp-packet.h" #include "util.h" #include "netlink.h" #include "netlink-socket.h" #include "openvswitch/ofp-actions.h" #include "openvswitch/ofp-print.h" #include "openvswitch/types.h" #include "openvswitch/uuid.h" #include "openvswitch/vlog.h" #include "ovstest.h" VLOG_DEFINE_THIS_MODULE(test_psample); static int psample_family = 0; static uint32_t group_id = 0; static bool has_filter; static void usage(void) { printf("%s: psample collector test utility\n" "usage: %s [OPTIONS] [GROUP]\n" "where GROUP is the psample group_id to listen on. " "If none is provided all events are printed.\n", program_name, program_name); vlog_usage(); printf("\nOther Options:\n" " -h, --help display this help message\n"); } static void parse_options(int argc, char *argv[]) { enum { VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"group", required_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *tmp_short_options, *short_options; int ret = EXIT_SUCCESS; bool do_exit = false; tmp_short_options = ovs_cmdl_long_options_to_short_options(long_options); short_options = xasprintf("+%s", tmp_short_options); while (!do_exit) { int option; option = getopt_long(argc, argv, short_options, long_options, NULL); if (option == -1) { break; } switch (option) { VLOG_OPTION_HANDLERS case 'h': usage(); do_exit = true; ret = EXIT_SUCCESS; break; case '?': do_exit = true; ret = EXIT_FAILURE; break; default: OVS_NOT_REACHED(); } } free(tmp_short_options); free(short_options); if (do_exit) { exit(ret); } } static int connect_psample_socket(struct nl_sock **sock) { unsigned int psample_packet_mcgroup; int error; error = nl_lookup_genl_family(PSAMPLE_GENL_NAME , &psample_family); if (error) { VLOG_ERR("PSAMPLE_GENL_NAME not found: %s", ovs_strerror(error)); return error; } error = nl_lookup_genl_mcgroup(PSAMPLE_GENL_NAME, PSAMPLE_NL_MCGRP_SAMPLE_NAME, &psample_packet_mcgroup); if (error) { VLOG_ERR("psample packet multicast group not found: %s", ovs_strerror(error)); return error; } error = nl_sock_create(NETLINK_GENERIC, sock); if (error) { VLOG_ERR("cannot create netlink socket: %s ", ovs_strerror(error)); return error; } nl_sock_listen_all_nsid(*sock, true); error = nl_sock_join_mcgroup(*sock, psample_packet_mcgroup); if (error) { nl_sock_destroy(*sock); *sock = NULL; VLOG_ERR("cannot join psample multicast group: %s", ovs_strerror(error)); return error; } return 0; } /* Internal representation of a sample. */ struct sample { struct dp_packet packet; uint32_t group_id; uint32_t rate; uint32_t obs_domain_id; uint32_t obs_point_id; bool has_cookie; }; static inline void sample_clear(struct sample *sample) { sample->group_id = 0; sample->rate = 0; sample->obs_domain_id = 0; sample->obs_point_id = 0; sample->has_cookie = false; dp_packet_clear(&sample->packet); } static int parse_psample(struct ofpbuf *buf, struct sample *sample) { static const struct nl_policy psample_packet_policy[] = { [PSAMPLE_ATTR_SAMPLE_GROUP] = { .type = NL_A_U32 }, [PSAMPLE_ATTR_SAMPLE_RATE] = { .type = NL_A_U32 }, [PSAMPLE_ATTR_DATA] = { .type = NL_A_UNSPEC, .optional = true }, [PSAMPLE_ATTR_USER_COOKIE] = { .type = NL_A_UNSPEC, .optional = true }, }; struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); struct nlattr *attr; struct nlattr *a[ARRAY_SIZE(psample_packet_policy)]; if (!nlmsg || !genl || !nl_policy_parse(&b, 0, psample_packet_policy, a, ARRAY_SIZE(psample_packet_policy))) { return EINVAL; } attr = a[PSAMPLE_ATTR_DATA]; if (attr) { dp_packet_push(&sample->packet, nl_attr_get(attr), nl_attr_get_size(attr)); } sample->group_id = nl_attr_get_u32(a[PSAMPLE_ATTR_SAMPLE_GROUP]); sample->rate = nl_attr_get_u32(a[PSAMPLE_ATTR_SAMPLE_RATE]); attr = a[PSAMPLE_ATTR_USER_COOKIE]; if (attr && nl_attr_get_size(attr) == sizeof sample->obs_domain_id + sizeof sample->obs_point_id) { const ovs_be32 *data = nl_attr_get(attr); sample->has_cookie = true; sample->obs_domain_id = ntohl(*data++); sample->obs_point_id = ntohl(*data); } return 0; } static void run(struct nl_sock *sock) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); struct sample sample; int error; dp_packet_init(&sample.packet, 1500); fprintf(stdout, "Listening for psample events\n"); fflush(stdout); for (;;) { uint64_t buf_stub[4096 / 8]; struct ofpbuf buf; sample_clear(&sample); ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); error = nl_sock_recv(sock, &buf, NULL, true); if (error == ENOBUFS) { fprintf(stderr, "[missed events]\n"); continue; } else if (error == EAGAIN) { continue; } else if (error) { VLOG_ERR_RL(&rl, "error reading samples: %i", error); continue; } error = parse_psample(&buf, &sample); if (error) { VLOG_ERR_RL(&rl, "error parsing samples: %i", error); continue; } if (!has_filter || sample.group_id == group_id) { fprintf(stdout, "group_id=0x%"PRIx32",prob=%"PRIu32" ", sample.group_id, sample.rate); if (sample.has_cookie) { fprintf(stdout, "obs_domain=0x%"PRIx32",obs_point=0x%"PRIx32" ", sample.obs_domain_id, sample.obs_point_id); } ofp_print_dp_packet(stdout, &sample.packet); } fflush(stdout); } } static void test_psample_main(int argc, char *argv[]) { struct nl_sock *sock; int error; parse_options(argc, argv); if (argc - optind > 1) { ovs_fatal(0, "at most one positional argument supported " "(use --help for help)"); } else if (argc - optind == 1) { if (!str_to_uint(argv[optind], 10, &group_id)) { ovs_fatal(0, "invalid group id"); } has_filter = true; } error = connect_psample_socket(&sock); if (error) { ovs_fatal(error, "failed to connect to psample socket"); } run(sock); } OVSTEST_REGISTER("test-psample", test_psample_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-random.c000066400000000000000000000037651514270232600225360ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "random.h" #include #include #include "ovstest.h" static void test_random_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { enum { N_ROUNDS = 10000 }; unsigned long long int total; int hist16[8][16]; int hist2[32]; int i; random_set_seed(1); total = 0; memset(hist2, 0, sizeof hist2); memset(hist16, 0, sizeof hist16); for (i = 0; i < N_ROUNDS; i++) { uint32_t x; int j; x = random_uint32(); total += x; for (j = 0; j < 32; j++) { if (x & (1u << j)) { hist2[j]++; } } for (j = 0; j < 8; j++) { hist16[j][(x >> (j * 4)) & 15]++; } } printf("average=%08llx\n", total / N_ROUNDS); printf("\nbit 0 1\n"); for (i = 0; i < 32; i++) { printf("%3d %5d %5d\n", i, N_ROUNDS - hist2[i], hist2[i]); } printf("(expected values are %d)\n", N_ROUNDS / 2); printf("\nnibble 0 1 2 3 4 5 6 7 8 9 10 11 12 " "13 14 15\n"); for (i = 0; i < 8; i++) { int j; printf("%6d", i); for (j = 0; j < 16; j++) { printf(" %3d", hist16[i][j]); } printf("\n"); } printf("(expected values are %d)\n", N_ROUNDS / 16); } OVSTEST_REGISTER("test-random", test_random_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-rcu.c000066400000000000000000000034251514270232600220400ustar00rootroot00000000000000/* * Copyright (c) 2016 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "fatal-signal.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "ovstest.h" #include "util.h" static void * quiescer_main(void *aux OVS_UNUSED) { /* A new thread must be not be quiescent */ ovs_assert(!ovsrcu_is_quiescent()); ovsrcu_quiesce_start(); /* After the above call it must be quiescent */ ovs_assert(ovsrcu_is_quiescent()); return NULL; } static void test_rcu_quiesce(void) { pthread_t quiescer; quiescer = ovs_thread_create("quiescer", quiescer_main, NULL); /* This is the main thread of the process. After spawning its first * thread it must not be quiescent. */ ovs_assert(!ovsrcu_is_quiescent()); xpthread_join(quiescer, NULL); } static void add_count(void *_count) { unsigned *count = (unsigned *)_count; (*count) ++; } static void test_rcu_barrier(void) { unsigned count = 0; for (int i = 0; i < 10; i ++) { ovsrcu_postpone(add_count, &count); } ovsrcu_barrier(); ovs_assert(count == 10); } static void test_rcu(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { test_rcu_quiesce(); test_rcu_barrier(); } OVSTEST_REGISTER("test-rcu", test_rcu); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-rculist.c000066400000000000000000000142321514270232600227320ustar00rootroot00000000000000/* * Copyright (c) 2023 Red Hat, Inc. * * 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. */ #include #undef NDEBUG #include #include "openvswitch/list.h" #include "ovstest.h" #include "ovs-thread.h" #include "random.h" #include "rculist.h" #include "util.h" enum { MAX_ELEMS = 10, MAX_CHECKS = 200 }; /* Sample list element. */ struct element { int value; struct rculist node; }; static void do_usleep(unsigned int usecs) { #ifdef _WIN32 Sleep(MAX(usecs / 1000, 1)); #else usleep(usecs); #endif } /* Continuously check the integrity of the list until it's empty. */ static void * checker_main(void *aux) { struct rculist *list = (struct rculist *) aux; struct element *elem; bool checked = false; for (int i = 0; i < MAX_CHECKS; i++) { int value = -1; RCULIST_FOR_EACH (elem, node, list) { ovs_assert(value <= elem->value); ovs_assert(elem->value < MAX_ELEMS); value = elem->value; if (!checked) { checked = true; } do_usleep(10); } ovsrcu_quiesce(); if (checked && rculist_is_empty(list)) { break; } } return NULL; } /* Run test while a thread checks the integrity of the list. * Tests must end up emptying the list. */ static void run_test_while_checking(void (*function)(struct rculist *list)) { struct rculist list; pthread_t checker; rculist_init(&list); checker = ovs_thread_create("checker", checker_main, &list); function(&list); ovs_assert(rculist_is_empty(&list)); ovsrcu_quiesce(); xpthread_join(checker, NULL); printf("."); } static void test_rculist_insert_delete__(struct rculist *list, bool long_version) { struct element *elem; int value; for (int i = 1; i < MAX_ELEMS; i++) { elem = xmalloc(sizeof *elem); elem->value = i; rculist_insert(list, &elem->node); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } ovsrcu_quiesce(); value = MAX_ELEMS; RCULIST_FOR_EACH_REVERSE_PROTECTED (elem, node, list) { ovs_assert (elem->value <= value); value = elem->value; } if (long_version) { struct element *next; RCULIST_FOR_EACH_SAFE_PROTECTED (elem, next, node, list) { rculist_remove(&elem->node); ovsrcu_postpone(free, elem); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } } else { RCULIST_FOR_EACH_SAFE_PROTECTED (elem, node, list) { rculist_remove(&elem->node); ovsrcu_postpone(free, elem); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } } } static void test_rculist_insert_delete(struct rculist *list) { test_rculist_insert_delete__(list, false); } static void test_rculist_insert_delete_long(struct rculist *list) { test_rculist_insert_delete__(list, true); } static void test_rculist_push_front_pop_back(struct rculist *list) { struct element *elem; for (int i = MAX_ELEMS - 1; i > 0; i--) { elem = xmalloc(sizeof *elem); elem->value = i; rculist_push_front(list, &elem->node); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } ovsrcu_quiesce(); while (!rculist_is_empty(list)) { elem = CONTAINER_OF(rculist_pop_back(list), struct element, node); ovsrcu_postpone(free, elem); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } } static void test_rculist_push_back_pop_front(struct rculist *list) { struct element *elem; for (int i = 0; i < MAX_ELEMS; i++) { elem = xmalloc(sizeof *elem); elem->value = i; rculist_push_back(list, &elem->node); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } ovsrcu_quiesce(); while (!rculist_is_empty(list)) { elem = CONTAINER_OF(rculist_pop_front(list), struct element, node); ovsrcu_postpone(free, elem); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } } static void test_rculist_splice(struct rculist *list) { struct element *elem; struct rculist other; rculist_init(&other); /* Insert elements in list by splicing an intermediate rculist. */ for (int i = 0; i < MAX_ELEMS; i++) { elem = xmalloc(sizeof *elem); elem->value = i; rculist_insert(&other, &elem->node); rculist_splice_hidden(list, rculist_next_protected(&other), &other); rculist_init(&other); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } ovsrcu_quiesce(); ovs_assert(rculist_size(list) == MAX_ELEMS); ovs_assert(rculist_is_empty(&other)); while (!rculist_is_empty(list)) { elem = CONTAINER_OF(rculist_pop_front(list), struct element, node); ovsrcu_postpone(free, elem); /* Leave some time for checkers to iterate through. */ do_usleep(random_range(1000)); } } static void test_rculist_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { run_test_while_checking(test_rculist_insert_delete); run_test_while_checking(test_rculist_insert_delete_long); run_test_while_checking(test_rculist_push_back_pop_front); run_test_while_checking(test_rculist_push_front_pop_back); run_test_while_checking(test_rculist_splice); printf("\n"); } OVSTEST_REGISTER("test-rculist", test_rculist_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-reconnect.c000066400000000000000000000220271514270232600232260ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2016 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "reconnect.h" #include #include #include #include #include "command-line.h" #include "compiler.h" #include "ovstest.h" #include "svec.h" #include "util.h" #include "openvswitch/vlog.h" static struct reconnect *reconnect; static int now; static void diff_stats(const struct reconnect_stats *old, const struct reconnect_stats *new, int delta); static const struct ovs_cmdl_command *get_all_commands(void); static void test_reconnect_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct reconnect_stats prev; unsigned int old_max_tries; int old_time; char line[128]; vlog_set_levels_from_string_assert("reconnect:off"); now = 1000; reconnect = reconnect_create(now); reconnect_set_name(reconnect, "remote"); reconnect_get_stats(reconnect, now, &prev); printf("### t=%d ###\n", now); old_time = now; old_max_tries = reconnect_get_max_tries(reconnect); while (fgets(line, sizeof line, stdin)) { struct reconnect_stats cur; struct svec args; fputs(line, stdout); if (line[0] == '#') { continue; } svec_init(&args); svec_parse_words(&args, line); svec_terminate(&args); if (!svec_is_empty(&args)) { struct ovs_cmdl_context ctx = { .argc = args.n, .argv = args.names, }; ovs_cmdl_run_command(&ctx, get_all_commands()); } svec_destroy(&args); if (old_time != now) { printf("\n### t=%d ###\n", now); } reconnect_get_stats(reconnect, now, &cur); diff_stats(&prev, &cur, now - old_time); prev = cur; if (reconnect_get_max_tries(reconnect) != old_max_tries) { old_max_tries = reconnect_get_max_tries(reconnect); printf(" %u tries left\n", old_max_tries); } old_time = now; } } static void do_enable(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_enable(reconnect, now); } static void do_disable(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_disable(reconnect, now); } static void do_force_reconnect(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_force_reconnect(reconnect, now); } static int error_from_string(const char *s) { if (!s) { return 0; } else if (!strcmp(s, "ECONNREFUSED")) { return ECONNREFUSED; } else if (!strcmp(s, "EOF")) { return EOF; } else { ovs_fatal(0, "unknown error '%s'", s); } } static void do_disconnected(struct ovs_cmdl_context *ctx) { reconnect_disconnected(reconnect, now, error_from_string(ctx->argv[1])); } static void do_connecting(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_connecting(reconnect, now); } static void do_connect_failed(struct ovs_cmdl_context *ctx) { reconnect_connect_failed(reconnect, now, error_from_string(ctx->argv[1])); } static void do_connected(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_connected(reconnect, now); } static void do_activity(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_activity(reconnect, now); } static void do_run(struct ovs_cmdl_context *ctx) { enum reconnect_action action; if (ctx->argc > 1) { now += atoi(ctx->argv[1]); } action = reconnect_run(reconnect, now); switch (action) { default: if (action != 0) { OVS_NOT_REACHED(); } break; case RECONNECT_CONNECT: printf(" should connect\n"); break; case RECONNECT_DISCONNECT: printf(" should disconnect\n"); break; case RECONNECT_PROBE: printf(" should send probe\n"); break; } } static void do_advance(struct ovs_cmdl_context *ctx) { now += atoi(ctx->argv[1]); } static void do_timeout(struct ovs_cmdl_context *ctx OVS_UNUSED) { int timeout = reconnect_timeout(reconnect, now); if (timeout >= 0) { printf(" advance %d ms\n", timeout); now += timeout; } else { printf(" no timeout\n"); } } static void do_set_max_tries(struct ovs_cmdl_context *ctx) { reconnect_set_max_tries(reconnect, atoi(ctx->argv[1])); } static void do_set_backoff_free_tries(struct ovs_cmdl_context *ctx) { reconnect_set_backoff_free_tries(reconnect, atoi(ctx->argv[1])); } static void diff_stats(const struct reconnect_stats *old, const struct reconnect_stats *new, int delta) { if (old->state != new->state || old->state_elapsed != new->state_elapsed || old->backoff != new->backoff) { printf(" in %s for %u ms (%d ms backoff)\n", new->state, new->state_elapsed, new->backoff); } if (old->creation_time != new->creation_time || old->last_activity != new->last_activity || old->last_connected != new->last_connected) { printf(" created %lld, last activity %lld, last connected %lld\n", new->creation_time, new->last_activity, new->last_connected); } if (old->n_successful_connections != new->n_successful_connections || old->n_attempted_connections != new->n_attempted_connections || old->seqno != new->seqno) { printf(" %u successful connections out of %u attempts, seqno %u\n", new->n_successful_connections, new->n_attempted_connections, new->seqno); } if (old->is_connected != new->is_connected) { printf(" %sconnected\n", new->is_connected ? "" : "dis"); } if (old->last_connected != new->last_connected || (old->msec_since_connect != new->msec_since_connect - delta && !(old->msec_since_connect == UINT_MAX && new->msec_since_connect == UINT_MAX)) || (old->total_connected_duration != new->total_connected_duration - delta && !(old->total_connected_duration == 0 && new->total_connected_duration == 0))) { printf(" last connected %u ms ago, connected %u ms total\n", new->msec_since_connect, new->total_connected_duration); } if (old->last_disconnected != new->last_disconnected || (old->msec_since_disconnect != new->msec_since_disconnect - delta && !(old->msec_since_disconnect == UINT_MAX && new->msec_since_disconnect == UINT_MAX))) { printf(" disconnected at %llu ms (%u ms ago)\n", new->last_disconnected, new->msec_since_disconnect); } } static void do_set_passive(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_set_passive(reconnect, true, now); } static void do_listening(struct ovs_cmdl_context *ctx OVS_UNUSED) { reconnect_listening(reconnect, now); } static void do_listen_error(struct ovs_cmdl_context *ctx) { reconnect_listen_error(reconnect, now, atoi(ctx->argv[1])); } static void do_receive_attempted(struct ovs_cmdl_context *ctx OVS_UNUSED) { if (!strcmp(ctx->argv[1], "now")) { reconnect_receive_attempted(reconnect, now); } else if (!strcmp(ctx->argv[1], "LLONG_MAX")) { reconnect_receive_attempted(reconnect, LLONG_MAX); } else { ovs_fatal(0, "%s: bad argument %s", ctx->argv[0], ctx->argv[1]); } } static const struct ovs_cmdl_command all_commands[] = { { "enable", NULL, 0, 0, do_enable, OVS_RO }, { "disable", NULL, 0, 0, do_disable, OVS_RO }, { "force-reconnect", NULL, 0, 0, do_force_reconnect, OVS_RO }, { "disconnected", NULL, 0, 1, do_disconnected, OVS_RO }, { "connecting", NULL, 0, 0, do_connecting, OVS_RO }, { "connect-failed", NULL, 0, 1, do_connect_failed, OVS_RO }, { "connected", NULL, 0, 0, do_connected, OVS_RO }, { "activity", NULL, 0, 0, do_activity, OVS_RO }, { "run", NULL, 0, 1, do_run, OVS_RO }, { "advance", NULL, 1, 1, do_advance, OVS_RO }, { "timeout", NULL, 0, 0, do_timeout, OVS_RO }, { "set-max-tries", NULL, 1, 1, do_set_max_tries, OVS_RO }, { "set-backoff-free-tries", NULL, 1, 1, do_set_backoff_free_tries, OVS_RO }, { "passive", NULL, 0, 0, do_set_passive, OVS_RO }, { "listening", NULL, 0, 0, do_listening, OVS_RO }, { "listen-error", NULL, 1, 1, do_listen_error, OVS_RO }, { "receive-attempted", NULL, 1, 1, do_receive_attempted, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static const struct ovs_cmdl_command * get_all_commands(void) { return all_commands; } OVSTEST_REGISTER("test-reconnect", test_reconnect_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-reconnect.py000066400000000000000000000140251514270232600234330ustar00rootroot00000000000000# Copyright (c) 2009, 2010, 2012 Nicira, Inc. # # 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. import errno import sys import ovs.reconnect now = 0 r = None def do_enable(_): r.enable(now) def do_disable(_): r.disable(now) def do_force_reconnect(_): r.force_reconnect(now) def error_from_string(s): if not s: return 0 elif s == "ECONNREFUSED": return errno.ECONNREFUSED elif s == "EOF": return ovs.reconnect.EOF else: sys.stderr.write("unknown error '%s'\n" % s) sys.exit(1) def do_disconnected(arg): r.disconnected(now, error_from_string(arg)) def do_connecting(_): r.connecting(now) def do_connect_failed(arg): r.connect_failed(now, error_from_string(arg)) def do_connected(_): r.connected(now) def do_activity(_): r.activity(now) def do_run(arg): global now if arg is not None: now += int(arg) action = r.run(now) if action is None: pass elif action == ovs.reconnect.CONNECT: print(" should connect") elif action == ovs.reconnect.DISCONNECT: print(" should disconnect") elif action == ovs.reconnect.PROBE: print(" should send probe") else: assert False def do_advance(arg): global now now += int(arg) def do_timeout(_): global now timeout = r.timeout(now) if timeout is not None and timeout >= 0: print(" advance %d ms" % timeout) now += timeout else: print(" no timeout") def do_set_max_tries(arg): r.set_max_tries(int(arg)) def do_set_backoff_free_tries(arg): r.set_backoff_free_tries(int(arg)) def diff_stats(old, new, delta): if (old.state != new.state or old.state_elapsed != new.state_elapsed or old.backoff != new.backoff): print(" in %s for %d ms (%d ms backoff)" % (new.state, new.state_elapsed, new.backoff)) if (old.creation_time != new.creation_time or old.last_activity != new.last_activity or old.last_connected != new.last_connected): print(" created %d, last activity %d, last connected %d" % (new.creation_time, new.last_activity, new.last_connected)) if (old.n_successful_connections != new.n_successful_connections or old.n_attempted_connections != new.n_attempted_connections or old.seqno != new.seqno): print(" %d successful connections out of %d attempts, seqno %d" % (new.n_successful_connections, new.n_attempted_connections, new.seqno)) if (old.is_connected != new.is_connected): if new.is_connected: negate = "" else: negate = "dis" print(" %sconnected" % negate) if (old.last_connected != new.last_connected or (new.msec_since_connect is not None and old.msec_since_connect != new.msec_since_connect - delta) or (old.total_connected_duration != new.total_connected_duration - delta and not (old.total_connected_duration == 0 and new.total_connected_duration == 0))): print(" last connected %d ms ago, connected %d ms total" % (new.msec_since_connect, new.total_connected_duration)) if (old.last_disconnected != new.last_disconnected or (new.msec_since_disconnect is not None and old.msec_since_disconnect != new.msec_since_disconnect - delta)): print(" disconnected at %d ms (%d ms ago)" % (new.last_disconnected, new.msec_since_disconnect)) def do_set_passive(_): r.set_passive(True, now) def do_listening(_): r.listening(now) def do_listen_error(arg): r.listen_error(now, int(arg)) def do_receive_attempted(arg): if arg == "now": r.receive_attempted(now) elif arg == "LLONG_MAX": r.receive_attempted(None) else: sys.stderr.write("receive-attempted: bad argument %s\n" % arg) sys.exit(1) def main(): commands = { "enable": do_enable, "disable": do_disable, "force-reconnect": do_force_reconnect, "disconnected": do_disconnected, "connecting": do_connecting, "connect-failed": do_connect_failed, "connected": do_connected, "activity": do_activity, "run": do_run, "advance": do_advance, "timeout": do_timeout, "set-max-tries": do_set_max_tries, "set-backoff-free-tries": do_set_backoff_free_tries, "passive": do_set_passive, "listening": do_listening, "listen-error": do_listen_error, "receive-attempted": do_receive_attempted } global now global r now = 1000 r = ovs.reconnect.Reconnect(now) r.set_name("remote") prev = r.get_stats(now) print("### t=%d ###" % now) old_time = now old_max_tries = r.get_max_tries() while True: line = sys.stdin.readline() if line == "": break print(line[:-1]) if line[0] == "#": continue args = line.split() if len(args) == 0: continue command = args[0] if len(args) > 1: op = args[1] else: op = None commands[command](op) if old_time != now: print() print("### t=%d ###" % now) cur = r.get_stats(now) diff_stats(prev, cur, now - old_time) prev = cur if r.get_max_tries() != old_max_tries: old_max_tries = r.get_max_tries() print(" %d tries left" % old_max_tries) old_time = now if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-rstp.c000066400000000000000000000472301514270232600222410ustar00rootroot00000000000000#include #undef NDEBUG #include "rstp-common.h" #include #include #include #include #include #include #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "dp-packet.h" #include "packets.h" #include "openvswitch/vlog.h" #define MAX_PORTS 10 struct bpdu { int port_no; void *data; size_t size; }; struct bridge { struct test_case *tc; int id; bool reached; struct rstp *rstp; struct lan *ports[RSTP_MAX_PORTS]; int n_ports; int n_active_ports; #define RXQ_SIZE 16 struct bpdu rxq[RXQ_SIZE]; int rxq_head, rxq_tail; }; struct lan_conn { struct bridge *bridge; int port_no; }; struct lan { struct test_case *tc; const char *name; bool reached; struct lan_conn conns[16]; int n_conns; }; struct test_case { struct bridge *bridges[16]; int n_bridges; struct lan *lans[26]; int n_lans; }; static const char *file_name; static int line_number; static char line[128]; static char *pos, *token; static int n_warnings; static struct test_case * new_test_case(void) { struct test_case *tc = xmalloc(sizeof *tc); tc->n_bridges = 0; tc->n_lans = 0; return tc; } /* This callback is called with rstp_mutex held. */ static void send_bpdu(struct dp_packet *pkt, void *port_, void *b_) OVS_REQUIRES(rstp_mutex) { struct bridge *b = b_; struct lan *lan; const struct rstp_port *port = port_; uint16_t port_no = port->port_number; assert(port_no < b->n_ports); lan = b->ports[port_no]; if (lan) { const void *data = dp_packet_l3(pkt); size_t size = (char *) dp_packet_tail(pkt) - (char *) data; int i; for (i = 0; i < lan->n_conns; i++) { struct lan_conn *conn = &lan->conns[i]; if (conn->bridge != b || conn->port_no != port_no) { struct bridge *dst = conn->bridge; struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE]; assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE); bpdu->data = xmemdup(data, size); bpdu->size = size; bpdu->port_no = conn->port_no; } } } dp_packet_delete(pkt); } #define RSTP_PORT_PATH_COST_100M 200000 static struct bridge * new_bridge(struct test_case *tc, int id) { struct bridge *b = xmalloc(sizeof *b); char name[16]; struct rstp_port *p; int i; b->tc = tc; b->id = id; snprintf(name, sizeof name, "rstp%x", id); b->rstp = rstp_create(name, id, send_bpdu, b); for (i = 1; i < MAX_PORTS; i++) { p = rstp_add_port(b->rstp); rstp_port_set_aux(p, p); rstp_port_set_path_cost(p, RSTP_PORT_PATH_COST_100M); rstp_port_set_state(p, RSTP_DISABLED); rstp_port_set_mac_operational(p, true); } assert(tc->n_bridges < ARRAY_SIZE(tc->bridges)); b->n_ports = 1; b->n_active_ports = 1; b->rxq_head = b->rxq_tail = 0; tc->bridges[tc->n_bridges++] = b; return b; } static struct lan * new_lan(struct test_case *tc, const char *name) { struct lan *lan = xmalloc(sizeof *lan); lan->tc = tc; lan->name = xstrdup(name); lan->n_conns = 0; assert(tc->n_lans < ARRAY_SIZE(tc->lans)); tc->lans[tc->n_lans++] = lan; return lan; } static void reconnect_port(struct bridge *b, int port_no, struct lan *new_lan) { struct lan *old_lan; int j; assert(port_no < b->n_ports); old_lan = b->ports[port_no]; if (old_lan == new_lan) { return; } /* Disconnect from old_lan. */ if (old_lan) { for (j = 0; j < old_lan->n_conns; j++) { struct lan_conn *c = &old_lan->conns[j]; if (c->bridge == b && c->port_no == port_no) { memmove(c, c + 1, sizeof *c * (old_lan->n_conns - j - 1)); old_lan->n_conns--; break; } } } /* Connect to new_lan. */ b->ports[port_no] = new_lan; if (new_lan) { int conn_no = new_lan->n_conns++; assert(conn_no < ARRAY_SIZE(new_lan->conns)); new_lan->conns[conn_no].bridge = b; new_lan->conns[conn_no].port_no = port_no; } } static void new_port(struct bridge *b, struct lan *lan, uint32_t path_cost) { int port_no = b->n_ports++; struct rstp_port *p = rstp_get_port(b->rstp, port_no); assert(port_no < ARRAY_SIZE(b->ports)); b->ports[port_no] = NULL; /* Enable port. */ reinitialize_port(p); rstp_port_set_path_cost(p, path_cost); rstp_port_set_state(p, RSTP_DISCARDING); rstp_port_set_mac_operational(p, true); reconnect_port(b, port_no, lan); } static void dump(struct test_case *tc) { int i; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; struct rstp *rstp = b->rstp; int j; printf("%s:", rstp_get_name(rstp)); if (rstp_is_root_bridge(rstp)) { printf(" root"); } printf("\n"); for (j = 0; j < b->n_ports; j++) { struct rstp_port *p = rstp_get_port(rstp, j); enum rstp_state state = rstp_port_get_state(p); printf("\tport %d", j); if (b->ports[j]) { printf(" (lan %s)", b->ports[j]->name); } else { printf(" (disconnected)"); } printf(": %s", rstp_state_name(state)); if (p == rstp_get_root_port(rstp)) { printf(" (root port, root_path_cost=%u)", rstp_get_root_path_cost(rstp)); } printf("\n"); } } } static void dump_lan_tree(struct test_case *, struct lan *, int level); static void dump_bridge_tree(struct test_case *tc, struct bridge *b, int level) { int i; if (b->reached) { return; } b->reached = true; for (i = 0; i < level; i++) { printf("\t"); } printf("%s\n", rstp_get_name(b->rstp)); for (i = 0; i < b->n_ports; i++) { struct lan *lan = b->ports[i]; struct rstp_port *p = rstp_get_port(b->rstp, i); if (rstp_port_get_state(p) == RSTP_FORWARDING && lan) { dump_lan_tree(tc, lan, level + 1); } } } static void dump_lan_tree(struct test_case *tc, struct lan *lan, int level) { int i; if (lan->reached) { return; } lan->reached = true; for (i = 0; i < level; i++) { printf("\t"); } printf("%s\n", lan->name); for (i = 0; i < lan->n_conns; i++) { struct bridge *b = lan->conns[i].bridge; dump_bridge_tree(tc, b, level + 1); } } static void tree(struct test_case *tc) { int i; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; b->reached = false; } for (i = 0; i < tc->n_lans; i++) { struct lan *lan = tc->lans[i]; lan->reached = false; } for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; struct rstp *rstp = b->rstp; if (rstp_is_root_bridge(rstp)) { dump_bridge_tree(tc, b, 0); } } } static void simulate(struct test_case *tc, int granularity) { int time, i, round_trips; for (time = 0; time < 1000 * 180; time += granularity) { for (i = 0; i < tc->n_bridges; i++) { rstp_tick_timers(tc->bridges[i]->rstp); } for (round_trips = 0; round_trips < granularity; round_trips++) { bool any = false; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; for (; b->rxq_tail != b->rxq_head; b->rxq_tail++) { struct bpdu *bpdu = &b->rxq[b->rxq_tail % RXQ_SIZE]; rstp_port_received_bpdu(rstp_get_port(b->rstp, bpdu->port_no), bpdu->data, bpdu->size); free(bpdu->data); any = true; } } if (!any) { break; } } } } OVS_NO_RETURN static void err(const char *message, ...) OVS_PRINTF_FORMAT(1, 2); static void err(const char *message, ...) { va_list args; fprintf(stderr, "%s:%d:%"PRIdPTR": ", file_name, line_number, pos - line); va_start(args, message); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); exit(EXIT_FAILURE); } static void warn(const char *message, ...) OVS_PRINTF_FORMAT(1, 2); static void warn(const char *message, ...) { va_list args; fprintf(stderr, "%s:%d: ", file_name, line_number); va_start(args, message); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); n_warnings++; } static bool get_token(void) { char *start; while (isspace((unsigned char) *pos)) { pos++; } if (*pos == '\0') { free(token); token = NULL; return false; } start = pos; if (isalpha((unsigned char) *pos)) { while (isalpha((unsigned char) *++pos)) { continue; } } else if (isdigit((unsigned char) *pos)) { if (*pos == '0' && (pos[1] == 'x' || pos[1] == 'X')) { pos += 2; while (isxdigit((unsigned char) *pos)) { pos++; } } else { while (isdigit((unsigned char) *++pos)) { continue; } } } else { pos++; } free(token); token = xmemdup0(start, pos - start); return true; } static bool get_int(int *intp) { char *save_pos = pos; if (token && isdigit((unsigned char) *token)) { *intp = strtol(token, NULL, 0); get_token(); return true; } else { pos = save_pos; return false; } } static bool match(const char *want) { if (token && !strcmp(want, token)) { get_token(); return true; } else { return false; } } static int must_get_int(void) { int x; if (!get_int(&x)) { err("expected integer"); } return x; } static void must_match(const char *want) { if (!match(want)) { err("expected \"%s\"", want); } } static void test_rstp_main(int argc, char *argv[]) { struct test_case *tc; FILE *input_file; int i; vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m"); vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF); rstp_init(); if (argc != 2) { ovs_fatal(0, "usage: test-rstp INPUT.RSTP"); } file_name = argv[1]; input_file = fopen(file_name, "r"); if (!input_file) { ovs_fatal(errno, "error opening \"%s\"", file_name); } tc = new_test_case(); for (i = 0; i < 26; i++) { char name[2]; name[0] = 'a' + i; name[1] = '\0'; new_lan(tc, name); } for (line_number = 1; fgets(line, sizeof line, input_file); line_number++) { char *newline, *hash; newline = strchr(line, '\n'); if (newline) { *newline = '\0'; } hash = strchr(line, '#'); if (hash) { *hash = '\0'; } pos = line; if (!get_token()) { continue; } if (match("bridge")) { struct bridge *bridge; int bridge_no, port_no; bridge_no = must_get_int(); if (bridge_no < tc->n_bridges) { bridge = tc->bridges[bridge_no]; } else if (bridge_no == tc->n_bridges) { bridge = new_bridge(tc, must_get_int()); } else { err("bridges must be numbered consecutively from 0"); } if (match("^")) { rstp_set_bridge_priority(bridge->rstp, must_get_int()); } if (match("=")) { for (port_no = 1; port_no < MAX_PORTS; port_no++) { struct rstp_port *p = rstp_get_port(bridge->rstp, port_no); if (!token || match("X")) { /* Disable port. */ reinitialize_port(p); rstp_port_set_state(p, RSTP_DISABLED); rstp_port_set_mac_operational(p, false); } else if (match("_")) { /* Nothing to do. */ } else { struct lan *lan; uint32_t path_cost; if (!strcmp(token, "0")) { lan = NULL; } else if (strlen(token) == 1 && islower((unsigned char)*token)) { lan = tc->lans[*token - 'a']; } else { err("%s is not a valid LAN name " "(0 or a lowercase letter)", token); } get_token(); path_cost = match(":") ? must_get_int() : RSTP_PORT_PATH_COST_100M; if (port_no < bridge->n_ports) { /* Enable port. */ reinitialize_port(p); rstp_port_set_path_cost(p, path_cost); rstp_port_set_state(p, RSTP_DISCARDING); rstp_port_set_mac_operational(p, true); reconnect_port(bridge, port_no, lan); } else if (port_no == bridge->n_ports) { new_port(bridge, lan, path_cost); bridge->n_active_ports++; } else { err("ports must be numbered consecutively"); } if (match("^")) { rstp_port_set_priority(p, must_get_int()); } } } } } else if (match("run")) { simulate(tc, must_get_int()); } else if (match("dump")) { dump(tc); } else if (match("tree")) { tree(tc); } else if (match("check")) { struct bridge *b; struct rstp *rstp; int bridge_no, port_no; uint32_t cost_value; bridge_no = must_get_int(); if (bridge_no >= tc->n_bridges) { err("no bridge numbered %d", bridge_no); } b = tc->bridges[bridge_no]; rstp = b->rstp; must_match("="); if (match("rootid")) { uint64_t rootid; must_match(":"); rootid = must_get_int(); if (match("^")) { rootid |= (uint64_t) must_get_int() << 48; } else { rootid |= UINT64_C(0x8000) << 48; } if (rstp_get_designated_root(rstp) != rootid) { warn("%s: root "RSTP_ID_FMT", not %"PRIx64, rstp_get_name(rstp), RSTP_ID_ARGS(rstp_get_designated_root(rstp)), rootid); } } cost_value = rstp_get_root_path_cost(rstp); if (match("root")) { if (cost_value != 0) { warn("%s: root path cost of root is %d instead of 0 \n", rstp_get_name(rstp), cost_value); } if (!rstp_is_root_bridge(rstp)) { warn("%s: root is "RSTP_ID_FMT", not "RSTP_ID_FMT"", rstp_get_name(rstp), RSTP_ID_ARGS(rstp_get_designated_root(rstp)), RSTP_ID_ARGS(rstp_get_bridge_id(rstp))); } for (port_no = 1; port_no < b->n_active_ports; port_no++) { struct rstp_port *p = rstp_get_port(rstp, port_no); enum rstp_state state = rstp_port_get_state(p); if (state != RSTP_DISABLED && state != RSTP_FORWARDING) { warn("%s: root port %d in state %s", rstp_get_name(b->rstp), port_no, rstp_state_name(state)); } } } else { for (port_no = 1; port_no < b->n_active_ports; port_no++) { struct rstp_port *p = rstp_get_port(rstp, port_no); enum rstp_state state; if (token == NULL || match("D")) { state = RSTP_DISABLED; } else if (match("Di")) { state = RSTP_DISCARDING; } else if (match("Le")) { state = RSTP_LEARNING; } else if (match("F")) { state = RSTP_FORWARDING; } else if (match("_")) { continue; } else { err("unknown port state %s", token); } if (rstp_port_get_state(p) != state) { warn("%s port %d: state is %s but should be %s", rstp_get_name(rstp), port_no, rstp_state_name(rstp_port_get_state(p)), rstp_state_name(state)); } if (state == RSTP_FORWARDING) { struct rstp_port *root_port = rstp_get_root_port(rstp); if (match(":")) { int root_path_cost = must_get_int(); if (p != root_port) { warn("%s: port %d is not the root port", rstp_get_name(rstp), port_no); if (!root_port) { warn("%s: (there is no root port)", rstp_get_name(rstp)); } else { warn("%s: (port %d is the root port)", rstp_get_name(rstp), rstp_port_get_number(root_port)); } } else if (cost_value != root_path_cost) { warn("%s: root path cost is %d, should be %d", rstp_get_name(rstp), cost_value, root_path_cost); } } else if (p == root_port) { warn("%s: port %d is the root port but " "not expected to be", rstp_get_name(rstp), port_no); } } } } if (n_warnings) { printf("failing because of %d warnings\n", n_warnings); exit(EXIT_FAILURE); } } if (get_token()) { printf("failing because of errors\n"); err("trailing garbage on line"); } } free(token); fclose(input_file); for (i = 0; i < tc->n_lans; i++) { struct lan *lan = tc->lans[i]; free(CONST_CAST(char *, lan->name)); free(lan); } for (i = 0; i < tc->n_bridges; i++) { struct bridge *bridge = tc->bridges[i]; int j; for (j = 1; j < MAX_PORTS; j++) { rstp_port_unref(rstp_get_port(bridge->rstp, j)); } rstp_unref(bridge->rstp); free(bridge); } free(tc); } OVSTEST_REGISTER("test-rstp", test_rstp_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-sflow.c000066400000000000000000000613341514270232600224040ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * Copyright (c) 2013 InMon Corp. * * 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. */ #include #undef NDEBUG #include "netflow.h" #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "daemon.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "packets.h" #include "openvswitch/poll-loop.h" #include "socket-util.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static unixctl_cb_func test_sflow_exit; /* Datagram. */ #define SFLOW_VERSION_5 5 #define SFLOW_MIN_LEN 36 /* Sample tag numbers. */ #define SFLOW_FLOW_SAMPLE 1 #define SFLOW_COUNTERS_SAMPLE 2 #define SFLOW_FLOW_SAMPLE_EXPANDED 3 #define SFLOW_COUNTERS_SAMPLE_EXPANDED 4 /* Structure element tag numbers. */ #define SFLOW_TAG_CTR_IFCOUNTERS 1 #define SFLOW_TAG_CTR_ETHCOUNTERS 2 #define SFLOW_TAG_CTR_LACPCOUNTERS 7 #define SFLOW_TAG_CTR_OPENFLOWPORT 1004 #define SFLOW_TAG_CTR_PORTNAME 1005 #define SFLOW_TAG_PKT_HEADER 1 #define SFLOW_TAG_PKT_SWITCH 1001 #define SFLOW_TAG_PKT_TUNNEL4_OUT 1023 #define SFLOW_TAG_PKT_TUNNEL4_IN 1024 #define SFLOW_TAG_PKT_TUNNEL_VNI_OUT 1029 #define SFLOW_TAG_PKT_TUNNEL_VNI_IN 1030 #define SFLOW_TAG_PKT_MPLS 1006 /* string sizes */ #define SFL_MAX_PORTNAME_LEN 255 struct sflow_addr { enum { SFLOW_ADDRTYPE_undefined = 0, SFLOW_ADDRTYPE_IP4, SFLOW_ADDRTYPE_IP6 } type; union { ovs_be32 ip4; ovs_be32 ip6[4]; } a; }; struct sflow_xdr { /* Exceptions. */ jmp_buf env; int errline; /* Cursor. */ ovs_be32 *datap; uint32_t i; uint32_t quads; /* Agent. */ struct sflow_addr agentAddr; char agentIPStr[INET6_ADDRSTRLEN + 2]; uint32_t subAgentId; uint32_t uptime_mS; /* Datasource. */ uint32_t dsClass; uint32_t dsIndex; /* Sequence numbers. */ uint32_t dgramSeqNo; uint32_t fsSeqNo; uint32_t csSeqNo; /* Structure offsets. */ struct { uint32_t HEADER; uint32_t SWITCH; uint32_t TUNNEL4_OUT; uint32_t TUNNEL4_IN; uint32_t TUNNEL_VNI_OUT; uint32_t TUNNEL_VNI_IN; uint32_t MPLS; uint32_t IFCOUNTERS; uint32_t ETHCOUNTERS; uint32_t LACPCOUNTERS; uint32_t OPENFLOWPORT; uint32_t PORTNAME; } offset; /* Flow sample fields. */ uint32_t meanSkipCount; uint32_t samplePool; uint32_t dropEvents; uint32_t inputPortFormat; uint32_t inputPort; uint32_t outputPortFormat; uint32_t outputPort; }; #define SFLOWXDR_try(x) ((x->errline = setjmp(x->env)) == 0) #define SFLOWXDR_throw(x) longjmp(x->env, __LINE__) #define SFLOWXDR_assert(x, t) if (!(t)) SFLOWXDR_throw(x) static void sflowxdr_init(struct sflow_xdr *x, void *buf, size_t len) { x->datap = buf; x->quads = len >> 2; } static uint32_t sflowxdr_next(struct sflow_xdr *x) { return ntohl(x->datap[x->i++]); } static ovs_be32 sflowxdr_next_n(struct sflow_xdr *x) { return x->datap[x->i++]; } static bool sflowxdr_more(const struct sflow_xdr *x, uint32_t q) { return q + x->i <= x->quads; } static void sflowxdr_skip(struct sflow_xdr *x, uint32_t q) { x->i += q; } static uint32_t sflowxdr_mark(const struct sflow_xdr *x, uint32_t q) { return x->i + q; } static bool sflowxdr_mark_ok(const struct sflow_xdr *x, uint32_t m) { return m == x->i; } static void sflowxdr_mark_unique(struct sflow_xdr *x, uint32_t *pi) { if (*pi) { SFLOWXDR_throw(x); } *pi = x->i; } static void sflowxdr_setc(struct sflow_xdr *x, uint32_t j) { x->i = j; } static const char * sflowxdr_str(const struct sflow_xdr *x) { return (const char *) (x->datap + x->i); } static uint64_t sflowxdr_next_int64(struct sflow_xdr *x) { uint64_t scratch; scratch = sflowxdr_next(x); scratch <<= 32; scratch += sflowxdr_next(x); return scratch; } static void process_counter_sample(struct sflow_xdr *x) { if (x->offset.IFCOUNTERS) { sflowxdr_setc(x, x->offset.IFCOUNTERS); printf("IFCOUNTERS"); printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo); printf(" ds=%s>%"PRIu32":%"PRIu32, x->agentIPStr, x->dsClass, x->dsIndex); printf(" csSeqNo=%"PRIu32, x->csSeqNo); printf(" ifindex=%"PRIu32, sflowxdr_next(x)); printf(" type=%"PRIu32, sflowxdr_next(x)); printf(" ifspeed=%"PRIu64, sflowxdr_next_int64(x)); printf(" direction=%"PRIu32, sflowxdr_next(x)); printf(" status=%"PRIu32, sflowxdr_next(x)); printf(" in_octets=%"PRIu64, sflowxdr_next_int64(x)); printf(" in_unicasts=%"PRIu32, sflowxdr_next(x)); printf(" in_multicasts=%"PRIu32, sflowxdr_next(x)); printf(" in_broadcasts=%"PRIu32, sflowxdr_next(x)); printf(" in_discards=%"PRIu32, sflowxdr_next(x)); printf(" in_errors=%"PRIu32, sflowxdr_next(x)); printf(" in_unknownprotos=%"PRIu32, sflowxdr_next(x)); printf(" out_octets=%"PRIu64, sflowxdr_next_int64(x)); printf(" out_unicasts=%"PRIu32, sflowxdr_next(x)); printf(" out_multicasts=%"PRIu32, sflowxdr_next(x)); printf(" out_broadcasts=%"PRIu32, sflowxdr_next(x)); printf(" out_discards=%"PRIu32, sflowxdr_next(x)); printf(" out_errors=%"PRIu32, sflowxdr_next(x)); printf(" promiscuous=%"PRIu32, sflowxdr_next(x)); printf("\n"); } if (x->offset.LACPCOUNTERS) { struct eth_addr *mac; union { ovs_be32 all; struct { uint8_t actorAdmin; uint8_t actorOper; uint8_t partnerAdmin; uint8_t partnerOper; } v; } state; sflowxdr_setc(x, x->offset.LACPCOUNTERS); printf("LACPCOUNTERS"); mac = (void *)sflowxdr_str(x); printf(" sysID="ETH_ADDR_FMT, ETH_ADDR_ARGS(*mac)); sflowxdr_skip(x, 2); mac = (void *)sflowxdr_str(x); printf(" partnerID="ETH_ADDR_FMT, ETH_ADDR_ARGS(*mac)); sflowxdr_skip(x, 2); printf(" aggID=%"PRIu32, sflowxdr_next(x)); state.all = sflowxdr_next_n(x); printf(" actorAdmin=0x%"PRIx32, state.v.actorAdmin); printf(" actorOper=0x%"PRIx32, state.v.actorOper); printf(" partnerAdmin=0x%"PRIx32, state.v.partnerAdmin); printf(" partnerOper=0x%"PRIx32, state.v.partnerOper); printf(" LACPDUsRx=%"PRIu32, sflowxdr_next(x)); printf(" markerPDUsRx=%"PRIu32, sflowxdr_next(x)); printf(" markerRespPDUsRx=%"PRIu32, sflowxdr_next(x)); printf(" unknownRx=%"PRIu32, sflowxdr_next(x)); printf(" illegalRx=%"PRIu32, sflowxdr_next(x)); printf(" LACPDUsTx=%"PRIu32, sflowxdr_next(x)); printf(" markerPDUsTx=%"PRIu32, sflowxdr_next(x)); printf(" markerRespPDUsTx=%"PRIu32, sflowxdr_next(x)); printf("\n"); } if (x->offset.OPENFLOWPORT) { sflowxdr_setc(x, x->offset.OPENFLOWPORT); printf("OPENFLOWPORT"); printf(" datapath_id=%"PRIu64, sflowxdr_next_int64(x)); printf(" port_no=%"PRIu32, sflowxdr_next(x)); printf("\n"); } if (x->offset.PORTNAME) { uint32_t pnLen; const char *pnBytes; char portName[SFL_MAX_PORTNAME_LEN + 1]; sflowxdr_setc(x, x->offset.PORTNAME); printf("PORTNAME"); pnLen = sflowxdr_next(x); SFLOWXDR_assert(x, (pnLen <= SFL_MAX_PORTNAME_LEN)); pnBytes = sflowxdr_str(x); memcpy(portName, pnBytes, pnLen); portName[pnLen] = '\0'; printf(" portName=%s", portName); printf("\n"); } if (x->offset.ETHCOUNTERS) { sflowxdr_setc(x, x->offset.ETHCOUNTERS); printf("ETHCOUNTERS"); printf(" dot3StatsAlignmentErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsFCSErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsSingleCollisionFrames=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsMultipleCollisionFrames=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsSQETestErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsDeferredTransmissions=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsLateCollisions=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsExcessiveCollisions=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsInternalMacTransmitErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsCarrierSenseErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsFrameTooLongs=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsInternalMacReceiveErrors=%"PRIu32, sflowxdr_next(x)); printf(" dot3StatsSymbolErrors=%"PRIu32, sflowxdr_next(x)); printf("\n"); } } static char bin_to_hex(int hexit) { return "0123456789ABCDEF"[hexit]; } static int print_hex(const char *a, int len, char *buf, int bufLen) { unsigned char nextByte; int b = 0; int i; for (i = 0; i < len; i++) { if (b > bufLen - 10) { break; } nextByte = a[i]; buf[b++] = bin_to_hex(nextByte >> 4); buf[b++] = bin_to_hex(nextByte & 0x0f); if (i < len - 1) { buf[b++] = '-'; } } buf[b] = '\0'; return b; } static void print_struct_ipv4(struct sflow_xdr *x, const char *prefix) { ovs_be32 src, dst; printf(" %s_length=%"PRIu32, prefix, sflowxdr_next(x)); printf(" %s_protocol=%"PRIu32, prefix, sflowxdr_next(x)); src = sflowxdr_next_n(x); dst = sflowxdr_next_n(x); printf(" %s_src="IP_FMT, prefix, IP_ARGS(src)); printf(" %s_dst="IP_FMT, prefix, IP_ARGS(dst)); printf(" %s_src_port=%"PRIu32, prefix, sflowxdr_next(x)); printf(" %s_dst_port=%"PRIu32, prefix, sflowxdr_next(x)); printf(" %s_tcp_flags=%"PRIu32, prefix, sflowxdr_next(x)); printf(" %s_tos=%"PRIu32, prefix, sflowxdr_next(x)); } #define SFLOW_HEX_SCRATCH 1024 static void process_flow_sample(struct sflow_xdr *x) { if (x->offset.HEADER) { uint32_t headerLen; char scratch[SFLOW_HEX_SCRATCH]; printf("HEADER"); printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo); printf(" ds=%s>%"PRIu32":%"PRIu32, x->agentIPStr, x->dsClass, x->dsIndex); printf(" fsSeqNo=%"PRIu32, x->fsSeqNo); if (x->offset.TUNNEL4_IN) { sflowxdr_setc(x, x->offset.TUNNEL4_IN); print_struct_ipv4(x, "tunnel4_in"); } if (x->offset.TUNNEL4_OUT) { sflowxdr_setc(x, x->offset.TUNNEL4_OUT); print_struct_ipv4(x, "tunnel4_out"); } if (x->offset.TUNNEL_VNI_IN) { sflowxdr_setc(x, x->offset.TUNNEL_VNI_IN); printf( " tunnel_in_vni=%"PRIu32, sflowxdr_next(x)); } if (x->offset.TUNNEL_VNI_OUT) { sflowxdr_setc(x, x->offset.TUNNEL_VNI_OUT); printf( " tunnel_out_vni=%"PRIu32, sflowxdr_next(x)); } if (x->offset.MPLS) { uint32_t addr_type, stack_depth, ii; ovs_be32 mpls_lse; sflowxdr_setc(x, x->offset.MPLS); /* OVS only sets the out_stack. The rest will be blank. */ /* skip next hop address */ addr_type = sflowxdr_next(x); sflowxdr_skip(x, addr_type == SFLOW_ADDRTYPE_IP6 ? 4 : 1); /* skip in_stack */ stack_depth = sflowxdr_next(x); sflowxdr_skip(x, stack_depth); /* print out_stack */ stack_depth = sflowxdr_next(x); for(ii = 0; ii < stack_depth; ii++) { mpls_lse=sflowxdr_next_n(x); printf(" mpls_label_%"PRIu32"=%"PRIu32, ii, mpls_lse_to_label(mpls_lse)); printf(" mpls_tc_%"PRIu32"=%"PRIu32, ii, mpls_lse_to_tc(mpls_lse)); printf(" mpls_ttl_%"PRIu32"=%"PRIu32, ii, mpls_lse_to_ttl(mpls_lse)); printf(" mpls_bos_%"PRIu32"=%"PRIu32, ii, mpls_lse_to_bos(mpls_lse)); } } if (x->offset.SWITCH) { sflowxdr_setc(x, x->offset.SWITCH); printf(" in_vlan=%"PRIu32, sflowxdr_next(x)); printf(" in_priority=%"PRIu32, sflowxdr_next(x)); printf(" out_vlan=%"PRIu32, sflowxdr_next(x)); printf(" out_priority=%"PRIu32, sflowxdr_next(x)); } sflowxdr_setc(x, x->offset.HEADER); printf(" meanSkip=%"PRIu32, x->meanSkipCount); printf(" samplePool=%"PRIu32, x->samplePool); printf(" dropEvents=%"PRIu32, x->dropEvents); printf(" in_ifindex=%"PRIu32, x->inputPort); printf(" in_format=%"PRIu32, x->inputPortFormat); printf(" out_ifindex=%"PRIu32, x->outputPort); printf(" out_format=%"PRIu32, x->outputPortFormat); printf(" hdr_prot=%"PRIu32, sflowxdr_next(x)); printf(" pkt_len=%"PRIu32, sflowxdr_next(x)); printf(" stripped=%"PRIu32, sflowxdr_next(x)); headerLen = sflowxdr_next(x); printf(" hdr_len=%"PRIu32, headerLen); print_hex(sflowxdr_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH); printf(" hdr=%s", scratch); printf("\n"); } } static void process_datagram(struct sflow_xdr *x) { uint32_t samples, s; SFLOWXDR_assert(x, (sflowxdr_next(x) == SFLOW_VERSION_5)); /* Read the sFlow header. */ x->agentAddr.type = sflowxdr_next(x); switch (x->agentAddr.type) { case SFLOW_ADDRTYPE_IP4: x->agentAddr.a.ip4 = sflowxdr_next_n(x); break; case SFLOW_ADDRTYPE_IP6: x->agentAddr.a.ip6[0] = sflowxdr_next_n(x); x->agentAddr.a.ip6[1] = sflowxdr_next_n(x); x->agentAddr.a.ip6[2] = sflowxdr_next_n(x); x->agentAddr.a.ip6[3] = sflowxdr_next_n(x); break; case SFLOW_ADDRTYPE_undefined: default: SFLOWXDR_throw(x); break; } x->subAgentId = sflowxdr_next(x); x->dgramSeqNo = sflowxdr_next(x); x->uptime_mS = sflowxdr_next(x); /* Store the agent address as a string. */ if (x->agentAddr.type == SFLOW_ADDRTYPE_IP6) { char ipstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, (const void *) &x->agentAddr.a.ip6, ipstr, INET6_ADDRSTRLEN); snprintf(x->agentIPStr, sizeof x->agentIPStr, "[%s]", ipstr); } else { snprintf(x->agentIPStr, sizeof x->agentIPStr, IP_FMT, IP_ARGS(x->agentAddr.a.ip4)); } /* Array of flow/counter samples. */ samples = sflowxdr_next(x); for (s = 0; s < samples; s++) { uint32_t sType = sflowxdr_next(x); uint32_t sQuads = sflowxdr_next(x) >> 2; uint32_t sMark = sflowxdr_mark(x, sQuads); SFLOWXDR_assert(x, sflowxdr_more(x, sQuads)); switch (sType) { case SFLOW_COUNTERS_SAMPLE_EXPANDED: case SFLOW_COUNTERS_SAMPLE: { uint32_t csElements, e; uint32_t ceTag, ceQuads, ceMark, csEnd; x->csSeqNo = sflowxdr_next(x); if (sType == SFLOW_COUNTERS_SAMPLE_EXPANDED) { x->dsClass = sflowxdr_next(x); x->dsIndex = sflowxdr_next(x); } else { uint32_t dsCombined = sflowxdr_next(x); x->dsClass = dsCombined >> 24; x->dsIndex = dsCombined & 0x00FFFFFF; } csElements = sflowxdr_next(x); for (e = 0; e < csElements; e++) { SFLOWXDR_assert(x, sflowxdr_more(x,2)); ceTag = sflowxdr_next(x); ceQuads = sflowxdr_next(x) >> 2; ceMark = sflowxdr_mark(x, ceQuads); SFLOWXDR_assert(x, sflowxdr_more(x,ceQuads)); /* Only care about selected structures. Just record their * offsets here. We'll read the fields out later. */ switch (ceTag) { case SFLOW_TAG_CTR_IFCOUNTERS: sflowxdr_mark_unique(x, &x->offset.IFCOUNTERS); break; case SFLOW_TAG_CTR_ETHCOUNTERS: sflowxdr_mark_unique(x, &x->offset.ETHCOUNTERS); break; case SFLOW_TAG_CTR_LACPCOUNTERS: sflowxdr_mark_unique(x, &x->offset.LACPCOUNTERS); break; case SFLOW_TAG_CTR_PORTNAME: sflowxdr_mark_unique(x, &x->offset.PORTNAME); break; case SFLOW_TAG_CTR_OPENFLOWPORT: sflowxdr_mark_unique(x, &x->offset.OPENFLOWPORT); break; /* Add others here... */ } sflowxdr_skip(x, ceQuads); SFLOWXDR_assert(x, sflowxdr_mark_ok(x, ceMark)); } csEnd = sflowxdr_mark(x, 0); process_counter_sample(x); /* Make sure we pick up the decoding where we left off. */ sflowxdr_setc(x, csEnd); /* Clear the offsets for the next sample. */ memset(&x->offset, 0, sizeof x->offset); } break; case SFLOW_FLOW_SAMPLE: case SFLOW_FLOW_SAMPLE_EXPANDED: { uint32_t fsElements, e; uint32_t feTag, feQuads, feMark, fsEnd; x->fsSeqNo = sflowxdr_next(x); if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) { x->dsClass = sflowxdr_next(x); x->dsIndex = sflowxdr_next(x); } else { uint32_t dsCombined = sflowxdr_next(x); x->dsClass = dsCombined >> 24; x->dsIndex = dsCombined & 0x00FFFFFF; } x->meanSkipCount = sflowxdr_next(x); x->samplePool = sflowxdr_next(x); x->dropEvents = sflowxdr_next(x); if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) { x->inputPortFormat = sflowxdr_next(x); x->inputPort = sflowxdr_next(x); x->outputPortFormat = sflowxdr_next(x); x->outputPort = sflowxdr_next(x); } else { uint32_t inp, outp; inp = sflowxdr_next(x); outp = sflowxdr_next(x); x->inputPortFormat = inp >> 30; x->inputPort = inp & 0x3fffffff; x->outputPortFormat = outp >> 30; x->outputPort = outp & 0x3fffffff; } fsElements = sflowxdr_next(x); for (e = 0; e < fsElements; e++) { SFLOWXDR_assert(x, sflowxdr_more(x,2)); feTag = sflowxdr_next(x); feQuads = sflowxdr_next(x) >> 2; feMark = sflowxdr_mark(x, feQuads); SFLOWXDR_assert(x, sflowxdr_more(x,feQuads)); /* Only care about selected structures. Just record their * offsets here. We'll read the fields out below. */ switch (feTag) { case SFLOW_TAG_PKT_HEADER: sflowxdr_mark_unique(x, &x->offset.HEADER); break; case SFLOW_TAG_PKT_SWITCH: sflowxdr_mark_unique(x, &x->offset.SWITCH); break; case SFLOW_TAG_PKT_TUNNEL4_OUT: sflowxdr_mark_unique(x, &x->offset.TUNNEL4_OUT); break; case SFLOW_TAG_PKT_TUNNEL4_IN: sflowxdr_mark_unique(x, &x->offset.TUNNEL4_IN); break; case SFLOW_TAG_PKT_TUNNEL_VNI_OUT: sflowxdr_mark_unique(x, &x->offset.TUNNEL_VNI_OUT); break; case SFLOW_TAG_PKT_TUNNEL_VNI_IN: sflowxdr_mark_unique(x, &x->offset.TUNNEL_VNI_IN); break; case SFLOW_TAG_PKT_MPLS: sflowxdr_mark_unique(x, &x->offset.MPLS); break; /* Add others here... */ } sflowxdr_skip(x, feQuads); SFLOWXDR_assert(x, sflowxdr_mark_ok(x, feMark)); } fsEnd = sflowxdr_mark(x, 0); process_flow_sample(x); /* Make sure we pick up the decoding where we left off. */ sflowxdr_setc(x, fsEnd); /* Clear the offsets for the next counter/flow sample. */ memset(&x->offset, 0, sizeof x->offset); } break; default: /* Skip other sample types. */ sflowxdr_skip(x, sQuads); } SFLOWXDR_assert(x, sflowxdr_mark_ok(x, sMark)); } } static void print_sflow(struct ofpbuf *buf) { char *dgram_buf; int dgram_len = buf->size; struct sflow_xdr xdrDatagram; struct sflow_xdr *x = &xdrDatagram; memset(x, 0, sizeof *x); if (SFLOWXDR_try(x)) { SFLOWXDR_assert(x, (dgram_buf = ofpbuf_try_pull(buf, buf->size))); sflowxdr_init(x, dgram_buf, dgram_len); SFLOWXDR_assert(x, dgram_len >= SFLOW_MIN_LEN); process_datagram(x); } else { // CATCH printf("\n>>>>> ERROR in " __FILE__ " at line %d\n", x->errline); } } static void test_sflow_main(int argc, char *argv[]) { struct unixctl_server *server; enum { MAX_RECV = 1500 }; const char *target; struct ofpbuf buf; bool exiting = false; int error; int sock; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); if (argc - optind != 1) { ovs_fatal(0, "exactly one non-option argument required " "(use --help for help)"); } target = argv[optind]; sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0, true); if (sock < 0) { ovs_fatal(0, "%s: failed to open (%s)", target, ovs_strerror(-sock)); } daemon_save_fd(STDOUT_FILENO); daemonize_start(false, false); error = unixctl_server_create(NULL, &server); if (error) { ovs_fatal(error, "failed to create unixctl server"); } unixctl_command_register("exit", "", 0, 0, test_sflow_exit, &exiting); daemonize_complete(); ofpbuf_init(&buf, MAX_RECV); for (;;) { int retval; unixctl_server_run(server); ofpbuf_clear(&buf); do { retval = recv(sock, buf.data, buf.allocated, 0); } while (retval < 0 && errno == EINTR); if (retval > 0) { ofpbuf_put_uninit(&buf, retval); print_sflow(&buf); fflush(stdout); } if (exiting) { break; } poll_fd_wait(sock, POLLIN); unixctl_server_wait(server); poll_block(); } ofpbuf_uninit(&buf); unixctl_server_destroy(server); } static void parse_options(int argc, char *argv[]) { enum { DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"verbose", optional_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void) { printf("%s: sflow collector test utility\n" "usage: %s [OPTIONS] PORT[:IP]\n" "where PORT is the UDP port to listen on and IP is optionally\n" "the IP address to listen on.\n", program_name, program_name); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n"); exit(EXIT_SUCCESS); } static void test_sflow_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } OVSTEST_REGISTER("test-sflow", test_sflow_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-sha1.c000066400000000000000000000153201514270232600221000ustar00rootroot00000000000000/* * Copyright (c) 2009, 2011, 2012 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "sha1.h" #include #include #include #include #include #include "ovstest.h" #include "random.h" #include "util.h" struct test_vector { char *data; size_t size; const uint8_t output[20]; }; struct test_api { void (*sha1_init)(struct sha1_ctx *); void (*sha1_update)(struct sha1_ctx *, const void *, uint32_t size); void (*sha1_final)(struct sha1_ctx *, uint8_t digest[SHA1_DIGEST_SIZE]); void (*sha1_bytes)(const void *, uint32_t size, uint8_t digest[SHA1_DIGEST_SIZE]); }; static const struct test_vector vectors[] = { /* FIPS 180-1. */ { "abc", 3, { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D } }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, }, /* RFC 3174. */ { "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567" "0123456701234567012345670123456701234567012345670123456701234567", 64 * 10, { 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC, 0xED, 0xC5, 0xEB, 0xB5, 0x63, 0x93, 0x4F, 0x46, 0x04, 0x52 }, }, /* http://www.febooti.com/products/filetweak/members/hash-and-crc/test-vectors/ */ { "", 0, { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 } }, { "Test vector from febooti.com", 28, { 0xa7, 0x63, 0x17, 0x95, 0xf6, 0xd5, 0x9c, 0xd6, 0xd1, 0x4e, 0xbd, 0x00, 0x58, 0xa6, 0x39, 0x4a, 0x4b, 0x93, 0xd8, 0x68 } }, /* http://en.wikipedia.org/wiki/SHA_hash_functions */ { "The quick brown fox jumps over the lazy dog", 43, { 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12 }, }, { "The quick brown fox jumps over the lazy cog", 43, { 0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3, 0xe8, 0x5a, 0x0b, 0xd1, 0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3 }, }, /* http://www.hashcash.org/docs/sha1-hashcash.html */ { "0:030626:adam@cypherspace.org:6470e06d773e05a8", 46, { 0x00, 0x00, 0x00, 0x00, 0xc7, 0x0d, 0xb7, 0x38, 0x9f, 0x24, 0x1b, 0x8f, 0x44, 0x1f, 0xcf, 0x06, 0x8a, 0xea, 0xd3, 0xf0 }, }, }; static void test_one(const struct test_api *api, const struct test_vector *vec) { uint8_t md[SHA1_DIGEST_SIZE]; int i; /* All at once. */ api->sha1_bytes(vec->data, vec->size, md); assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE)); /* In two pieces. */ for (i = 0; i < 20; i++) { int n0 = vec->size ? random_range(vec->size) : 0; int n1 = vec->size - n0; struct sha1_ctx sha1; api->sha1_init(&sha1); api->sha1_update(&sha1, vec->data, n0); api->sha1_update(&sha1, vec->data + n0, n1); api->sha1_final(&sha1, md); assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE)); } putchar('.'); fflush(stdout); } static void test_big_vector(const struct test_api *api) { enum { SIZE = 1000000 }; struct test_vector vec = { NULL, SIZE, { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } }; size_t i; vec.data = xmalloc(SIZE); for (i = 0; i < SIZE; i++) { vec.data[i] = 'a'; } test_one(api, &vec); free(vec.data); } static void test_huge_vector(const struct test_api *api) { enum { SIZE = 1000000000 }; struct test_vector vec = { NULL, SIZE, /* Computed by the sha1sum utility for a file with 10^9 symbols 'a'. */ { 0xD0, 0xF3, 0xE4, 0xF2, 0xF3, 0x1C, 0x66, 0x5A, 0xBB, 0xD8, 0xF5, 0x18, 0xE8, 0x48, 0xD5, 0xCB, 0x80, 0xCA, 0x78, 0xF7 } }; int chunk = random_range(SIZE / 10000); uint8_t md[SHA1_DIGEST_SIZE]; struct sha1_ctx sha1; size_t i, sz; /* It's not user-friendly to allocate 1GB of memory for a unit test, * so we're allocating only a small chunk and re-using it. */ vec.data = xmalloc(chunk); for (i = 0; i < chunk; i++) { vec.data[i] = 'a'; } api->sha1_init(&sha1); for (sz = 0; sz < SIZE; sz += chunk) { int n = sz + chunk < SIZE ? chunk : SIZE - sz; api->sha1_update(&sha1, vec.data, n); } api->sha1_final(&sha1, md); ovs_assert(!memcmp(md, vec.output, SHA1_DIGEST_SIZE)); free(vec.data); putchar('.'); fflush(stdout); } static void test_shar1_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct test_api api[] = { { .sha1_init = sha1_init, .sha1_update = sha1_update, .sha1_final = sha1_final, .sha1_bytes = sha1_bytes, }, { .sha1_init = ovs_sha1_init, .sha1_update = ovs_sha1_update, .sha1_final = ovs_sha1_final, .sha1_bytes = ovs_sha1_bytes, }, }; for (int i = 0; i < ARRAY_SIZE(api); i++) { for (int j = 0; j < ARRAY_SIZE(vectors); j++) { test_one(&api[i], &vectors[j]); } test_big_vector(&api[i]); test_huge_vector(&api[i]); } putchar('\n'); } OVSTEST_REGISTER("test-sha1", test_shar1_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-skiplist.c000066400000000000000000000132301514270232600231040ustar00rootroot00000000000000/* Copyright (C) 2016 Hewlett Packard Enterprise Development LP * All Rights Reserved. * * 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. */ /* A non-exhaustive test for some of the functions and macros declared in * skiplist.h. */ #include #undef NDEBUG #include #include #include "ovstest.h" #include "skiplist.h" #include "random.h" #include "util.h" static void test_skiplist_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED); static int test_skiplist_cmp(const void *a, const void *b, const void *conf); static void test_skiplist_insert(void); static void test_skiplist_delete(void); static void test_skiplist_find(void); static void test_skiplist_forward_to(void); static void test_skiplist_random(void); static int test_skiplist_cmp(const void *a, const void *b, const void *conf OVS_UNUSED) { const int *n = (const int *)a; const int *m = (const int *)b; return (*n > *m) - (*n < *m); } static void test_skiplist_insert(void) { struct skiplist *sl = skiplist_create(test_skiplist_cmp, NULL); int i; int *integer; /* Insert a million rows */ for (i = 0; i < 1000000; i++) { integer = xmalloc(sizeof(int)); *integer = i; skiplist_insert(sl, integer); } /* Check that the skiplist maintains the list sorted */ struct skiplist_node *node = skiplist_first(sl); for (i = 0; i < 1000000; i++) { integer = (int *)skiplist_get_data(node); ovs_assert(i == *integer); node = skiplist_next(node); } skiplist_destroy(sl, free); } static void test_skiplist_delete(void) { struct skiplist *sl = skiplist_create(test_skiplist_cmp, NULL); int a, b, c; a = 1; b = 2; c = 3; /* Insert rows */ skiplist_insert(sl, &a); skiplist_insert(sl, &c); skiplist_insert(sl, &b); /* Check that the items exists */ ovs_assert(a == *(int *)skiplist_get_data(skiplist_find(sl, &a))); ovs_assert(b == *(int *)skiplist_get_data(skiplist_find(sl, &b))); ovs_assert(c == *(int *)skiplist_get_data(skiplist_find(sl, &c))); /* Delete b*/ skiplist_delete(sl, &b); /* Check that the items doesn't exists */ ovs_assert(a == *(int *)skiplist_get_data(skiplist_find(sl, &a))); ovs_assert(NULL == skiplist_get_data(skiplist_find(sl, &b))); ovs_assert(c == *(int *)skiplist_get_data(skiplist_find(sl, &c))); skiplist_destroy(sl, NULL); } static void test_skiplist_find(void) { struct skiplist *sl = skiplist_create(test_skiplist_cmp, NULL); int i; int *integer; /* Insert a many rows */ for (i = 0; i < 1000000; i++) { integer = xmalloc(sizeof(int)); *integer = i; skiplist_insert(sl, integer); } /* Seek all the items */ for (i = 0; i < 1000000; i++) { integer = (int *)skiplist_get_data(skiplist_find(sl, &i)); ovs_assert(i == *integer); } skiplist_destroy(sl, free); } static void test_skiplist_forward_to(void) { struct skiplist *sl = skiplist_create(test_skiplist_cmp, NULL); int a, b, c, d, x; a = 1; b = 3; c = 7; d = 10; /* Insert rows */ skiplist_insert(sl, &a); skiplist_insert(sl, &c); skiplist_insert(sl, &b); skiplist_insert(sl, &d); /* Check that forward_to returns the expected value */ x = a; ovs_assert(a == *(int *)skiplist_get_data(skiplist_forward_to(sl, &x))); x = 2; ovs_assert(b == *(int *)skiplist_get_data(skiplist_forward_to(sl, &x))); x = 5; ovs_assert(c == *(int *)skiplist_get_data(skiplist_forward_to(sl, &x))); x = 8; ovs_assert(d == *(int *)skiplist_get_data(skiplist_forward_to(sl, &x))); x = 15; ovs_assert(NULL == (int *)skiplist_get_data(skiplist_forward_to(sl, &x))); /* Destroy skiplist */ skiplist_destroy(sl, NULL); } static void test_skiplist_random(void) { struct skiplist *sl = skiplist_create(test_skiplist_cmp, NULL); int total_numbers = 50; int expected_count = 0; int *numbers = xmalloc(sizeof(int) * total_numbers); int i, op, element; for (i = 0; i < total_numbers; i++) { numbers[i] = i; } random_init(); for (i = 0; i < total_numbers * 1000; i++) { op = random_uint32() % 2; element = random_range(total_numbers); if (op == 0) { if (!skiplist_find(sl, &numbers[element])) { expected_count++; } skiplist_insert(sl, &numbers[element]); } else { if (skiplist_find(sl, &numbers[element])) { expected_count--; } skiplist_delete(sl, &numbers[element]); } ovs_assert(expected_count == skiplist_get_size(sl)); } skiplist_destroy(sl, NULL); free(numbers); } static void test_skiplist_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { printf("skiplist insert\n"); test_skiplist_insert(); printf("skiplist delete\n"); test_skiplist_delete(); printf("skiplist find\n"); test_skiplist_find(); printf("skiplist forward_to\n"); test_skiplist_forward_to(); printf("skiplist random\n"); test_skiplist_random(); printf("\n"); } OVSTEST_REGISTER("test-skiplist", test_skiplist_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-stopwatch.c000066400000000000000000000135761514270232600232730ustar00rootroot00000000000000/* * Copyright (c) 2018 Red Hat, Inc. * * 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. */ #include #undef NDEBUG #include "stopwatch.h" #include #include #include #include "ovstest.h" #include "util.h" #define MAX_SAMPLES 100 #define UNIT SW_MS struct test_data { const char *name; unsigned long long samples[MAX_SAMPLES]; size_t num_samples; struct stopwatch_stats expected_stats; }; static struct test_data data_sets[] = { { .name = "1-interval-zero-length", .samples = { 0, 0 }, .num_samples = 2, .expected_stats = { .count = 1, .unit = UNIT, .max = 0, .min = 0, .pctl_95 = 0, .ewma_50 = 0, .ewma_1 = 0, }, }, { .name = "1-interval-unit-length", .samples = { 0, 1 }, .num_samples = 2, .expected_stats = { .count = 1, .unit = UNIT, .max = 1, .min = 1, .pctl_95 = 0, .ewma_50 = 1, .ewma_1 = 1, }, }, { .name = "10-intervals-unit-length", .samples = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, .num_samples = 11, .expected_stats = { .count = 10, .unit = UNIT, .max = 1, .min = 1, .pctl_95 = 1, .ewma_50 = 1, .ewma_1 = 1, }, }, { .name = "10-intervals-linear-growth", .samples = { 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56 }, .num_samples = 11, .expected_stats = { .count = 10, .unit = UNIT, .max = 10, .min = 1, .pctl_95 = 10.0, .ewma_50 = 9.0, .ewma_1 = 1.4, }, }, { .name = "60-intervals-unit-length", .samples = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, }, .num_samples = 61, .expected_stats = { .count = 60, .unit = UNIT, .max = 1, .min = 1, .pctl_95 = 1, .ewma_50 = 1, .ewma_1 = 1, }, }, { .name = "60-intervals-linear-growth", .samples = { 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137, 154, 172, 191, 211, 232, 254, 277, 301, 326, 352, 379, 407, 436, 466, 497, 529, 562, 596, 631, 667, 704, 742, 781, 821, 862, 904, 947, 991, 1036, 1082, 1129, 1177, 1226, 1276, 1327, 1379, 1432, 1486, 1541, 1597, 1654, 1712, 1771, 1831, }, .num_samples = 61, .expected_stats = { .count = 60, .unit = UNIT, .max = 60, .min = 1, /* 95th percentile is actually closer to 57, but the estimate is * pretty dang close */ .pctl_95 = 56, .ewma_50 = 59, .ewma_1 = 15.7, }, }, }; #define ASSERT_MSG(COND, MSG, ...) \ if (!(COND)) { \ fprintf(stderr, MSG "\n", ##__VA_ARGS__); \ assert(COND); \ } #define ASSERT_ULL_EQ(a, b) \ ASSERT_MSG(a == b, \ "Assertion '%s == %s' failed: %llu == %llu", \ #a, #b, a, b) #define ASSERT_DOUBLE_EQ(a, b, eps) \ ASSERT_MSG(fabs(a - b) < eps, \ "Assertion '|%s - %s| < %s' failed: |%g - %g| < %g", \ #a, #b, #eps, a, b, eps) #define ASSERT_STATS_EQ(a, b) \ do { \ ASSERT_ULL_EQ((a)->count, (b)->count); \ ASSERT_ULL_EQ((a)->max, (b)->max); \ ASSERT_ULL_EQ((a)->min, (b)->min); \ ASSERT_DOUBLE_EQ((a)->pctl_95, (b)->pctl_95, 1e-1); \ ASSERT_DOUBLE_EQ((a)->ewma_50, (b)->ewma_50, 1e-1); \ ASSERT_DOUBLE_EQ((a)->ewma_1, (b)->ewma_1, 1e-1); \ } while (0) static void test_stopwatch_calculate_stats(void) { struct test_data *d; for (size_t i = 0; i < ARRAY_SIZE(data_sets); i++) { d = &data_sets[i]; fprintf(stderr, "TEST '%s'\n", d->name); stopwatch_create(d->name, UNIT); for (size_t j = 0; j < d->num_samples - 1; j ++) { stopwatch_start(d->name, d->samples[j]); stopwatch_stop(d->name, d->samples[j + 1]); } stopwatch_sync(); struct stopwatch_stats stats = { .unit = UNIT }; stopwatch_get_stats(d->name, &stats); ASSERT_STATS_EQ(&stats, &d->expected_stats); printf("."); } } static void test_stopwatch_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { test_stopwatch_calculate_stats(); printf("\n"); } OVSTEST_REGISTER("test-stopwatch", test_stopwatch_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-stp.c000066400000000000000000000451601514270232600220570ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "stp.h" #include #include #include #include #include #include #include "dp-packet.h" #include "openvswitch/ofpbuf.h" #include "ovstest.h" #include "packets.h" #include "openvswitch/vlog.h" struct bpdu { int port_no; void *data; size_t size; }; struct bridge { struct test_case *tc; int id; bool reached; struct stp *stp; struct lan *ports[STP_MAX_PORTS]; int n_ports; #define RXQ_SIZE 16 struct bpdu rxq[RXQ_SIZE]; int rxq_head, rxq_tail; }; struct lan_conn { struct bridge *bridge; int port_no; }; struct lan { struct test_case *tc; const char *name; bool reached; struct lan_conn conns[16]; int n_conns; }; struct test_case { struct bridge *bridges[16]; int n_bridges; struct lan *lans[26]; int n_lans; }; static const char *file_name; static int line_number; static char line[128]; static char *pos, *token; static int n_warnings; static struct test_case * new_test_case(void) { struct test_case *tc = xmalloc(sizeof *tc); tc->n_bridges = 0; tc->n_lans = 0; return tc; } static void send_bpdu(struct dp_packet *pkt, int port_no, void *b_) { struct bridge *b = b_; struct lan *lan; assert(port_no < b->n_ports); lan = b->ports[port_no]; if (lan) { const void *data = dp_packet_l3(pkt); size_t size = (char *) dp_packet_tail(pkt) - (char *) data; int i; for (i = 0; i < lan->n_conns; i++) { struct lan_conn *conn = &lan->conns[i]; if (conn->bridge != b || conn->port_no != port_no) { struct bridge *dst = conn->bridge; struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE]; assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE); bpdu->data = xmemdup(data, size); bpdu->size = size; bpdu->port_no = conn->port_no; } } } dp_packet_delete(pkt); } static struct bridge * new_bridge(struct test_case *tc, int id) { struct bridge *b = xmalloc(sizeof *b); char name[16]; b->tc = tc; b->id = id; snprintf(name, sizeof name, "stp%x", id); b->stp = stp_create(name, id, send_bpdu, b); assert(tc->n_bridges < ARRAY_SIZE(tc->bridges)); b->n_ports = 0; b->rxq_head = b->rxq_tail = 0; tc->bridges[tc->n_bridges++] = b; return b; } static struct lan * new_lan(struct test_case *tc, const char *name) { struct lan *lan = xmalloc(sizeof *lan); lan->tc = tc; lan->name = xstrdup(name); lan->n_conns = 0; assert(tc->n_lans < ARRAY_SIZE(tc->lans)); tc->lans[tc->n_lans++] = lan; return lan; } static void reconnect_port(struct bridge *b, int port_no, struct lan *new_lan) { struct lan *old_lan; int j; assert(port_no < b->n_ports); old_lan = b->ports[port_no]; if (old_lan == new_lan) { return; } /* Disconnect from old_lan. */ if (old_lan) { for (j = 0; j < old_lan->n_conns; j++) { struct lan_conn *c = &old_lan->conns[j]; if (c->bridge == b && c->port_no == port_no) { memmove(c, c + 1, sizeof *c * (old_lan->n_conns - j - 1)); old_lan->n_conns--; break; } } } /* Connect to new_lan. */ b->ports[port_no] = new_lan; if (new_lan) { int conn_no = new_lan->n_conns++; assert(conn_no < ARRAY_SIZE(new_lan->conns)); new_lan->conns[conn_no].bridge = b; new_lan->conns[conn_no].port_no = port_no; } } static void new_port(struct bridge *b, struct lan *lan, int path_cost) { int port_no = b->n_ports++; struct stp_port *p = stp_get_port(b->stp, port_no); assert(port_no < ARRAY_SIZE(b->ports)); b->ports[port_no] = NULL; stp_port_set_path_cost(p, path_cost); stp_port_enable(p); reconnect_port(b, port_no, lan); } static void dump(struct test_case *tc) { int i; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; struct stp *stp = b->stp; int j; printf("%s:", stp_get_name(stp)); if (stp_is_root_bridge(stp)) { printf(" root"); } printf("\n"); for (j = 0; j < b->n_ports; j++) { struct stp_port *p = stp_get_port(stp, j); enum stp_state state = stp_port_get_state(p); printf("\tport %d", j); if (b->ports[j]) { printf(" (lan %s)", b->ports[j]->name); } else { printf(" (disconnected)"); } printf(": %s", stp_state_name(state)); if (p == stp_get_root_port(stp)) { printf(" (root port, root_path_cost=%u)", stp_get_root_path_cost(stp)); } printf("\n"); } } } static void dump_lan_tree(struct test_case *, struct lan *, int level); static void dump_bridge_tree(struct test_case *tc, struct bridge *b, int level) { int i; if (b->reached) { return; } b->reached = true; for (i = 0; i < level; i++) { printf("\t"); } printf("%s\n", stp_get_name(b->stp)); for (i = 0; i < b->n_ports; i++) { struct lan *lan = b->ports[i]; struct stp_port *p = stp_get_port(b->stp, i); if (stp_port_get_state(p) == STP_FORWARDING && lan) { dump_lan_tree(tc, lan, level + 1); } } } static void dump_lan_tree(struct test_case *tc, struct lan *lan, int level) { int i; if (lan->reached) { return; } lan->reached = true; for (i = 0; i < level; i++) { printf("\t"); } printf("%s\n", lan->name); for (i = 0; i < lan->n_conns; i++) { struct bridge *b = lan->conns[i].bridge; dump_bridge_tree(tc, b, level + 1); } } static void tree(struct test_case *tc) { int i; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; b->reached = false; } for (i = 0; i < tc->n_lans; i++) { struct lan *lan = tc->lans[i]; lan->reached = false; } for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; struct stp *stp = b->stp; if (stp_is_root_bridge(stp)) { dump_bridge_tree(tc, b, 0); } } } static void simulate(struct test_case *tc, int granularity) { int time; for (time = 0; time < 1000 * 180; time += granularity) { int round_trips; int i; for (i = 0; i < tc->n_bridges; i++) { stp_tick(tc->bridges[i]->stp, granularity); } for (round_trips = 0; round_trips < granularity; round_trips++) { bool any = false; for (i = 0; i < tc->n_bridges; i++) { struct bridge *b = tc->bridges[i]; for (; b->rxq_tail != b->rxq_head; b->rxq_tail++) { struct bpdu *bpdu = &b->rxq[b->rxq_tail % RXQ_SIZE]; stp_received_bpdu(stp_get_port(b->stp, bpdu->port_no), bpdu->data, bpdu->size); free(bpdu->data); any = true; } } if (!any) { break; } } } } OVS_NO_RETURN static void err(const char *message, ...) OVS_PRINTF_FORMAT(1, 2); static void err(const char *message, ...) { va_list args; fprintf(stderr, "%s:%d:%"PRIdPTR": ", file_name, line_number, pos - line); va_start(args, message); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); exit(EXIT_FAILURE); } static void warn(const char *message, ...) OVS_PRINTF_FORMAT(1, 2); static void warn(const char *message, ...) { va_list args; fprintf(stderr, "%s:%d: ", file_name, line_number); va_start(args, message); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); n_warnings++; } static bool get_token(void) { char *start; while (isspace((unsigned char) *pos)) { pos++; } if (*pos == '\0') { free(token); token = NULL; return false; } start = pos; if (isalpha((unsigned char) *pos)) { while (isalpha((unsigned char) *++pos)) { continue; } } else if (isdigit((unsigned char) *pos)) { if (*pos == '0' && (pos[1] == 'x' || pos[1] == 'X')) { pos += 2; while (isxdigit((unsigned char) *pos)) { pos++; } } else { while (isdigit((unsigned char) *++pos)) { continue; } } } else { pos++; } free(token); token = xmemdup0(start, pos - start); return true; } static bool get_int(int *intp) { char *save_pos = pos; if (token && isdigit((unsigned char) *token)) { *intp = strtol(token, NULL, 0); get_token(); return true; } else { pos = save_pos; return false; } } static bool match(const char *want) { if (token && !strcmp(want, token)) { get_token(); return true; } else { return false; } } static int must_get_int(void) { int x; if (!get_int(&x)) { err("expected integer"); } return x; } static void must_match(const char *want) { if (!match(want)) { err("expected \"%s\"", want); } } static void test_stp_main(int argc, char *argv[]) { struct test_case *tc; FILE *input_file; int i; vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m"); vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF); if (argc != 2) { ovs_fatal(0, "usage: test-stp INPUT.STP"); } file_name = argv[1]; input_file = fopen(file_name, "r"); if (!input_file) { ovs_fatal(errno, "error opening \"%s\"", file_name); } tc = new_test_case(); for (i = 0; i < 26; i++) { char name[2]; name[0] = 'a' + i; name[1] = '\0'; new_lan(tc, name); } for (line_number = 1; fgets(line, sizeof line, input_file); line_number++) { char *newline, *hash; newline = strchr(line, '\n'); if (newline) { *newline = '\0'; } hash = strchr(line, '#'); if (hash) { *hash = '\0'; } pos = line; if (!get_token()) { continue; } if (match("bridge")) { struct bridge *bridge; int bridge_no, port_no; bridge_no = must_get_int(); if (bridge_no < tc->n_bridges) { bridge = tc->bridges[bridge_no]; } else if (bridge_no == tc->n_bridges) { bridge = new_bridge(tc, must_get_int()); } else { err("bridges must be numbered consecutively from 0"); } if (match("^")) { stp_set_bridge_priority(bridge->stp, must_get_int()); } if (match("=")) { for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) { struct stp_port *p = stp_get_port(bridge->stp, port_no); if (!token || match("X")) { stp_port_disable(p); } else if (match("_")) { /* Nothing to do. */ } else { struct lan *lan; int path_cost; if (!strcmp(token, "0")) { lan = NULL; } else if (strlen(token) == 1 && islower((unsigned char)*token)) { lan = tc->lans[*token - 'a']; } else { err("%s is not a valid LAN name " "(0 or a lowercase letter)", token); } get_token(); path_cost = match(":") ? must_get_int() : 10; if (port_no < bridge->n_ports) { stp_port_set_path_cost(p, path_cost); stp_port_enable(p); reconnect_port(bridge, port_no, lan); } else if (port_no == bridge->n_ports) { new_port(bridge, lan, path_cost); } else { err("ports must be numbered consecutively"); } if (match("^")) { stp_port_set_priority(p, must_get_int()); } } } } } else if (match("run")) { simulate(tc, must_get_int()); } else if (match("dump")) { dump(tc); } else if (match("tree")) { tree(tc); } else if (match("check")) { struct bridge *b; struct stp *stp; int bridge_no, port_no; bridge_no = must_get_int(); if (bridge_no >= tc->n_bridges) { err("no bridge numbered %d", bridge_no); } b = tc->bridges[bridge_no]; stp = b->stp; must_match("="); if (match("rootid")) { uint64_t rootid; must_match(":"); rootid = must_get_int(); if (match("^")) { rootid |= (uint64_t) must_get_int() << 48; } else { rootid |= UINT64_C(0x8000) << 48; } if (stp_get_designated_root(stp) != rootid) { warn("%s: root %"PRIx64", not %"PRIx64, stp_get_name(stp), stp_get_designated_root(stp), rootid); } } if (match("root")) { if (stp_get_root_path_cost(stp)) { warn("%s: root path cost of root is %u but should be 0", stp_get_name(stp), stp_get_root_path_cost(stp)); } if (!stp_is_root_bridge(stp)) { warn("%s: root is %"PRIx64", not %"PRIx64, stp_get_name(stp), stp_get_designated_root(stp), stp_get_bridge_id(stp)); } for (port_no = 0; port_no < b->n_ports; port_no++) { struct stp_port *p = stp_get_port(stp, port_no); enum stp_state state = stp_port_get_state(p); if (!(state & (STP_DISABLED | STP_FORWARDING))) { warn("%s: root port %d in state %s", stp_get_name(b->stp), port_no, stp_state_name(state)); } } } else { for (port_no = 0; port_no < STP_MAX_PORTS; port_no++) { struct stp_port *p = stp_get_port(stp, port_no); enum stp_state state; if (token == NULL || match("D")) { state = STP_DISABLED; } else if (match("B")) { state = STP_BLOCKING; } else if (match("Li")) { state = STP_LISTENING; } else if (match("Le")) { state = STP_LEARNING; } else if (match("F")) { state = STP_FORWARDING; } else if (match("_")) { continue; } else { err("unknown port state %s", token); } if (stp_port_get_state(p) != state) { warn("%s port %d: state is %s but should be %s", stp_get_name(stp), port_no, stp_state_name(stp_port_get_state(p)), stp_state_name(state)); } if (state == STP_FORWARDING) { struct stp_port *root_port = stp_get_root_port(stp); if (match(":")) { int root_path_cost = must_get_int(); if (p != root_port) { warn("%s: port %d is not the root port", stp_get_name(stp), port_no); if (!root_port) { warn("%s: (there is no root port)", stp_get_name(stp)); } else { warn("%s: (port %d is the root port)", stp_get_name(stp), stp_port_no(root_port)); } } else if (root_path_cost != stp_get_root_path_cost(stp)) { warn("%s: root path cost is %u, should be %d", stp_get_name(stp), stp_get_root_path_cost(stp), root_path_cost); } } else if (p == root_port) { warn("%s: port %d is the root port but " "not expected to be", stp_get_name(stp), port_no); } } } } if (n_warnings) { exit(EXIT_FAILURE); } } if (get_token()) { err("trailing garbage on line"); } } free(token); for (i = 0; i < tc->n_lans; i++) { struct lan *lan = tc->lans[i]; free(CONST_CAST(char *, lan->name)); free(lan); } for (i = 0; i < tc->n_bridges; i++) { struct bridge *bridge = tc->bridges[i]; stp_unref(bridge->stp); free(bridge); } free(tc); fclose(input_file); } OVSTEST_REGISTER("test-stp", test_stp_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-stream.c000066400000000000000000000032271514270232600225420ustar00rootroot00000000000000/* * Copyright (c) 2018 Ilya Maximets * * 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. */ #include #include "fatal-signal.h" #include "openvswitch/vlog.h" #include "stream.h" #include "stream-ssl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(test_stream); int main(int argc, char *argv[]) { int error; struct stream *stream; fatal_ignore_sigpipe(); set_program_name(argv[0]); if (argc < 2) { ovs_fatal(0, "usage: %s REMOTE [SSL_KEY] [SSL_CERT] [SSL_CA]", argv[0]); } if (strncmp("ssl:", argv[1], 4) == 0) { if (argc < 5) { ovs_fatal(0, "usage with ssl: %s REMOTE SSL_KEY SSL_CERT SSL_CA", argv[0]); } stream_ssl_set_ca_cert_file(argv[4], false); stream_ssl_set_key_and_cert(argv[2], argv[3]); } error = stream_open_block(stream_open(argv[1], &stream, DSCP_DEFAULT), 10000, &stream); if (error) { VLOG_ERR("stream_open_block(%s) failure: %s", argv[1], ovs_strerror(error)); } stream_close(stream); return (error || !stream) ? 1 : 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-stream.py000066400000000000000000000027211514270232600227460ustar00rootroot00000000000000# Copyright (c) 2018, Red Hat Inc. # # 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. import sys import ovs.stream import ovs.util def main(argv): if len(argv) < 2: ovs.util.ovs_fatal(0, "usage: %s REMOTE [SSL_KEY] [SSL_CERT] [SSL_CA]", argv[0], ) remote = argv[1] if remote.startswith("ssl:"): if len(argv) < 5: ovs.util.ovs_fatal( 0, "usage with ssl: %s REMOTE [SSL_KEY] [SSL_CERT] [SSL_CA]", argv[0], ) ovs.stream.SSLStream.ssl_set_ca_cert_file(argv[4]) ovs.stream.SSLStream.ssl_set_certificate_file(argv[3]) ovs.stream.SSLStream.ssl_set_private_key_file(argv[2]) err, stream = ovs.stream.Stream.open_block( ovs.stream.Stream.open(remote), 10000) if err or stream is None: sys.exit(1) sys.exit(0) if __name__ == '__main__': main(sys.argv) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-strtok_r.c000066400000000000000000000023731514270232600231170ustar00rootroot00000000000000/* * Copyright (c) 2010 Nicira, Inc. * * 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. */ #include #include #include /* Some versions of glibc 2.7 has a bug in strtok_r when with optimization that * can cause segfaults: * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614. * * Open vSwitch works around this problem by supplying a replacement string.h. * This test program verifies that the workaround is in place. */ int main(void) { char string[] = ":::"; char *save_ptr = (char *) 0xc0ffee; char *token1, *token2; token1 = strtok_r(string, ":", &save_ptr); token2 = strtok_r(NULL, ":", &save_ptr); printf ("%s %s\n", token1 ? token1 : "NULL", token2 ? token2 : "NULL"); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-type-props.c000066400000000000000000000046031514270232600233700ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2011, 2015 Nicira, Inc. * * 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. */ #include #include "openvswitch/type-props.h" #include #include #include #include #define MUST_SUCCEED(EXPRESSION) \ if (!(EXPRESSION)) { \ fprintf(stderr, "%s:%d: %s failed\n", \ __FILE__, __LINE__, #EXPRESSION); \ exit(EXIT_FAILURE); \ } #define TEST_TYPE(type, minimum, maximum, is_signed) \ MUST_SUCCEED(TYPE_IS_INTEGER(type)); \ MUST_SUCCEED(TYPE_IS_SIGNED(type) == is_signed); \ MUST_SUCCEED(TYPE_MAXIMUM(type) == maximum); \ MUST_SUCCEED(TYPE_MINIMUM(type) == minimum); \ sprintf(max_s, "%"PRIuMAX, (uintmax_t) (maximum)); \ MUST_SUCCEED(strlen(max_s) <= INT_STRLEN(type)); \ sprintf(min_s, "%"PRIdMAX, (intmax_t) (minimum)); \ MUST_SUCCEED(strlen(min_s) <= INT_STRLEN(type)); int main (void) { char max_s[128]; char min_s[128]; #ifndef __CHECKER__ /* sparse hates sizeof(bool). */ TEST_TYPE(_Bool, 0, 1, 0); #endif TEST_TYPE(char, CHAR_MIN, CHAR_MAX, (CHAR_MIN < 0)); TEST_TYPE(signed char, SCHAR_MIN, SCHAR_MAX, 1); TEST_TYPE(short int, SHRT_MIN, SHRT_MAX, 1); TEST_TYPE(int, INT_MIN, INT_MAX, 1); TEST_TYPE(long int, LONG_MIN, LONG_MAX, 1); TEST_TYPE(long long int, LLONG_MIN, LLONG_MAX, 1); TEST_TYPE(unsigned char, 0, UCHAR_MAX, 0); TEST_TYPE(unsigned short int, 0, USHRT_MAX, 0); TEST_TYPE(unsigned int, 0, UINT_MAX, 0); TEST_TYPE(unsigned long int, 0, ULONG_MAX, 0); TEST_TYPE(unsigned long long int, 0, ULLONG_MAX, 0); MUST_SUCCEED(!(TYPE_IS_INTEGER(float))); MUST_SUCCEED(!(TYPE_IS_INTEGER(double))); MUST_SUCCEED(!(TYPE_IS_INTEGER(long double))); return 0; } openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-unix-socket.c000066400000000000000000000034521514270232600235200ustar00rootroot00000000000000/* * Copyright (c) 2010, 2012, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "socket-util.h" #include #include #include #include "ovstest.h" #include "util.h" static void test_unix_socket_main(int argc, char *argv[]) { const char *sockname1; const char *sockname2; int sock1, sock2; set_program_name(argv[0]); if (argc != 2 && argc != 3) { ovs_fatal(0, "usage: %s SOCKETNAME1 [SOCKETNAME2]", argv[0]); } sockname1 = argv[1]; sockname2 = argc > 2 ? argv[2] : sockname1; signal(SIGALRM, SIG_DFL); alarm(5); /* Create a listening socket under name 'sockname1'. */ sock1 = make_unix_socket(SOCK_STREAM, false, sockname1, NULL); if (sock1 < 0) { ovs_fatal(-sock1, "%s: bind failed", sockname1); } if (listen(sock1, 1)) { ovs_fatal(errno, "%s: listen failed", sockname1); } /* Connect to 'sockname2' (which should be the same file, perhaps under a * different name). */ sock2 = make_unix_socket(SOCK_STREAM, false, NULL, sockname2); if (sock2 < 0) { ovs_fatal(-sock2, "%s: connect failed", sockname2); } close(sock1); close(sock2); } OVSTEST_REGISTER("test-unix-socket", test_unix_socket_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-unix-socket.py000066400000000000000000000035601514270232600237260ustar00rootroot00000000000000# # Copyright (c) 2010, 2012, 2013 Nicira, Inc. # # 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. import os import signal import socket import sys import ovs.socket_util from ovs.fatal_signal import signal_alarm def main(argv): if len(argv) not in (2, 3): sys.stderr.write("usage: %s SOCKETNAME1 [SOCKETNAME2]", argv[0]) sys.exit(1) sockname1 = argv[1] if len(argv) > 2: sockname2 = argv[2] else: sockname2 = sockname1 signal.signal(signal.SIGALRM, signal.SIG_DFL) signal_alarm(5) # Create a listening socket under name 'sockname1'. error, sock1 = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, False, sockname1, None) if error: sys.stderr.write("%s: bind failed (%s)" % (sockname1, os.strerror(error))) sys.exit(1) sock1.listen(1) # Connect to 'sockname2' (which should be the same file, perhaps under a # different name). error, sock2 = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, False, None, sockname2) if error: sys.stderr.write("%s: connect failed (%s)" % (sockname2, os.strerror(error))) sys.exit(1) if __name__ == '__main__': main(sys.argv) openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-unixctl.c000066400000000000000000000117421514270232600227360ustar00rootroot00000000000000/* Copyright (c) 2015 Nicira, Inc. * * 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. */ #include #include #include "command-line.h" #include "daemon.h" #include "fatal-signal.h" #include "openvswitch/vlog.h" #include "ovstest.h" #include "openvswitch/poll-loop.h" #include "unixctl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(test_unixctl); static void parse_options(int *argc, char **argvp[], char **unixctl_pathp); OVS_NO_RETURN static void usage(void); static void test_unixctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void test_unixctl_echo(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { unixctl_command_reply(conn, argv[1]); } static void test_unixctl_echo_error(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { unixctl_command_reply_error(conn, argv[1]); } static void test_unixctl_log(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { VLOG_INFO("%s", argv[1]); unixctl_command_reply(conn, NULL); } static void test_unixctl_block(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { VLOG_INFO("%s", argv[1]); unixctl_command_reply(conn, NULL); } static int test_unixctl_main(int argc, char *argv[]) { char *unixctl_path = NULL; struct unixctl_server *unixctl; bool exiting = false; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); fatal_ignore_sigpipe(); parse_options(&argc, &argv, &unixctl_path); daemonize_start(false, false); int retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, test_unixctl_exit, &exiting); unixctl_command_register("echo", "ARG", 1, 1, test_unixctl_echo, NULL); unixctl_command_register("echo_error", "ARG", 1, 1, test_unixctl_echo_error, NULL); unixctl_command_register("log", "ARG", 1, 1, test_unixctl_log, NULL); unixctl_command_register("block", "", 0, 0, test_unixctl_block, NULL); daemonize_complete(); VLOG_INFO("Entering run loop."); while (!exiting) { unixctl_server_run(unixctl); unixctl_server_wait(unixctl); if (exiting) { poll_immediate_wake(); } poll_block(); } unixctl_server_destroy(unixctl); service_stop(); return 0; } static void parse_options(int *argcp, char **argvp[], char **unixctl_pathp) { enum { OPT_REMOTE = UCHAR_MAX + 1, OPT_UNIXCTL, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; static const struct option long_options[] = { {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); int argc = *argcp; char **argv = *argvp; for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case OPT_UNIXCTL: *unixctl_pathp = optarg; break; case 'h': usage(); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); *argcp -= optind; *argvp += optind; } static void usage(void) { printf("%s: Open vSwitch unixctl test program\n" "usage: %s [OPTIONS]\n", program_name, program_name); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } OVSTEST_REGISTER("test-unixctl", test_unixctl_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-unixctl.py000066400000000000000000000050601514270232600231400ustar00rootroot00000000000000# Copyright (c) 2012 Nicira, Inc. # # 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. import argparse import ovs.daemon import ovs.unixctl import ovs.unixctl.server vlog = ovs.vlog.Vlog("test-unixctl") exiting = False def unixctl_exit(conn, unused_argv, aux): assert aux == "aux_exit" global exiting exiting = True conn.reply(None) def unixctl_echo(conn, argv, aux): assert aux == "aux_echo" conn.reply(str(argv)) def unixctl_echo_error(conn, argv, aux): assert aux == "aux_echo_error" conn.reply_error(str(argv)) def unixctl_log(conn, argv, unused_aux): vlog.info(str(argv[0])) conn.reply(None) def unixctl_block(conn, unused_argv, unused_aux): pass def main(): parser = argparse.ArgumentParser( description="Open vSwitch unixctl test program for Python") parser.add_argument("--unixctl", help="UNIXCTL socket location or 'none'.") ovs.daemon.add_args(parser) ovs.vlog.add_args(parser) args = parser.parse_args() ovs.daemon.handle_args(args) ovs.vlog.handle_args(args) ovs.daemon.daemonize_start() error, server = ovs.unixctl.server.UnixctlServer.create(args.unixctl) if error: ovs.util.ovs_fatal(error, "could not create unixctl server at %s" % args.unixctl, vlog) ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, "aux_exit") ovs.unixctl.command_register("echo", "[arg ...]", 1, 2, unixctl_echo, "aux_echo") ovs.unixctl.command_register("log", "[arg ...]", 1, 2, unixctl_log, None) ovs.unixctl.command_register("echo_error", "[arg ...]", 1, 2, unixctl_echo_error, "aux_echo_error") ovs.unixctl.command_register("block", "", 0, 0, unixctl_block, None) ovs.daemon.daemonize_complete() vlog.info("Entering run loop.") poller = ovs.poller.Poller() while not exiting: server.run() server.wait(poller) if exiting: poller.immediate_wake() poller.block() server.close() if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-util.c000066400000000000000000001166411514270232600222310ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2019 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "util.h" #include #include #include #include #include #include #include "byte-order.h" #include "command-line.h" #include "ovstest.h" #include "random.h" #include "sat-math.h" #include "openvswitch/vlog.h" static void check_log_2_floor(uint32_t x, int n) { if (log_2_floor(x) != n) { fprintf(stderr, "log_2_floor(%"PRIu32") is %d but should be %d\n", x, log_2_floor(x), n); abort(); } } static void test_log_2_floor(struct ovs_cmdl_context *ctx OVS_UNUSED) { for (uint32_t n = 0; n < 32; n++) { /* Check minimum x such that f(x) == n. */ check_log_2_floor(UINT32_C(1) << n, n); /* Check maximum x such that f(x) == n. */ check_log_2_floor((UINT32_C(1) << n) | ((UINT32_C(1) << n) - 1), n); /* Check a random value in the middle. */ check_log_2_floor((random_uint32() & ((UINT32_C(1) << n) - 1)) | (UINT32_C(1) << n), n); } /* log_2_floor(0) is undefined, so don't check it. */ } static void check_ctz32(uint32_t x, int n) { if (ctz32(x) != n) { fprintf(stderr, "ctz32(%"PRIu32") is %d but should be %d\n", x, ctz32(x), n); abort(); } } static void check_ctz64(uint64_t x, int n) { if (ctz64(x) != n) { fprintf(stderr, "ctz64(%"PRIu64") is %d but should be %d\n", x, ctz64(x), n); abort(); } } static void test_ctz(struct ovs_cmdl_context *ctx OVS_UNUSED) { int n; for (n = 0; n < 32; n++) { /* Check minimum x such that f(x) == n. */ check_ctz32(UINT32_C(1) << n, n); /* Check maximum x such that f(x) == n. */ check_ctz32(UINT32_MAX << n, n); /* Check a random value in the middle. */ check_ctz32((random_uint32() | 1) << n, n); } for (n = 0; n < 64; n++) { /* Check minimum x such that f(x) == n. */ check_ctz64(UINT64_C(1) << n, n); /* Check maximum x such that f(x) == n. */ check_ctz64(UINT64_MAX << n, n); /* Check a random value in the middle. */ check_ctz64((random_uint64() | UINT64_C(1)) << n, n); } /* Check ctz(0). */ check_ctz32(0, 32); check_ctz64(0, 64); } static void check_clz32(uint32_t x, int n) { if (clz32(x) != n) { fprintf(stderr, "clz32(%"PRIu32") is %d but should be %d\n", x, clz32(x), n); abort(); } } static void check_clz64(uint64_t x, int n) { if (clz64(x) != n) { fprintf(stderr, "clz64(%"PRIu64") is %d but should be %d\n", x, clz64(x), n); abort(); } } static void test_clz(struct ovs_cmdl_context *ctx OVS_UNUSED) { int n; for (n = 0; n < 32; n++) { /* Check minimum x such that f(x) == n. */ check_clz32((1u << 31) >> n, n); /* Check maximum x such that f(x) == n. */ check_clz32(UINT32_MAX >> n, n); /* Check a random value in the middle. */ check_clz32((random_uint32() | 1u << 31) >> n, n); } for (n = 0; n < 64; n++) { /* Check minimum x such that f(x) == n. */ check_clz64((UINT64_C(1) << 63) >> n, n); /* Check maximum x such that f(x) == n. */ check_clz64(UINT64_MAX >> n, n); /* Check a random value in the middle. */ check_clz64((random_uint64() | UINT64_C(1) << 63) >> n, n); } /* Check clz(0). */ check_clz32(0, 32); check_clz64(0, 64); } /* Returns a random number in the range 'min'...'max' inclusive. */ static uint32_t random_in_range(uint32_t min, uint32_t max) { return min == max ? min : min + random_range(max - min + 1); } static void check_rup2(uint32_t x, int n) { uint32_t rup2 = ROUND_UP_POW2(x); if (rup2 != n) { fprintf(stderr, "ROUND_UP_POW2(%#"PRIx32") is %#"PRIx32" " "but should be %#"PRIx32"\n", x, rup2, n); abort(); } } static void test_round_up_pow2(struct ovs_cmdl_context *ctx OVS_UNUSED) { int n; for (n = 0; n < 32; n++) { /* Min, max value for which ROUND_UP_POW2 should yield (1 << n). */ uint32_t min = ((1u << n) >> 1) + 1; uint32_t max = 1u << n; check_rup2(min, 1u << n); check_rup2(max, 1u << n); check_rup2(random_in_range(min, max), 1u << n); } check_rup2(0, 0); } static void check_rdp2(uint32_t x, int n) { uint32_t rdp2 = ROUND_DOWN_POW2(x); if (rdp2 != n) { fprintf(stderr, "ROUND_DOWN_POW2(%#"PRIx32") is %#"PRIx32" " "but should be %#"PRIx32"\n", x, rdp2, n); abort(); } } static void test_round_down_pow2(struct ovs_cmdl_context *ctx OVS_UNUSED) { int n; for (n = 0; n < 32; n++) { /* Min, max value for which ROUND_DOWN_POW2 should yield (1 << n). */ uint32_t min = 1u << n; uint32_t max = ((1u << n) << 1) - 1; check_rdp2(min, 1u << n); check_rdp2(max, 1u << n); check_rdp2(random_in_range(min, max), 1u << n); } check_rdp2(0, 0); } static void shuffle(uint64_t *p, size_t n) { for (; n > 1; n--, p++) { uint64_t *q = &p[random_range(n)]; uint64_t tmp = *p; *p = *q; *q = tmp; } } static void check_count_1bits(uint64_t x, int n) { if (count_1bits(x) != n) { fprintf(stderr, "count_1bits(%#"PRIx64") is %d but should be %d\n", x, count_1bits(x), n); abort(); } } static void test_count_1bits(struct ovs_cmdl_context *ctx OVS_UNUSED) { uint64_t bits[64]; int i; for (i = 0; i < ARRAY_SIZE(bits); i++) { bits[i] = UINT64_C(1) << i; } check_count_1bits(0, 0); for (i = 0; i < 1000; i++) { uint64_t x = 0; int j; shuffle(bits, ARRAY_SIZE(bits)); for (j = 0; j < 64; j++) { x |= bits[j]; check_count_1bits(x, j + 1); } assert(x == UINT64_MAX); shuffle(bits, ARRAY_SIZE(bits)); for (j = 63; j >= 0; j--) { x &= ~bits[j]; check_count_1bits(x, j); } assert(x == 0); } } /* Returns the sum of the squares of the first 'n' positive integers. */ static unsigned int sum_of_squares(int n) { return n * (n + 1) * (2 * n + 1) / 6; } static void test_bitwise_copy(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int n_loops; int src_ofs; int dst_ofs; int n_bits; n_loops = 0; for (n_bits = 0; n_bits <= 64; n_bits++) { for (src_ofs = 0; src_ofs < 64 - n_bits; src_ofs++) { for (dst_ofs = 0; dst_ofs < 64 - n_bits; dst_ofs++) { ovs_be64 src = htonll(random_uint64()); ovs_be64 dst = htonll(random_uint64()); ovs_be64 orig_dst = dst; ovs_be64 expect; if (n_bits == 64) { expect = dst; } else { uint64_t mask = (UINT64_C(1) << n_bits) - 1; expect = orig_dst & ~htonll(mask << dst_ofs); expect |= htonll(((ntohll(src) >> src_ofs) & mask) << dst_ofs); } bitwise_copy(&src, sizeof src, src_ofs, &dst, sizeof dst, dst_ofs, n_bits); if (expect != dst) { fprintf(stderr,"copy_bits(0x%016"PRIx64",8,%d, " "0x%016"PRIx64",8,%d, %d) yielded 0x%016"PRIx64" " "instead of the expected 0x%016"PRIx64"\n", ntohll(src), src_ofs, ntohll(orig_dst), dst_ofs, n_bits, ntohll(dst), ntohll(expect)); abort(); } n_loops++; } } } if (n_loops != sum_of_squares(64)) { abort(); } } static void test_bitwise_zero(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int n_loops; int dst_ofs; int n_bits; n_loops = 0; for (n_bits = 0; n_bits <= 64; n_bits++) { for (dst_ofs = 0; dst_ofs < 64 - n_bits; dst_ofs++) { ovs_be64 dst = htonll(random_uint64()); ovs_be64 orig_dst = dst; ovs_be64 expect; if (n_bits == 64) { expect = htonll(0); } else { uint64_t mask = (UINT64_C(1) << n_bits) - 1; expect = orig_dst & ~htonll(mask << dst_ofs); } bitwise_zero(&dst, sizeof dst, dst_ofs, n_bits); if (expect != dst) { fprintf(stderr,"bitwise_zero(0x%016"PRIx64",8,%d, %d) " "yielded 0x%016"PRIx64" " "instead of the expected 0x%016"PRIx64"\n", ntohll(orig_dst), dst_ofs, n_bits, ntohll(dst), ntohll(expect)); abort(); } n_loops++; } } if (n_loops != 64 * (64 + 1) / 2) { abort(); } } static void test_bitwise_one(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned int n_loops; int dst_ofs; int n_bits; n_loops = 0; for (n_bits = 0; n_bits <= 64; n_bits++) { for (dst_ofs = 0; dst_ofs < 64 - n_bits; dst_ofs++) { ovs_be64 dst = htonll(random_uint64()); ovs_be64 orig_dst = dst; ovs_be64 expect; if (n_bits == 64) { expect = OVS_BE64_MAX; } else { uint64_t mask = (UINT64_C(1) << n_bits) - 1; expect = orig_dst | htonll(mask << dst_ofs); } bitwise_one(&dst, sizeof dst, dst_ofs, n_bits); if (expect != dst) { fprintf(stderr,"bitwise_one(0x%016"PRIx64",8,%d, %d) " "yielded 0x%016"PRIx64" " "instead of the expected 0x%016"PRIx64"\n", ntohll(orig_dst), dst_ofs, n_bits, ntohll(dst), ntohll(expect)); abort(); } n_loops++; } } if (n_loops != 64 * (64 + 1) / 2) { abort(); } } static void test_bitwise_is_all_zeros(struct ovs_cmdl_context *ctx OVS_UNUSED) { int n_loops; for (n_loops = 0; n_loops < 100; n_loops++) { ovs_be64 x = htonll(0); int i; for (i = 0; i < 64; i++) { ovs_be64 bit; int ofs, n; /* Change a random 0-bit into a 1-bit. */ do { bit = htonll(UINT64_C(1) << (random_range(64))); } while (x & bit); x |= bit; for (ofs = 0; ofs < 64; ofs++) { for (n = 0; n <= 64 - ofs; n++) { bool expect; bool answer; expect = (n == 64 ? x == 0 : !(x & htonll(((UINT64_C(1) << n) - 1) << ofs))); answer = bitwise_is_all_zeros(&x, sizeof x, ofs, n); if (expect != answer) { fprintf(stderr, "bitwise_is_all_zeros(0x%016"PRIx64",8,%d,%d " "returned %s instead of %s\n", ntohll(x), ofs, n, answer ? "true" : "false", expect ? "true" : "false"); abort(); } } } } } } static int trivial_bitwise_rscan(const void *p, unsigned int len, bool target, int start, int end) { int ofs; for (ofs = start; ofs > end; ofs--) { if (bitwise_get_bit(p, len, ofs) == target) { break; } } return ofs; } static void test_bitwise_rscan(struct ovs_cmdl_context *ctx OVS_UNUSED) { /* All 1s */ uint8_t s1[3] = {0xff, 0xff, 0xff}; /* Target is the first bit */ ovs_assert(23 == bitwise_rscan(s1, 3, 1, 23, -1)); /* Target is not found, return -1 */ ovs_assert(-1 == bitwise_rscan(s1, 3, 0, 23, -1)); /* Target is not found and end != -1, return end */ ovs_assert(20 == bitwise_rscan(s1, 3, 0, 23, 20)); /* bit 20 - 23 are 0s */ uint8_t s2[3] = {0x0f, 0xff, 0xff}; /* Target is in the first byte but not the first bit */ ovs_assert(19 == bitwise_rscan(s2, 3, 1, 23, -1)); /* Target exists before the start postion */ ovs_assert(18 == bitwise_rscan(s2, 3, 1, 18, -1)); /* Target exists after the end postion, return end */ ovs_assert(20 == bitwise_rscan(s2, 3, 1, 23, 20)); /* Target is at the end postion, return end */ ovs_assert(19 == bitwise_rscan(s2, 3, 1, 23, 19)); /* start == end, target at start */ ovs_assert(19 == bitwise_rscan(s2, 3, 1, 19, 19)); /* start == end, target not at start */ ovs_assert(20 == bitwise_rscan(s2, 3, 1, 20, 20)); /* Target is 0 ... */ ovs_assert(22 == bitwise_rscan(s2, 3, 0, 22, -1)); /* bit 4 - 23 are 0s */ uint8_t s3[3] = {0x00, 0x00, 0x0f}; /* Target is in the end byte */ ovs_assert(3 == bitwise_rscan(s3, 3, 1, 16, -1)); /* Target exists after the end byte, return end */ ovs_assert(15 == bitwise_rscan(s3, 3, 1, 23, 15)); /* Target exists in end byte but after the end bit, return end */ ovs_assert(4 == bitwise_rscan(s3, 3, 1, 23, 4)); /* Target is 0 ... */ ovs_assert(12 == bitwise_rscan(s3, 3, 0, 12, -1)); /* All 0s */ uint8_t s4[3] = {0x00, 0x00, 0x00}; /* Target not found */ ovs_assert(-1 == bitwise_rscan(s4, 3, 1, 23, -1)); /* Target is 0 ..., start is 0 */ ovs_assert(0 == bitwise_rscan(s4, 3, 0, 0, -1)); int n_loops; for (n_loops = 0; n_loops < 100; n_loops++) { ovs_be64 x = htonll(0); int i; for (i = 0; i < 64; i++) { ovs_be64 bit; /* Change a random 0-bit into a 1-bit. */ do { bit = htonll(UINT64_C(1) << (random_range(64))); } while (x & bit); x |= bit; for (int end = -1; end <= 63; end++) { for (int start = end; start <= 63; start++) { for (int target = 0; target < 2; target++) { bool expect = trivial_bitwise_rscan( &x, sizeof x, target, start, end); bool answer = bitwise_rscan( &x, sizeof x, target, start, end); if (expect != answer) { fprintf(stderr, "bitwise_rscan(0x%016"PRIx64",8,%s,%d,%d) " "returned %s instead of %s\n", ntohll(x), target ? "true" : "false", start, end, answer ? "true" : "false", expect ? "true" : "false"); abort(); } } } } } } } static void test_follow_symlinks(struct ovs_cmdl_context *ctx) { int i; for (i = 1; i < ctx->argc; i++) { char *target = follow_symlinks(ctx->argv[i]); puts(target); free(target); } } static void test_assert(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovs_assert(false); } static void test_ovs_scan(struct ovs_cmdl_context *ctx OVS_UNUSED) { char str[16], str2[16], str3[16]; long double ld, ld2; long long ll, ll2; signed char c, c2; ptrdiff_t pd, pd2; intmax_t im, im2; size_t sz, sz2; int n, n2, n3; double d, d2; short s, s2; float f, f2; long l, l2; int i, i2; ovs_assert(ovs_scan("", " ")); ovs_assert(ovs_scan(" ", " ")); ovs_assert(ovs_scan(" ", " ")); ovs_assert(ovs_scan(" \t ", " ")); ovs_assert(ovs_scan("xyzzy", "xyzzy")); ovs_assert(ovs_scan("xy%zzy", "xy%%zzy")); ovs_assert(!ovs_scan(" xy%zzy", "xy%%zzy")); ovs_assert(ovs_scan(" xy%\tzzy", " xy%% zzy")); ovs_assert(ovs_scan("123", "%d", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0", "%d", &i)); ovs_assert(i == 0); ovs_assert(!ovs_scan("123", "%d%d", &i, &i2)); ovs_assert(ovs_scan("+123", "%d", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("-123", "%d", &i)); ovs_assert(i == -123); ovs_assert(ovs_scan("0123", "%d", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan(" 123", "%d", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0x123", "%d", &i)); ovs_assert(i == 0); ovs_assert(ovs_scan("123", "%2d %d", &i, &i2)); ovs_assert(i == 12); ovs_assert(i2 == 3); ovs_assert(ovs_scan("+123", "%2d %d", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%2d %d", &i, &i2)); ovs_assert(i == -1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("0123", "%2d %d", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("123", "%*2d %d", &i)); ovs_assert(i == 3); ovs_assert(ovs_scan("+123", "%2d %*d", &i)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%*2d %*d")); ovs_assert(ovs_scan("123", "%u", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0", "%u", &i)); ovs_assert(i == 0); ovs_assert(!ovs_scan("123", "%u%u", &i, &i2)); ovs_assert(ovs_scan("+123", "%u", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("-123", "%u", &i)); ovs_assert(i == -123); ovs_assert(ovs_scan("0123", "%u", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan(" 123", "%u", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0x123", "%u", &i)); ovs_assert(i == 0); ovs_assert(ovs_scan("123", "%2u %u", &i, &i2)); ovs_assert(i == 12); ovs_assert(i2 == 3); ovs_assert(ovs_scan("+123", "%2u %u", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%2u %u", &i, &i2)); ovs_assert(i == -1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("0123", "%2u %u", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("123", "%*2u %u", &i)); ovs_assert(i == 3); ovs_assert(ovs_scan("+123", "%2u %*u", &i)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%*2u %*u")); ovs_assert(ovs_scan("123", "%i", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0", "%i", &i)); ovs_assert(i == 0); ovs_assert(!ovs_scan("123", "%i%i", &i, &i2)); ovs_assert(ovs_scan("+123", "%i", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("-123", "%i", &i)); ovs_assert(i == -123); ovs_assert(ovs_scan("0123", "%i", &i)); ovs_assert(i == 0123); ovs_assert(ovs_scan(" 123", "%i", &i)); ovs_assert(i == 123); ovs_assert(ovs_scan("0x123", "%i", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan("123", "%2i %i", &i, &i2)); ovs_assert(i == 12); ovs_assert(i2 == 3); ovs_assert(ovs_scan("+123", "%2i %i", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%2i %i", &i, &i2)); ovs_assert(i == -1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("0123", "%2i %i", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("123", "%*2i %i", &i)); ovs_assert(i == 3); ovs_assert(ovs_scan("+123", "%2i %*i", &i)); ovs_assert(i == 1); ovs_assert(i2 == 23); ovs_assert(ovs_scan("-123", "%*2i %*i")); ovs_assert(ovs_scan("123", "%o", &i)); ovs_assert(i == 0123); ovs_assert(ovs_scan("0", "%o", &i)); ovs_assert(i == 0); ovs_assert(!ovs_scan("123", "%o%o", &i, &i2)); ovs_assert(ovs_scan("+123", "%o", &i)); ovs_assert(i == 0123); ovs_assert(ovs_scan("-123", "%o", &i)); ovs_assert(i == -0123); ovs_assert(ovs_scan("0123", "%o", &i)); ovs_assert(i == 0123); ovs_assert(ovs_scan(" 123", "%o", &i)); ovs_assert(i == 0123); ovs_assert(ovs_scan("0x123", "%o", &i)); ovs_assert(i == 0); ovs_assert(ovs_scan("123", "%2o %o", &i, &i2)); ovs_assert(i == 012); ovs_assert(i2 == 3); ovs_assert(ovs_scan("+123", "%2o %o", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 023); ovs_assert(ovs_scan("-123", "%2o %o", &i, &i2)); ovs_assert(i == -1); ovs_assert(i2 == 023); ovs_assert(ovs_scan("0123", "%2o %o", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 023); ovs_assert(ovs_scan("123", "%*2o %o", &i)); ovs_assert(i == 3); ovs_assert(ovs_scan("+123", "%2o %*o", &i)); ovs_assert(i == 1); ovs_assert(i2 == 023); ovs_assert(ovs_scan("-123", "%*2o %*o")); ovs_assert(ovs_scan("123", "%x", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan("0", "%x", &i)); ovs_assert(i == 0); ovs_assert(!ovs_scan("123", "%x%x", &i, &i2)); ovs_assert(ovs_scan("+123", "%x", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan("-123", "%x", &i)); ovs_assert(i == -0x123); ovs_assert(ovs_scan("0123", "%x", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan(" 123", "%x", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan("0x123", "%x", &i)); ovs_assert(i == 0x123); ovs_assert(ovs_scan("123", "%2x %x", &i, &i2)); ovs_assert(i == 0x12); ovs_assert(i2 == 3); ovs_assert(ovs_scan("+123", "%2x %x", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 0x23); ovs_assert(ovs_scan("-123", "%2x %x", &i, &i2)); ovs_assert(i == -1); ovs_assert(i2 == 0x23); ovs_assert(ovs_scan("0123", "%2x %x", &i, &i2)); ovs_assert(i == 1); ovs_assert(i2 == 0x23); ovs_assert(ovs_scan("123", "%*2x %x", &i)); ovs_assert(i == 3); ovs_assert(ovs_scan("+123", "%2x %*x", &i)); ovs_assert(i == 1); ovs_assert(i2 == 0x23); ovs_assert(ovs_scan("-123", "%*2x %*x")); ovs_assert(ovs_scan("123", "%hd", &s)); ovs_assert(s == 123); ovs_assert(!ovs_scan("123", "%hd%hd", &s, &s2)); ovs_assert(ovs_scan("+123", "%hd", &s)); ovs_assert(s == 123); ovs_assert(ovs_scan("-123", "%hd", &s)); ovs_assert(s == -123); ovs_assert(ovs_scan("0123", "%hd", &s)); ovs_assert(s == 123); ovs_assert(ovs_scan(" 123", "%hd", &s)); ovs_assert(s == 123); ovs_assert(ovs_scan("0x123", "%hd", &s)); ovs_assert(s == 0); ovs_assert(ovs_scan("123", "%2hd %hd", &s, &s2)); ovs_assert(s == 12); ovs_assert(s2 == 3); ovs_assert(ovs_scan("+123", "%2hd %hd", &s, &s2)); ovs_assert(s == 1); ovs_assert(s2 == 23); ovs_assert(ovs_scan("-123", "%2hd %hd", &s, &s2)); ovs_assert(s == -1); ovs_assert(s2 == 23); ovs_assert(ovs_scan("0123", "%2hd %hd", &s, &s2)); ovs_assert(s == 1); ovs_assert(s2 == 23); ovs_assert(ovs_scan("123", "%hhd", &c)); ovs_assert(c == 123); ovs_assert(ovs_scan("0", "%hhd", &c)); ovs_assert(c == 0); ovs_assert(!ovs_scan("123", "%hhd%hhd", &c, &c2)); ovs_assert(ovs_scan("+123", "%hhd", &c)); ovs_assert(c == 123); ovs_assert(ovs_scan("-123", "%hhd", &c)); ovs_assert(c == -123); ovs_assert(ovs_scan("0123", "%hhd", &c)); ovs_assert(c == 123); ovs_assert(ovs_scan(" 123", "%hhd", &c)); ovs_assert(c == 123); ovs_assert(ovs_scan("0x123", "%hhd", &c)); ovs_assert(c == 0); ovs_assert(ovs_scan("123", "%2hhd %hhd", &c, &c2)); ovs_assert(c == 12); ovs_assert(c2 == 3); ovs_assert(ovs_scan("+123", "%2hhd %hhd", &c, &c2)); ovs_assert(c == 1); ovs_assert(c2 == 23); ovs_assert(ovs_scan("-123", "%2hhd %hhd", &c, &c2)); ovs_assert(c == -1); ovs_assert(c2 == 23); ovs_assert(ovs_scan("0123", "%2hhd %hhd", &c, &c2)); ovs_assert(c == 1); ovs_assert(c2 == 23); ovs_assert(ovs_scan("123", "%ld", &l)); ovs_assert(l == 123); ovs_assert(ovs_scan("0", "%ld", &l)); ovs_assert(l == 0); ovs_assert(!ovs_scan("123", "%ld%ld", &l, &l2)); ovs_assert(ovs_scan("+123", "%ld", &l)); ovs_assert(l == 123); ovs_assert(ovs_scan("-123", "%ld", &l)); ovs_assert(l == -123); ovs_assert(ovs_scan("0123", "%ld", &l)); ovs_assert(l == 123); ovs_assert(ovs_scan(" 123", "%ld", &l)); ovs_assert(l == 123); ovs_assert(ovs_scan("0x123", "%ld", &l)); ovs_assert(l == 0); ovs_assert(ovs_scan("123", "%2ld %ld", &l, &l2)); ovs_assert(l == 12); ovs_assert(l2 == 3); ovs_assert(ovs_scan("+123", "%2ld %ld", &l, &l2)); ovs_assert(l == 1); ovs_assert(l2 == 23); ovs_assert(ovs_scan("-123", "%2ld %ld", &l, &l2)); ovs_assert(l == -1); ovs_assert(l2 == 23); ovs_assert(ovs_scan("0123", "%2ld %ld", &l, &l2)); ovs_assert(l == 1); ovs_assert(l2 == 23); ovs_assert(ovs_scan("123", "%lld", &ll)); ovs_assert(ll == 123); ovs_assert(ovs_scan("0", "%lld", &ll)); ovs_assert(ll == 0); ovs_assert(!ovs_scan("123", "%lld%lld", &ll, &ll2)); ovs_assert(ovs_scan("+123", "%lld", &ll)); ovs_assert(ll == 123); ovs_assert(ovs_scan("-123", "%lld", &ll)); ovs_assert(ll == -123); ovs_assert(ovs_scan("0123", "%lld", &ll)); ovs_assert(ll == 123); ovs_assert(ovs_scan(" 123", "%lld", &ll)); ovs_assert(ll == 123); ovs_assert(ovs_scan("0x123", "%lld", &ll)); ovs_assert(ll == 0); ovs_assert(ovs_scan("123", "%2lld %lld", &ll, &ll2)); ovs_assert(ll == 12); ovs_assert(ll2 == 3); ovs_assert(ovs_scan("+123", "%2lld %lld", &ll, &ll2)); ovs_assert(ll == 1); ovs_assert(ll2 == 23); ovs_assert(ovs_scan("-123", "%2lld %lld", &ll, &ll2)); ovs_assert(ll == -1); ovs_assert(ll2 == 23); ovs_assert(ovs_scan("0123", "%2lld %lld", &ll, &ll2)); ovs_assert(ll == 1); ovs_assert(ll2 == 23); ovs_assert(ovs_scan("123", "%jd", &im)); ovs_assert(im == 123); ovs_assert(ovs_scan("0", "%jd", &im)); ovs_assert(im == 0); ovs_assert(!ovs_scan("123", "%jd%jd", &im, &im2)); ovs_assert(ovs_scan("+123", "%jd", &im)); ovs_assert(im == 123); ovs_assert(ovs_scan("-123", "%jd", &im)); ovs_assert(im == -123); ovs_assert(ovs_scan("0123", "%jd", &im)); ovs_assert(im == 123); ovs_assert(ovs_scan(" 123", "%jd", &im)); ovs_assert(im == 123); ovs_assert(ovs_scan("0x123", "%jd", &im)); ovs_assert(im == 0); ovs_assert(ovs_scan("123", "%2jd %jd", &im, &im2)); ovs_assert(im == 12); ovs_assert(im2 == 3); ovs_assert(ovs_scan("+123", "%2jd %jd", &im, &im2)); ovs_assert(im == 1); ovs_assert(im2 == 23); ovs_assert(ovs_scan("-123", "%2jd %jd", &im, &im2)); ovs_assert(im == -1); ovs_assert(im2 == 23); ovs_assert(ovs_scan("0123", "%2jd %jd", &im, &im2)); ovs_assert(im == 1); ovs_assert(im2 == 23); ovs_assert(ovs_scan("123", "%td", &pd)); ovs_assert(pd == 123); ovs_assert(ovs_scan("0", "%td", &pd)); ovs_assert(pd == 0); ovs_assert(!ovs_scan("123", "%td%td", &pd, &pd2)); ovs_assert(ovs_scan("+123", "%td", &pd)); ovs_assert(pd == 123); ovs_assert(ovs_scan("-123", "%td", &pd)); ovs_assert(pd == -123); ovs_assert(ovs_scan("0123", "%td", &pd)); ovs_assert(pd == 123); ovs_assert(ovs_scan(" 123", "%td", &pd)); ovs_assert(pd == 123); ovs_assert(ovs_scan("0x123", "%td", &pd)); ovs_assert(pd == 0); ovs_assert(ovs_scan("123", "%2td %td", &pd, &pd2)); ovs_assert(pd == 12); ovs_assert(pd2 == 3); ovs_assert(ovs_scan("+123", "%2td %td", &pd, &pd2)); ovs_assert(pd == 1); ovs_assert(pd2 == 23); ovs_assert(ovs_scan("-123", "%2td %td", &pd, &pd2)); ovs_assert(pd == -1); ovs_assert(pd2 == 23); ovs_assert(ovs_scan("0123", "%2td %td", &pd, &pd2)); ovs_assert(pd == 1); ovs_assert(pd2 == 23); ovs_assert(ovs_scan("123", "%zd", &sz)); ovs_assert(sz == 123); ovs_assert(ovs_scan("0", "%zd", &sz)); ovs_assert(sz == 0); ovs_assert(!ovs_scan("123", "%zd%zd", &sz, &sz2)); ovs_assert(ovs_scan("+123", "%zd", &sz)); ovs_assert(sz == 123); ovs_assert(ovs_scan("-123", "%zd", &sz)); ovs_assert(sz == -123); ovs_assert(ovs_scan("0123", "%zd", &sz)); ovs_assert(sz == 123); ovs_assert(ovs_scan(" 123", "%zd", &sz)); ovs_assert(sz == 123); ovs_assert(ovs_scan("0x123", "%zd", &sz)); ovs_assert(sz == 0); ovs_assert(ovs_scan("123", "%2zd %zd", &sz, &sz2)); ovs_assert(sz == 12); ovs_assert(sz2 == 3); ovs_assert(ovs_scan("+123", "%2zd %zd", &sz, &sz2)); ovs_assert(sz == 1); ovs_assert(sz2 == 23); ovs_assert(ovs_scan("-123", "%2zd %zd", &sz, &sz2)); ovs_assert(sz == -1); ovs_assert(sz2 == 23); ovs_assert(ovs_scan("0123", "%2zd %zd", &sz, &sz2)); ovs_assert(sz == 1); ovs_assert(sz2 == 23); ovs_assert(ovs_scan("0.25", "%f", &f)); ovs_assert(f == 0.25); ovs_assert(ovs_scan("1.0", "%f", &f)); ovs_assert(f == 1.0); ovs_assert(ovs_scan("-5", "%f", &f)); ovs_assert(f == -5.0); ovs_assert(ovs_scan("+6", "%f", &f)); ovs_assert(f == 6.0); ovs_assert(ovs_scan("-1e5", "%f", &f)); ovs_assert(f == -1e5); ovs_assert(ovs_scan("-.25", "%f", &f)); ovs_assert(f == -.25); ovs_assert(ovs_scan("+123.e1", "%f", &f)); ovs_assert(f == 1230.0); ovs_assert(ovs_scan("25e-2", "%f", &f)); ovs_assert(f == 0.25); ovs_assert(ovs_scan("0.25", "%1f %f", &f, &f2)); ovs_assert(f == 0); ovs_assert(f2 == 0.25); ovs_assert(ovs_scan("1.0", "%2f %f", &f, &f2)); ovs_assert(f == 1.0); ovs_assert(f2 == 0.0); ovs_assert(!ovs_scan("-5", "%1f", &f)); ovs_assert(!ovs_scan("+6", "%1f", &f)); ovs_assert(!ovs_scan("-1e5", "%2f %*f", &f)); ovs_assert(f == -1); ovs_assert(!ovs_scan("-.25", "%2f", &f)); ovs_assert(!ovs_scan("+123.e1", "%6f", &f)); ovs_assert(!ovs_scan("25e-2", "%4f", &f)); ovs_assert(ovs_scan("0.25", "%lf", &d)); ovs_assert(d == 0.25); ovs_assert(ovs_scan("1.0", "%lf", &d)); ovs_assert(d == 1.0); ovs_assert(ovs_scan("-5", "%lf", &d)); ovs_assert(d == -5.0); ovs_assert(ovs_scan("+6", "%lf", &d)); ovs_assert(d == 6.0); ovs_assert(ovs_scan("-1e5", "%lf", &d)); ovs_assert(d == -1e5); ovs_assert(ovs_scan("-.25", "%lf", &d)); ovs_assert(d == -.25); ovs_assert(ovs_scan("+123.e1", "%lf", &d)); ovs_assert(d == 1230.0); ovs_assert(ovs_scan("25e-2", "%lf", &d)); ovs_assert(d == 0.25); ovs_assert(ovs_scan("0.25", "%1lf %lf", &d, &d2)); ovs_assert(d == 0); ovs_assert(d2 == 0.25); ovs_assert(ovs_scan("1.0", "%2lf %lf", &d, &d2)); ovs_assert(d == 1.0); ovs_assert(d2 == 0.0); ovs_assert(!ovs_scan("-5", "%1lf", &d)); ovs_assert(!ovs_scan("+6", "%1lf", &d)); ovs_assert(!ovs_scan("-1e5", "%2lf %*f", &d)); ovs_assert(d == -1); ovs_assert(!ovs_scan("-.25", "%2lf", &d)); ovs_assert(!ovs_scan("+123.e1", "%6lf", &d)); ovs_assert(!ovs_scan("25e-2", "%4lf", &d)); ovs_assert(ovs_scan("0.25", "%Lf", &ld)); ovs_assert(ld == 0.25); ovs_assert(ovs_scan("1.0", "%Lf", &ld)); ovs_assert(ld == 1.0); ovs_assert(ovs_scan("-5", "%Lf", &ld)); ovs_assert(ld == -5.0); ovs_assert(ovs_scan("+6", "%Lf", &ld)); ovs_assert(ld == 6.0); ovs_assert(ovs_scan("-1e5", "%Lf", &ld)); ovs_assert(ld == -1e5); ovs_assert(ovs_scan("-.25", "%Lf", &ld)); ovs_assert(ld == -.25); ovs_assert(ovs_scan("+123.e1", "%Lf", &ld)); ovs_assert(ld == 1230.0); ovs_assert(ovs_scan("25e-2", "%Lf", &ld)); ovs_assert(ld == 0.25); ovs_assert(ovs_scan("0.25", "%1Lf %Lf", &ld, &ld2)); ovs_assert(ld == 0); ovs_assert(ld2 == 0.25); ovs_assert(ovs_scan("1.0", "%2Lf %Lf", &ld, &ld2)); ovs_assert(ld == 1.0); ovs_assert(ld2 == 0.0); ovs_assert(!ovs_scan("-5", "%1Lf", &ld)); ovs_assert(!ovs_scan("+6", "%1Lf", &ld)); ovs_assert(!ovs_scan("-1e5", "%2Lf %*f", &ld)); ovs_assert(ld == -1); ovs_assert(!ovs_scan("-.25", "%2Lf", &ld)); ovs_assert(!ovs_scan("+123.e1", "%6Lf", &ld)); ovs_assert(!ovs_scan("25e-2", "%4Lf", &ld)); ovs_assert(ovs_scan(" Hello,\tworld ", "%*s%n%*s%n", &n, &n2)); ovs_assert(n == 7); ovs_assert(n2 == 13); ovs_assert(!ovs_scan(" Hello,\tworld ", "%*s%*s%*s")); ovs_assert(ovs_scan(" Hello,\tworld ", "%6s%n%5s%n", str, &n, str2, &n2)); ovs_assert(!strcmp(str, "Hello,")); ovs_assert(n == 7); ovs_assert(!strcmp(str2, "world")); ovs_assert(n2 == 13); ovs_assert(ovs_scan(" Hello,\tworld ", "%5s%5s%5s", str, str2, str3)); ovs_assert(!strcmp(str, "Hello")); ovs_assert(!strcmp(str2, ",")); ovs_assert(!strcmp(str3, "world")); ovs_assert(!ovs_scan(" ", "%*s")); ovs_assert(ovs_scan(" Hello,\tworld ", "%*c%n%*c%n%c%n", &n, &n2, &c, &n3)); ovs_assert(n == 1); ovs_assert(n2 == 2); ovs_assert(c == 'e'); ovs_assert(n3 == 3); ovs_assert(ovs_scan(" Hello,\tworld ", "%*5c%5c", str)); ovs_assert(!memcmp(str, "o,\two", 5)); ovs_assert(!ovs_scan(" Hello,\tworld ", "%*15c")); ovs_assert(ovs_scan("0x1234xyzzy", "%9[x0-9a-fA-F]%n", str, &n)); ovs_assert(!strcmp(str, "0x1234x")); ovs_assert(n == 7); ovs_assert(ovs_scan("foo:bar=baz", "%5[^:=]%n:%5[^:=]%n=%5[^:=]%n", str, &n, str2, &n2, str3, &n3)); ovs_assert(!strcmp(str, "foo")); ovs_assert(n == 3); ovs_assert(!strcmp(str2, "bar")); ovs_assert(n2 == 7); ovs_assert(!strcmp(str3, "baz")); ovs_assert(n3 == 11); ovs_assert(!ovs_scan(" ", "%*[0-9]")); ovs_assert(ovs_scan("0x123a]4xyzzy-", "%[]x0-9a-fA-F]", str)); ovs_assert(!strcmp(str, "0x123a]4x")); ovs_assert(ovs_scan("abc]xyz","%[^]xyz]", str)); ovs_assert(!strcmp(str, "abc")); ovs_assert(!ovs_scan("0x123a]4xyzzy-", "%[x0-9]a-fA-F]", str)); ovs_assert(ovs_scan("0x12-3]xyz", "%[x0-9a-f-]", str)); ovs_assert(!strcmp(str, "0x12-3")); ovs_assert(ovs_scan("0x12-3]xyz", "%[^a-f-]", str)); ovs_assert(!strcmp(str, "0x12")); ovs_assert(sscanf("0x12-3]xyz", "%[^-a-f]", str)); ovs_assert(!strcmp(str, "0x12")); } static void test_snprintf(struct ovs_cmdl_context *ctx OVS_UNUSED) { char s[16]; /* GCC 7+ and Clang 18+ warn about the following calls that truncate * a string using snprintf(). We're testing that truncation works * properly, so temporarily disable the warning. */ #if __GNUC__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" #endif #if __clang_major__ >= 18 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-truncation" #endif ovs_assert(snprintf(s, 4, "abcde") == 5); ovs_assert(!strcmp(s, "abc")); ovs_assert(snprintf(s, 5, "abcde") == 5); ovs_assert(!strcmp(s, "abcd")); #if __GNUC__ >= 7 #pragma GCC diagnostic pop #endif #if __clang_major__ >= 18 #pragma clang diagnostic pop #endif ovs_assert(snprintf(s, 6, "abcde") == 5); ovs_assert(!strcmp(s, "abcde")); ovs_assert(snprintf(NULL, 0, "abcde") == 5); } static void check_sat(long long int x, long long int y, const char *op, long long int r_a, long long int r_b) { if (r_a != r_b) { fprintf(stderr, "%lld %s %lld saturates differently: %lld vs %lld\n", x, op, y, r_a, r_b); abort(); } } static void test_sat_math(struct ovs_cmdl_context *ctx OVS_UNUSED) { long long int values[] = { LLONG_MIN, LLONG_MIN + 1, LLONG_MIN + 2, LLONG_MIN + 3, LLONG_MIN + 4, LLONG_MIN + 5, LLONG_MIN + 6, LLONG_MIN + 7, LLONG_MIN / 2, LLONG_MIN / 3, LLONG_MIN / 4, LLONG_MIN / 5, -1000, -50, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 50, 1000, LLONG_MAX / 5, LLONG_MAX / 4, LLONG_MAX / 3, LLONG_MAX / 2, LLONG_MAX - 7, LLONG_MAX - 6, LLONG_MAX - 5, LLONG_MAX - 4, LLONG_MAX - 3, LLONG_MAX - 2, LLONG_MAX - 1, LLONG_MAX, }; /* Compare the behavior of our local implementation of these functions * against the compiler implementation or its fallback. (If the fallback * is in use then this is comparing the fallback to itself, so this test is * only really useful if the compiler has an implementation.) */ for (long long int *x = values; x < values + ARRAY_SIZE(values); x++) { for (long long int *y = values; y < values + ARRAY_SIZE(values); y++) { check_sat(*x, *y, "+", llsat_add(*x, *y), llsat_add__(*x, *y)); check_sat(*x, *y, "-", llsat_sub(*x, *y), llsat_sub__(*x, *y)); check_sat(*x, *y, "*", llsat_mul(*x, *y), llsat_mul__(*x, *y)); } } } #ifndef _WIN32 static void test_file_name(struct ovs_cmdl_context *ctx) { int i; for (i = 1; i < ctx->argc; i++) { char *dir, *base; dir = dir_name(ctx->argv[i]); puts(dir); free(dir); base = base_name(ctx->argv[i]); puts(base); free(base); } } #endif /* _WIN32 */ static const struct ovs_cmdl_command commands[] = { {"ctz", NULL, 0, 0, test_ctz, OVS_RO}, {"clz", NULL, 0, 0, test_clz, OVS_RO}, {"round_up_pow2", NULL, 0, 0, test_round_up_pow2, OVS_RO}, {"round_down_pow2", NULL, 0, 0, test_round_down_pow2, OVS_RO}, {"count_1bits", NULL, 0, 0, test_count_1bits, OVS_RO}, {"log_2_floor", NULL, 0, 0, test_log_2_floor, OVS_RO}, {"bitwise_copy", NULL, 0, 0, test_bitwise_copy, OVS_RO}, {"bitwise_zero", NULL, 0, 0, test_bitwise_zero, OVS_RO}, {"bitwise_one", NULL, 0, 0, test_bitwise_one, OVS_RO}, {"bitwise_is_all_zeros", NULL, 0, 0, test_bitwise_is_all_zeros, OVS_RO}, {"bitwise_rscan", NULL, 0, 0, test_bitwise_rscan, OVS_RO}, {"follow-symlinks", NULL, 1, INT_MAX, test_follow_symlinks, OVS_RO}, {"assert", NULL, 0, 0, test_assert, OVS_RO}, {"ovs_scan", NULL, 0, 0, test_ovs_scan, OVS_RO}, {"snprintf", NULL, 0, 0, test_snprintf, OVS_RO}, {"sat_math", NULL, 0, 0, test_sat_math, OVS_RO}, #ifndef _WIN32 {"file_name", NULL, 1, INT_MAX, test_file_name, OVS_RO}, #endif {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void parse_options(int argc, char *argv[]) { enum { VLOG_OPTION_ENUMS }; static const struct option long_options[] = { VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void test_util_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; set_program_name(argv[0]); parse_options(argc, argv); /* On Windows, stderr is fully buffered if connected to a pipe. * Make it _IONBF so that an abort does not miss log contents. * POSIX doesn't define the circumstances in which stderr is * fully buffered either. */ setvbuf(stderr, NULL, _IONBF, 0); ctx.argc = argc - optind; ctx.argv = argv + optind; ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-util", test_util_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-uuid.c000066400000000000000000000022131514270232600222070ustar00rootroot00000000000000/* * Copyright (c) 2009, 2014 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include "util.h" #include "uuid.h" #include #include "ovstest.h" static void test_uuid_main(int argc, char *argv[]) { struct uuid uuid; if (argc == 1) { uuid_generate(&uuid); } else if (argc == 2) { if (!uuid_from_string(&uuid, argv[1])) { ovs_fatal(0, "\"%s\" is not a valid UUID", argv[1]); } } else { ovs_fatal(0, "usage: %s [UUID]", argv[0]); } printf(UUID_FMT"\n", UUID_ARGS(&uuid)); } OVSTEST_REGISTER("test-uuid", test_uuid_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-uuidset.c000066400000000000000000000041441514270232600227300ustar00rootroot00000000000000/* * Copyright (c) 2022 Red Hat, Inc. * * 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. */ #include #include "lib/util.h" #include "lib/uuidset.h" #include "ovstest.h" static void test_uuidset_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct uuidset set = UUIDSET_INITIALIZER(&set); struct uuid uuids[2]; for (size_t i = 0; i < ARRAY_SIZE(uuids); i++) { uuid_generate(&uuids[i]); } ovs_assert(uuidset_is_empty(&set)); for (size_t i = 0; i < ARRAY_SIZE(uuids); i++) { struct uuid *u = &uuids[i]; if (i == 0) { ovs_assert(uuidset_is_empty(&set)); } else { ovs_assert(!uuidset_is_empty(&set)); } ovs_assert(uuidset_count(&set) == i); ovs_assert(!uuidset_contains(&set, u)); ovs_assert(!uuidset_find_and_delete(&set, u)); /* Insert twice to check set property. */ uuidset_insert(&set, u); uuidset_insert(&set, u); ovs_assert(uuidset_count(&set) == i + 1); struct uuidset_node *n = uuidset_find(&set, u); ovs_assert(n); uuidset_delete(&set, n); ovs_assert(uuidset_count(&set) == i); ovs_assert(!uuidset_contains(&set, u)); uuidset_insert(&set, u); ovs_assert(uuidset_count(&set) == i + 1); ovs_assert(uuidset_contains(&set, u)); ovs_assert(uuidset_find_and_delete(&set, u)); ovs_assert(uuidset_count(&set) == i); ovs_assert(!uuidset_contains(&set, u)); uuidset_insert(&set, u); } uuidset_destroy(&set); } OVSTEST_REGISTER("test-uuidset", test_uuidset_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-vconn.c000066400000000000000000000326471514270232600224020ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2017, 2019 Nicira, Inc. * * 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. */ #include #undef NDEBUG #include #include #include #include #include #include #include "command-line.h" #include "fatal-signal.h" #include "openflow/openflow.h" #include "openvswitch/ofp-msgs.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovstest.h" #include "openvswitch/poll-loop.h" #include "socket-util.h" #include "stream.h" #include "stream-ssl.h" #include "timeval.h" #include "util.h" #define TIMEOUT 10 struct fake_pvconn { const char *type; char *pvconn_name; char *vconn_name; struct pstream *pstream; }; static void check(int a, int b, const char *as, const char *file, int line) { if (a != b) { ovs_fatal(0, "%s:%d: %s is %d but should be %d", file, line, as, a, b); } } #define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__) static void check_errno(int a, int b, const char *as, const char *file, int line) { if (a != b) { char *str_b = xstrdup(ovs_strerror(abs(b))); ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)", file, line, as, a, ovs_strerror(abs(a)), b, str_b); } } #define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__) static void fpv_create(const char *type, struct fake_pvconn *fpv) { #ifdef HAVE_OPENSSL if (!strcmp(type, "ssl")) { stream_ssl_set_private_key_file("testpki-privkey.pem"); stream_ssl_set_certificate_file("testpki-cert.pem"); stream_ssl_set_ca_cert_file("testpki-cacert.pem", false); } #endif fpv->type = type; if (!strcmp(type, "unix")) { static int unix_count = 0; char *bind_path; bind_path = xasprintf("fake-pvconn.%d", unix_count++); fpv->pvconn_name = xasprintf("punix:%s", bind_path); fpv->vconn_name = xasprintf("unix:%s", bind_path); CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream, DSCP_DEFAULT), 0); free(bind_path); } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) { char *s, *port, *save_ptr = NULL; char *open_name; open_name = xasprintf("p%s:0:127.0.0.1", type); CHECK_ERRNO(pstream_open(open_name, &fpv->pstream, DSCP_DEFAULT), 0); /* Extract bound port number from pstream name. */ s = xstrdup(pstream_get_name(fpv->pstream)); strtok_r(s, ":", &save_ptr); port = strtok_r(NULL, ":", &save_ptr); /* Save info. */ fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream)); fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port); free(open_name); free(s); } else { abort(); } } static struct stream * fpv_accept(struct fake_pvconn *fpv) { struct stream *stream; CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0); return stream; } static void fpv_close(struct fake_pvconn *fpv) { pstream_close(fpv->pstream); fpv->pstream = NULL; } static void fpv_destroy(struct fake_pvconn *fpv) { fpv_close(fpv); free(fpv->pvconn_name); free(fpv->vconn_name); } /* Connects to a fake_pvconn with vconn_open(), then closes the listener and * verifies that vconn_connect() reports 'expected_error'. */ static void test_refuse_connection(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct fake_pvconn fpv; struct vconn *vconn; int error; fpv_create(type, &fpv); CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); fpv_close(&fpv); vconn_run(vconn); error = vconn_connect_block(vconn, (TIMEOUT - 2) * 1000); if (!strcmp(type, "tcp")) { if (error != ECONNRESET && error != EPIPE && error != ETIMEDOUT && error != ECONNREFUSED #ifdef _WIN32 && error != WSAECONNRESET #endif ) { ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", error, ovs_strerror(error)); } } else if (!strcmp(type, "unix")) { CHECK_ERRNO(error, EPIPE); } else if (!strcmp(type, "ssl")) { if (error != EPROTO && error != ECONNRESET && error != ETIMEDOUT) { ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", error, ovs_strerror(error)); } } else { ovs_fatal(0, "invalid connection type %s", type); } vconn_close(vconn); fpv_destroy(&fpv); } /* Connects to a fake_pvconn with vconn_open(), accepts that connection and * closes it immediately, and verifies that vconn_connect() reports * 'expected_error'. */ static void test_accept_then_close(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct fake_pvconn fpv; struct vconn *vconn; int error; fpv_create(type, &fpv); CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); vconn_run(vconn); stream_close(fpv_accept(&fpv)); fpv_close(&fpv); error = vconn_connect_block(vconn, -1); if (!strcmp(type, "tcp") || !strcmp(type, "unix")) { if (error != ECONNRESET && error != EPIPE #ifdef _WIN32 && error != WSAECONNRESET #endif ) { ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", error, ovs_strerror(error)); } } else { CHECK_ERRNO(error, EPROTO); } vconn_close(vconn); fpv_destroy(&fpv); } /* Connects to a fake_pvconn with vconn_open(), accepts that connection and * reads the hello message from it, then closes the connection and verifies * that vconn_connect() reports 'expected_error'. */ static void test_read_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct fake_pvconn fpv; struct vconn *vconn; struct stream *stream; int error; fpv_create(type, &fpv); CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); vconn_run(vconn); stream = fpv_accept(&fpv); fpv_destroy(&fpv); for (;;) { struct ofp_header hello; int retval; retval = stream_recv(stream, &hello, sizeof hello); if (retval == sizeof hello) { enum ofpraw raw; CHECK(hello.version, OFP15_VERSION); CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0); CHECK(raw, OFPRAW_OFPT_HELLO); CHECK(ntohs(hello.length), sizeof hello); break; } else { CHECK_ERRNO(retval, -EAGAIN); } vconn_run(vconn); CHECK_ERRNO(vconn_connect(vconn), EAGAIN); vconn_run_wait(vconn); vconn_connect_wait(vconn); stream_recv_wait(stream); poll_block(); } stream_close(stream); error = vconn_connect_block(vconn, -1); if (error != ECONNRESET && error != EPIPE) { ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", error, ovs_strerror(error)); } vconn_close(vconn); } /* Connects to a fake_pvconn with vconn_open(), accepts that connection and * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO * message), then verifies that vconn_connect() reports * 'expect_connect_error'. */ static void test_send_hello(const char *type, const void *out, size_t out_size, int expect_connect_error) { struct fake_pvconn fpv; struct vconn *vconn; bool read_hello, connected; struct ofpbuf *msg; struct stream *stream; size_t n_sent; fpv_create(type, &fpv); CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); vconn_run(vconn); stream = fpv_accept(&fpv); fpv_destroy(&fpv); n_sent = 0; while (n_sent < out_size) { int retval; retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent); if (retval > 0) { n_sent += retval; } else if (retval == -EAGAIN) { stream_run(stream); vconn_run(vconn); stream_recv_wait(stream); vconn_connect_wait(vconn); vconn_run_wait(vconn); poll_block(); } else { ovs_fatal(0, "stream_send returned unexpected value %d", retval); } } read_hello = connected = false; for (;;) { if (!read_hello) { struct ofp_header hello; int retval = stream_recv(stream, &hello, sizeof hello); if (retval == sizeof hello) { enum ofpraw raw; CHECK(hello.version, OFP15_VERSION); CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0); CHECK(raw, OFPRAW_OFPT_HELLO); CHECK(ntohs(hello.length), sizeof hello); read_hello = true; } else { CHECK_ERRNO(retval, -EAGAIN); } } vconn_run(vconn); if (!connected) { int error = vconn_connect(vconn); if (error == expect_connect_error) { if (!error) { connected = true; } else { stream_close(stream); vconn_close(vconn); return; } } else { CHECK_ERRNO(error, EAGAIN); } } if (read_hello && connected) { break; } vconn_run_wait(vconn); if (!connected) { vconn_connect_wait(vconn); } if (!read_hello) { stream_recv_wait(stream); } poll_block(); } stream_close(stream); CHECK_ERRNO(vconn_recv_block(vconn, &msg), EOF); vconn_close(vconn); } /* Try connecting and sending a normal hello, which should succeed. */ static void test_send_plain_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofpbuf *hello; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, htonl(0x12345678), 0); test_send_hello(type, hello->data, hello->size, 0); ofpbuf_delete(hello); } /* Try connecting and sending an extra-long hello, which should succeed (since * the specification says that implementations must accept and ignore extra * data). */ static void test_send_long_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofpbuf *hello; enum { EXTRA_BYTES = 8 }; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, htonl(0x12345678), EXTRA_BYTES); ofpbuf_put_zeros(hello, EXTRA_BYTES); ofpmsg_update_length(hello); test_send_hello(type, hello->data, hello->size, 0); ofpbuf_delete(hello); } /* Try connecting and sending an echo request instead of a hello, which should * fail with EPROTO. */ static void test_send_echo_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofpbuf *echo; echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP15_VERSION, htonl(0x12345678), 0); test_send_hello(type, echo->data, echo->size, EPROTO); ofpbuf_delete(echo); } /* Try connecting and sending a hello packet that has its length field as 0, * which should fail with EPROTO. */ static void test_send_short_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofp_header hello; memset(&hello, 0, sizeof hello); test_send_hello(type, &hello, sizeof hello, EPROTO); } /* Try connecting and sending a hello packet that has a bad version, which * should fail with EPROTO. */ static void test_send_invalid_version_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofpbuf *hello; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, htonl(0x12345678), 0); ((struct ofp_header *) hello->data)->version = 0; test_send_hello(type, hello->data, hello->size, EPROTO); ofpbuf_delete(hello); } static const struct ovs_cmdl_command commands[] = { {"refuse-connection", NULL, 1, 1, test_refuse_connection, OVS_RO}, {"accept-then-close", NULL, 1, 1, test_accept_then_close, OVS_RO}, {"read-hello", NULL, 1, 1, test_read_hello, OVS_RO}, {"send-plain-hello", NULL, 1, 1, test_send_plain_hello, OVS_RO}, {"send-long-hello", NULL, 1, 1, test_send_long_hello, OVS_RO}, {"send-echo-hello", NULL, 1, 1, test_send_echo_hello, OVS_RO}, {"send-short-hello", NULL, 1, 1, test_send_short_hello, OVS_RO}, {"send-invalid-version-hello", NULL, 1, 1, test_send_invalid_version_hello, OVS_RO}, {NULL, NULL, 0, 0, NULL, OVS_RO}, }; static void test_vconn_main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = argc - 1, .argv = argv + 1, }; set_program_name(argv[0]); vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER); vlog_set_levels(NULL, VLF_CONSOLE, VLL_DBG); fatal_ignore_sigpipe(); time_alarm(TIMEOUT); ovs_cmdl_run_command(&ctx, commands); } OVSTEST_REGISTER("test-vconn", test_vconn_main); openvswitch-3.7.0~git20260211.8c6ebf8/tests/test-vlog.py000066400000000000000000000027141514270232600224240ustar00rootroot00000000000000# Copyright (c) 2011 Nicira, Inc. # # 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. import argparse import ovs.vlog def main(): modules = [ovs.vlog.Vlog("module_%d" % i) for i in range(3)] parser = argparse.ArgumentParser(description="Vlog Module Tester") ovs.vlog.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) for m in modules: m.emer("emergency") m.err("error") m.warn("warning") m.info("information") m.dbg("debug") try: fail = False # Silence pychecker warning. assert fail except AssertionError: m.emer("emergency exception", exc_info=True) m.err("error exception", exc_info=True) m.warn("warn exception", exc_info=True) m.info("information exception", exc_info=True) m.dbg("debug exception", exc_info=True) m.exception("exception") if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/tests/testsuite.at000066400000000000000000000051231514270232600225020ustar00rootroot00000000000000AT_INIT AT_COPYRIGHT([Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. 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.]) m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS]) m4_include([tests/ovs-macros.at]) m4_include([tests/ovsdb-macros.at]) m4_include([tests/ofproto-macros.at]) m4_include([tests/completion.at]) m4_include([tests/checkpatch.at]) m4_include([tests/bfd.at]) m4_include([tests/cfm.at]) m4_include([tests/lacp.at]) m4_include([tests/library.at]) m4_include([tests/heap.at]) m4_include([tests/bundle.at]) m4_include([tests/classifier.at]) m4_include([tests/check-structs.at]) m4_include([tests/daemon.at]) m4_include([tests/daemon-py.at]) m4_include([tests/ofp-actions.at]) m4_include([tests/ofp-print.at]) m4_include([tests/ofp-util.at]) m4_include([tests/ofp-errors.at]) m4_include([tests/ovs-ofctl.at]) m4_include([tests/fuzz-regression.at]) m4_include([tests/odp.at]) m4_include([tests/mpls-xlate.at]) m4_include([tests/multipath.at]) m4_include([tests/learn.at]) m4_include([tests/vconn.at]) m4_include([tests/file_name.at]) m4_include([tests/aes128.at]) m4_include([tests/unixctl-py.at]) m4_include([tests/uuid.at]) m4_include([tests/json.at]) m4_include([tests/jsonrpc.at]) m4_include([tests/jsonrpc-py.at]) m4_include([tests/tunnel.at]) m4_include([tests/tunnel-push-pop.at]) m4_include([tests/tunnel-push-pop-ipv6.at]) m4_include([tests/ovs-router.at]) m4_include([tests/lockfile.at]) m4_include([tests/reconnect.at]) m4_include([tests/ovs-vswitchd.at]) m4_include([tests/ofproto.at]) m4_include([tests/dpif-netdev.at]) m4_include([tests/pmd.at]) m4_include([tests/alb.at]) m4_include([tests/dpctl.at]) m4_include([tests/ofproto-dpif.at]) m4_include([tests/bridge.at]) m4_include([tests/netdev-type.at]) m4_include([tests/ovsdb.at]) m4_include([tests/ovs-vsctl.at]) m4_include([tests/stp.at]) m4_include([tests/rstp.at]) m4_include([tests/vlog.at]) m4_include([tests/vtep-ctl.at]) m4_include([tests/auto-attach.at]) m4_include([tests/mcast-snooping.at]) m4_include([tests/packet-type-aware.at]) m4_include([tests/nsh.at]) m4_include([tests/drop-stats.at]) m4_include([tests/pytest.at]) m4_include([tests/learning-switch.at]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/testsuite.patch000066400000000000000000000041011514270232600231700ustar00rootroot00000000000000--- testsuite 2015-02-11 17:19:21.654646439 -0800 +++ testsuite 2015-02-11 17:15:03.810653032 -0800 @@ -4669,6 +4669,73 @@ fi exec 6<&- wait +elif test $at_jobs -ne 1 && + test "$IS_WIN32" = "yes"; then + # FIFO job dispatcher. + trap 'at_pids= + for at_pid in `jobs -p`; do + at_pids="$at_pids $at_job_group$at_pid" + done + if test -n "$at_pids"; then + at_sig=TSTP + test "${TMOUT+set}" = set && at_sig=STOP + kill -$at_sig $at_pids 2>/dev/null + fi + kill -STOP $$ + test -z "$at_pids" || kill -CONT $at_pids 2>/dev/null' TSTP + + echo + # Turn jobs into a list of numbers, starting from 1. + running_jobs="`pwd`/tests/jobdispatcher" + mkdir -p $running_jobs + at_joblist=`$as_echo "$at_groups" | sed -n 1,${at_jobs}p` + + set X $at_joblist + shift + for at_group in $at_groups; do + $at_job_control_on 2>/dev/null + ( + # Start one test group. + $at_job_control_off + touch $running_jobs/$at_group + trap 'set +x; set +e + trap "" PIPE + echo stop > "$at_stop_file" + rm -f $running_jobs/$at_group + as_fn_exit 141' PIPE + at_fn_group_prepare + if cd "$at_group_dir" && + at_fn_test $at_group && + . "$at_test_source" + then :; else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unable to parse test group: $at_group" >&5 +$as_echo "$as_me: WARNING: unable to parse test group: $at_group" >&2;} + at_failed=: + fi + rm -f $running_jobs/$at_group + at_fn_group_postprocess + ) & + $at_job_control_off + shift # Consume one token. + if test $# -gt 0; then :; else + while [ "`ls -l $running_jobs 2>/dev/null | wc -l`" -gt "$at_jobs" ]; do + sleep 0.1 + done + set x $* + fi + test -f "$at_stop_file" && break + done + # Read back the remaining ($at_jobs - 1) tokens. + set X $at_joblist + shift + if test $# -gt 0; then + shift + while [ "`ls -l $running_jobs | wc -l`" -gt 1 ]; do + sleep 0.1 + done + fi + rmdir $running_jobs + wait else # Run serially, avoid forks and other potential surprises. for at_group in $at_groups; do openvswitch-3.7.0~git20260211.8c6ebf8/tests/tunnel-push-pop-ipv6.at000066400000000000000000001470441514270232600244220ustar00rootroot00000000000000AT_BANNER([tunnel_push_pop_ipv6]) AT_SETUP([tunnel_push_pop_ipv6 - srv6]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00 options:pcap=p0.pcap]) AT_CHECK([ovs-vsctl add-br int-br1 -- set bridge int-br1 datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-br int-br2 -- set bridge int-br2 datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-br int-br3 -- set bridge int-br3 datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br1 t1 -- set Interface t1 type=srv6 \ options:remote_ip=2001:cafe::91 ofport_request=2 \ options:srv6_flowlabel=copy \ ], [0]) AT_CHECK([ovs-vsctl add-port int-br2 t2 -- set Interface t2 type=srv6 \ options:remote_ip=2001:cafe::92 ofport_request=3 \ options:srv6_flowlabel=zero \ ], [0]) AT_CHECK([ovs-vsctl add-port int-br3 t3 -- set Interface t3 type=srv6 \ options:remote_ip=2001:cafe::93 ofport_request=4 \ options:srv6_flowlabel=compute \ ], [0]) dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) dnl Checking that a local routes for added IPs were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::91 aa:55:aa:55:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::92 aa:55:aa:55:00:02], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::93 aa:55:aa:55:00:03], [0], [OK ]) AT_CHECK([ovs-ofctl add-flow br0 action=1]) AT_CHECK([ovs-ofctl add-flow int-br1 action=2]) AT_CHECK([ovs-ofctl add-flow int-br2 action=3]) AT_CHECK([ovs-ofctl add-flow int-br3 action=4]) dnl Check "srv6_flowlabel=copy". AT_CHECK([ovs-appctl netdev-dummy/receive int-br1 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br1 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br1 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::2,label=2,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br1 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::3,label=3,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-ofctl parse-pcap p0.pcap | tail -n 4 | grep -o 'ipv6_label=0x[[0-9a-f]]*' | sort], [0], [dnl ipv6_label=0x00000 ipv6_label=0x00000 ipv6_label=0x00002 ipv6_label=0x00003 ]) dnl Check "srv6_flowlabel=zero". AT_CHECK([ovs-appctl netdev-dummy/receive int-br2 'in_port(3),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br2 'in_port(3),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br2 'in_port(3),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::2,label=2,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br2 'in_port(3),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::3,label=3,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-ofctl parse-pcap p0.pcap | tail -n 4 | grep -o 'ipv6_label=0x[[0-9a-f]]*'], [0], [dnl ipv6_label=0x00000 ipv6_label=0x00000 ipv6_label=0x00000 ipv6_label=0x00000 ]) dnl dnl Check "srv6_flowlabel=compute" for different flows. AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::2,label=2,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::3,label=3,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-ofctl parse-pcap p0.pcap | tail -n 4 | grep -o 'ipv6_label=0x[[0-9a-f]]*'| sort | uniq -c | wc -l], [0], [dnl 4 ]) dnl dnl Check "srv6_flowlabel=compute" for same IPv4/TCP flow. AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=100,dst=200),tcp_flags(0x002)']) AT_CHECK([ovs-ofctl parse-pcap p0.pcap | tail -n 2 | grep -o 'ipv6_label=0x[[0-9a-f]]*' | sort | uniq -c | wc -l], [0], [dnl 1 ]) dnl dnl Check "srv6_flowlabel=compute" for same IPv6/TCP flow. AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::2,label=2,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x001)']) AT_CHECK([ovs-appctl netdev-dummy/receive int-br3 'in_port(4),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:beef::1,dst=2001:beef::2,label=3,proto=6,tclass=0x0,hlimit=64),tcp(src=100,dst=200),tcp_flags(0x002)']) AT_CHECK([ovs-ofctl parse-pcap p0.pcap | tail -n 2 | grep -o 'ipv6_label=0x[[0-9a-f]]*' | sort | uniq -c | wc -l], [0], [dnl 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop_ipv6 - ip6gre]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=ip6gre \ options:remote_ip=2001:cafe::92 ofport_request=2\ options:packet_type=legacy_l2 ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t2 2/6: (ip6gre: remote_ip=2001:cafe::92) ]) dnl Setup dummy interface IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local routes for added IPs were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) dnl Check Neighbour discovery. AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([cat p0.pcap.txt | grep 92aa55aa55000086dd6000000000203aff2001cafe | uniq], [0], [dnl 3333ff000092aa55aa55000086dd6000000000203aff2001cafe000000000000000000000088ff0200000000000000000001ff00009287004d48000000002001cafe0000000000000000000000920101aa55aa550000 ]) dnl AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)']) AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: ip6gre_sys (6) ref_cnt=1 ]) dnl Check IPv6 GRE tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=47,tclass=0x0,hlimit=64)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6) ]) dnl Check IPv6 GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6),header(size=58,type=109,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x0,proto=0x6558))),out_port(100)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop_ipv6 - ip6erspan]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=ip6erspan \ options:remote_ip=2001:cafe::92 options:key=123 \ options:erspan_ver=1 options:erspan_idx=3 ofport_request=2\ -- add-port int-br t3 -- set Interface t3 type=ip6erspan \ options:remote_ip=2001:cafe::93 options:key=567 \ options:erspan_ver=2 options:erspan_dir=1 options:erspan_hwid=0x7 \ ofport_request=3\ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t2 2/6: (ip6erspan: erspan_idx=0x3, erspan_ver=1, key=123, remote_ip=2001:cafe::92) t3 3/6: (ip6erspan: erspan_dir=1, erspan_hwid=0x7, erspan_ver=2, key=567, remote_ip=2001:cafe::93) ]) dnl Setup dummy interface IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local routes for added IPs were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) dnl Check Neighbour discovery. AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([cat p0.pcap.txt | grep 92aa55aa55000086dd6000000000203aff2001cafe | uniq], [0], [dnl 3333ff000092aa55aa55000086dd6000000000203aff2001cafe000000000000000000000088ff0200000000000000000001ff00009287004d48000000002001cafe0000000000000000000000920101aa55aa550000 ]) AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | uniq], [0], [dnl 3333ff000093aa55aa55000086dd6000000000203aff2001cafe000000000000000000000088ff0200000000000000000001ff00009387004d46000000002001cafe0000000000000000000000930101aa55aa550000 ]) dnl AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)']) dnl AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)']) dnl Check ARP Snoop dnl AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)']) dnl AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 2001:cafe::93 f8:bc:12:44:34:b7 br0 ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: ip6erspan_sys (6) ref_cnt=2 ]) dnl Check ERSPAN tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=47,tclass=0x0,hlimit=64)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6) ]) dnl Check ERSPAN v1 tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6),header(size=70,type=108,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),erspan(ver=1,sid=0x7b,idx=0x3)),out_port(100)),1 ]) dnl Check ERSPAN v2 tunnel push AT_CHECK([ovs-ofctl mod-flows int-br action=3]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6),header(size=74,type=108,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::93,label=0,proto=47,tclass=0x0,hlimit=64),erspan(ver=2,sid=0x237,dir=1,hwid=0x7)),out_port(100)),1 ]) ovs-appctl vlog/set dbg dnl Check decapsulation of ERSPAN v1 dnl Hex dump: GRE:(100088be) dnl ERSPAN: v1, session id = 0x7b (1000007b), index=3 (00000003) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe000000000000000000000088100088be000000011000007b00000003fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe000000000000000000000088100088be000000011000007b00000003fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 2'], [0], [dnl port 2: rx pkts=2, bytes=196, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation ERSPAN v2 dnl Hex dump: GRE:(100022eb) dnl ERSPAN: v2, session id = 0x237 (20000237), hwid = 8,dir = 1 (00000078) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000932001cafe000000000000000000000088100022eb000000012000023710abcd0100000078fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl port 3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=? ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop_ipv6 - action]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \ options:remote_ip=2001:cafe::92 options:key=123 ofport_request=2\ -- add-port int-br t1 -- set Interface t1 type=gre \ options:remote_ip=2001:cafe::92 options:key=456 ofport_request=3\ -- add-port int-br t3 -- set Interface t3 type=vxlan \ options:remote_ip=2001:cafe::93 options:out_key=flow options:csum=true ofport_request=4\ -- add-port int-br t4 -- set Interface t4 type=geneve \ options:remote_ip=flow options:key=123 ofport_request=5\ -- add-port int-br t5 -- set Interface t5 type=gre \ options:remote_ip=2001:cafe::92 options:key=455 options:packet_type=legacy_l3 ofport_request=6\ -- add-port int-br t6 -- set Interface t6 type=srv6 \ options:remote_ip=2001:cafe::92 ofport_request=7\ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t1 3/3: (gre: key=456, remote_ip=2001:cafe::92) t2 2/4789: (vxlan: key=123, remote_ip=2001:cafe::92) t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=2001:cafe::93) t4 5/6081: (geneve: key=123, remote_ip=flow) t5 6/3: (gre: key=455, packet_type=legacy_l3, remote_ip=2001:cafe::92) t6 7/6: (srv6: remote_ip=2001:cafe::92) ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: genev_sys_6081 (6081) ref_cnt=1 gre_sys (3) ref_cnt=2 srv6_sys (6) ref_cnt=1 srv6_sys (6) ref_cnt=1 vxlan_sys_4789 (4789) ref_cnt=2 ]) dnl Setup dummy interface IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local routes for added IPs were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) dnl Check Neighbour discovery. AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) dnl Wait for the two Neighbor Solicitation packets to be sent. dnl Sometimes the system can be slow (e.g. under valgrind) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | sort | uniq | wc -l` -ge 2]) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([cat p0.pcap.txt | grep 92aa55aa55000086dd6000000000203aff2001cafe | uniq], [0], [dnl 3333ff000092aa55aa55000086dd6000000000203aff2001cafe000000000000000000000088ff0200000000000000000001ff00009287004d48000000002001cafe0000000000000000000000920101aa55aa550000 ]) AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | uniq], [0], [dnl 3333ff000093aa55aa55000086dd6000000000203aff2001cafe000000000000000000000088ff0200000000000000000001ff00009387004d46000000002001cafe0000000000000000000000930101aa55aa550000 ]) dnl Set the aging time to 5 seconds AT_CHECK([ovs-appctl tnl/neigh/aging 5], [0], [OK ]) dnl Read the current aging time AT_CHECK([ovs-appctl tnl/neigh/aging], [0], [5 ]) dnl Add an entry AT_CHECK([ovs-appctl tnl/neigh/set br0 2001:cafe::92 aa:bb:cc:00:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 2001:cafe::92 aa:bb:cc:00:00:01 br0 ]) ovs-appctl time/warp 5000 dnl Check the entry has been removed AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl ]) dnl Restore the aging time to 900s (default) AT_CHECK([ovs-appctl tnl/neigh/aging 900], [0], [OK ]) dnl Read the current aging time AT_CHECK([ovs-appctl tnl/neigh/aging], [0], [900 ]) dnl Check ARP Snoop AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving Neighbor Advertisement with incorrect VLAN id should not alter tunnel neighbor cache AT_CHECK([ovs-vsctl set port br0 tag=10]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache AT_CHECK([ovs-vsctl set port br0 tag=10]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 ]) dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 ]) dnl Receive Neighbor Advertisement without VLAN header AT_CHECK([ovs-vsctl set port br0 tag=0]) AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK ]) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff00:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 2001:cafe::93 f8:bc:12:44:34:b7 br0 ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: genev_sys_6081 (6081) ref_cnt=1 gre_sys (3) ref_cnt=2 srv6_sys (6) ref_cnt=1 srv6_sys (6) ref_cnt=1 vxlan_sys_4789 (4789) ref_cnt=2 ]) dnl Check VXLAN tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=51283,dst=4789)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(4789) ]) dnl Check GRE tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=47,tclass=0x0,hlimit=64)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(3) ]) dnl Check Geneve tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=51283,dst=6081)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6081) ]) dnl Check SRv6 tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=4,tclass=0x0,hlimit=64)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6) ]) dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) dnl Check VXLAN tunnel push set tunnel id by flow and checksum AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::93,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100)),1 ]) dnl Check GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=3]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=62,type=109,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)),1 ]) dnl Check SRv6 tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=7]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_eth,tnl_push(tnl_port(6),header(size=78,type=112,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=0,segs(2001:cafe::92))),out_port(100)),1 ]) dnl Check Geneve tunnel push AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:2001:cafe::92->tun_ipv6_dst,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),1 ]) dnl Check Geneve tunnel push with options AT_CHECK([ovs-ofctl add-tlv-map int-br "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:2001:cafe::92->tun_ipv6_dst,set_field:0xa->tun_metadata0,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=78,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)),1 ]) dnl Check decapsulation of GRE packet AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl port 3: rx pkts=2, bytes=196, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation of L3GRE packet AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 6'], [0], [dnl port 6: rx pkts=2, bytes=168, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation of Geneve packet with options AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor int-br 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-ofctl add-flow int-br "tun_metadata0=0xa/0xf,actions=5,controller"]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6486dd60000000008211402001cafe0000000000000000000000922001cafe000000000000000000000088308817c1008200000400655800007b00ffff80010000000affff00010000000bfe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=98 tun_id=0x7b,tun_ipv6_src=2001:cafe::92,tun_ipv6_dst=2001:cafe::88,tun_metadata0=0xa,in_port=5 (via action) data_len=98 (unbuffered) icmp,vlan_tci=0x0000,dl_src=be:b6:f4:e1:49:4a,dl_dst=fe:71:d8:83:72:4f,nw_src=30.0.0.1,nw_dst=30.0.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=0,icmp_code=0 icmp_csum:4227 ]) AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 5'], [0], [dnl port 5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=? ]) AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl recirc_id(0),tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(+key)),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535)) ]) dnl Receive VXLAN with different MAC and verify that the neigh cache gets updated AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000f8bc1244cafe86dd60000000003a11402001cafe0000000000000000000000922001cafe000000000000000000000088c85312b5003abc700c00000300007b00ffffffffffff00000000000008004500001c0001000040117cce7f0000017f0000010035003500080172']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:ca:fe,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) dnl No drop so far. AT_CHECK([ovs-appctl coverage/read-counter datapath_drop_tunnel_pop_error], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_err], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_err], [0], [0 ]) dnl Yet csum validation happened on all previous packets (with non null checksums). AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_checked], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_checked], [0], [1 ]) dnl Send a VXLAN packet with bad UDP checksum. AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000f8bc1244cafe86dd60000000003a11402001cafe0000000000000000000000922001cafe000000000000000000000088c85312b5003aDEAD0c00000300007b00ffffffffffff00000000000008004500001c0001000040117cce7f0000017f0000010035003500080172']) AT_CHECK([ovs-appctl coverage/read-counter datapath_drop_tunnel_pop_error], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_err], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_err], [0], [1 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_checked], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_checked], [0], [2 ]) AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:ca:fe br0 2001:cafe::93 f8:bc:12:44:34:b7 br0 ]) dnl Restore and check the cache entries AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000f8bc124434b686dd60000000003a11402001cafe0000000000000000000000922001cafe000000000000000000000088c85312b5003abc700c00000300007b00ffffffffffff00000000000008004500001c0001000040117cce7f0000017f0000010035003500080172']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl 2001:cafe::92 f8:bc:12:44:34:b6 br0 2001:cafe::93 f8:bc:12:44:34:b7 br0 ]) dnl Disable checksum from VXLAN port. AT_CHECK([ovs-vsctl set Interface t3 options:csum=false]) AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-ofctl add-flow int-br action=4]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::93,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(100)),1 ]) ovs-appctl time/warp 10000 AT_CHECK([ovs-vsctl del-port int-br t3 \ -- set Interface t1 type=vxlan \ -- set Interface t2 options:dst_port=4790 \ ], [0]) dnl Check tunnel lookup entries after deleting/reconfiguring some ports AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: genev_sys_6081 (6081) ref_cnt=1 gre_sys (3) ref_cnt=1 srv6_sys (6) ref_cnt=1 srv6_sys (6) ref_cnt=1 vxlan_sys_4789 (4789) ref_cnt=1 vxlan_sys_4790 (4790) ref_cnt=1 ]) AT_CHECK([ovs-vsctl del-port int-br t1 \ -- del-port int-br t2 \ -- del-port int-br t4 \ -- del-port int-br t5 \ -- del-port int-br t6 \ ], [0]) dnl Check tunnel lookup entries after deleting all remaining tunnel ports AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop_ipv6 - local_ip configuration]) OVS_VSWITCHD_START( [add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1 \ other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 \ -- set Interface t2 type=geneve \ options:local_ip=2001:beef::88 \ options:remote_ip=2001:cafe::92 \ options:key=123 ofport_request=2]) dnl Setup multiple IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:beef::88/64], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 2001:beef::/64 dev br0 SRC 2001:beef::88 Cached: 2001:beef::88/128 dev br0 SRC 2001:beef::88 local Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) dnl This Neighbor Advertisement from p0 has two effects: dnl 1. The neighbor cache will learn that 2001:cafe::92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),dnl ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),dnl icmpv6(type=136,code=0),dnl nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)' ]) dnl Check that local_ip is used for encapsulation in the trace. AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ | grep -E 'tunnel|actions'], [0], [dnl -> output to native tunnel -> tunneling to 2001:cafe::92 via br0 -> tunneling from aa:55:aa:55:00:00 2001:beef::88 to f8:bc:12:44:34:b6 2001:cafe::92 Datapath actions: tnl_push(tnl_port(6081),header(size=70,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ipv6(src=2001:beef::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),1 ]) dnl Now check that the packet actually has the local_ip in the header. AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) packet=50540000000a5054000000091234 eth=f8bc124434b6aa55aa55000086dd ip6=60000000001e11402001beef0000000000000000000000882001cafe000000000000000000000092 dnl Source port is based on a packet hash, so it may differ depending on the dnl compiler flags and CPU type. Same for UDP checksum. Masked with '....'. udp=....17c1001e.... geneve=0000655800007b00 encap=${eth}${ip6}${udp}${geneve} dnl Output to tunnel from a int-br internal port. dnl Checking that the packet arrived and it was correctly encapsulated. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) dnl Sending again to exercise the non-miss upcall path. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) dnl Finally, checking that the datapath flow also has a local_ip. AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ | strip_ufid | strip_used], [0], [dnl recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl packets:1, bytes:14, used:0.0s, dnl actions:tnl_push(tnl_port(6081),header(size=70,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ipv6(src=2001:beef::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is a regression test for outer header checksum offloading dnl with recirculation. AT_SETUP([tunnel_push_pop_ipv6 - recirculation after encapsulation]) OVS_VSWITCHD_START( [add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1 \ other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 \ -- set Interface t2 type=geneve \ options:remote_ip=2001:cafe::92 \ options:key=123 ofport_request=2]) dnl Setup an IP address. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) dnl Add a dp-hash selection group. AT_CHECK([ovs-ofctl add-group br0 \ 'group_id=1234,type=select,selection_method=dp_hash,bucket=weight=1,output:p0']) AT_CHECK([ovs-ofctl add-flow br0 in_port=br0,action=group:1234]) AT_CHECK([ovs-ofctl add-flow br0 in_port=p0,action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) dnl This Neighbor Advertisement from p0 has two effects: dnl 1. The neighbor cache will learn that 2001:cafe::92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),dnl ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),dnl icmpv6(type=136,code=0),dnl nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)' ]) dnl Check that selection group is used in the trace. AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ | grep -E 'tunnel|actions'], [0], [dnl -> output to native tunnel -> tunneling to 2001:cafe::92 via br0 -> tunneling from aa:55:aa:55:00:00 2001:cafe::88 to f8:bc:12:44:34:b6 2001:cafe::92 Datapath actions: tnl_push(tnl_port(6081),header(size=70,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),dnl hash(l4(0)),recirc(0x1) ]) dnl Now check that the packet is actually encapsulated and delivered. AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) packet=50540000000a5054000000091234 eth=f8bc124434b6aa55aa55000086dd ip6=60000000001e11402001cafe0000000000000000000000882001cafe000000000000000000000092 dnl Source port is based on a packet hash, so it may differ depending on the dnl compiler flags and CPU type. Same for UDP checksum. Masked with '....'. udp=....17c1001e.... geneve=0000655800007b00 encap=${eth}${ip6}${udp}${geneve} dnl Output to tunnel from a int-br internal port. dnl Checking that the packet arrived and it was correctly encapsulated. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) dnl Sending again to exercise the non-miss upcall path. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) dnl Finally, checking that the datapath flow is also correct. AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ | strip_ufid | strip_used], [0], [dnl recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl packets:1, bytes:14, used:0.0s, dnl actions:tnl_push(tnl_port(6081),header(size=70,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),dnl hash(l4(0)),recirc(0x2) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop_ipv6 - Mirror over tunnels]) OVS_VSWITCHD_START([dnl add-br br-ext -- set bridge br-ext datapath_type=dummy \ other-config:hwaddr=aa:55:aa:55:00:00 \ -- add-port br0 t1 -- set Interface t1 type=geneve \ options:remote_ip=2001:cafe::91 \ -- add-port br0 t2 -- set Interface t2 type=erspan \ options:remote_ip=2001:cafe::92 options:key=flow \ options:erspan_ver=1 options:erspan_idx=flow \ -- add-port br0 p0 -- set Interface p0 type=dummy \ -- add-port br0 p1 -- set Interface p1 type=dummy \ -- add-port br-ext p-ext -- set Interface p-ext type=dummy \ options:pcap=ext.pcap]) dnl Configure mirroring over the UDP and ERSPAN tunnels. AT_CHECK([dnl ovs-vsctl \ set Bridge br0 mirrors=@m1,@m2 -- \ --id=@t1 get Port t1 -- \ --id=@t2 get Port t2 -- \ --id=@m1 create Mirror name=vxlan select_all=true output_port=@t1 -- \ --id=@m2 create Mirror name=erspan select_all=true output_port=@t2], [0], [stdout]) AT_CHECK([ovs-ofctl add-flow br-ext actions=normal]) AT_CHECK([ovs-ofctl add-flow br0 actions=normal]) dnl Make sure ephemeral ports stay static across tests. AT_CHECK([ovs-appctl tnl/egress_port_range 35190 35190], [0], [OK ]) dnl Setup an IP address. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br-ext 2001:cafe::90/64], [0], [OK ]) dnl Send two ND packets to set the tunnel's port and mac address. AT_CHECK([ovs-appctl netdev-dummy/receive p-ext dnl 'eth(src=f8:bc:12:44:34:b3,dst=aa:55:aa:55:00:00),eth_type(0x86dd),dnl ipv6(src=2001:cafe::91,dst=2001:cafe::90,label=0,proto=58,tclass=0,hlimit=255,frag=no),dnl icmpv6(type=136,code=0),dnl nd(target=2001:cafe::91,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b3)' ]) AT_CHECK([ovs-appctl netdev-dummy/receive p-ext dnl 'eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),dnl ipv6(src=2001:cafe::92,dst=2001:cafe::90,label=0,proto=58,tclass=0,hlimit=255,frag=no),dnl icmpv6(type=136,code=0),dnl nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)' ]) m4_define([FLOW], [m4_join([,], [in_port(p1)], [eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800)], [ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)], [icmp(type=8,code=0)])]) m4_define([ERSPAN_ACT], [m4_join([,], [clone(tnl_push(tnl_port(erspan_sys)], [header(size=70,type=108], [eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd)], [ipv6(src=2001:cafe::90,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64)], [erspan(ver=1,sid=0x0,idx=0x0))], [out_port(br-ext))], [p-ext)])]) m4_define([GENEVE_ACT], [m4_join([,], [clone(tnl_push(tnl_port(genev_sys_6081)], [header(size=70,type=5], [eth(dst=f8:bc:12:44:34:b3,src=aa:55:aa:55:00:00,dl_type=0x86dd)], [ipv6(src=2001:cafe::90,dst=2001:cafe::91,label=0,proto=17,tclass=0x0,hlimit=64)], [udp(src=0,dst=6081,csum=0xffff)], [geneve(vni=0x0))], [out_port(br-ext))], [p-ext)])]) dnl Verify packet is mirrored to both tunnels. Tunnel actions may happen dnl in any order. AT_CHECK([ovs-appctl ofproto/trace --names ovs-dummy "FLOW"], [0], [stdout]) AT_CHECK([grep -q "ERSPAN_ACT" stdout]) AT_CHECK([grep -q "GENEVE_ACT" stdout]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/tunnel-push-pop.at000066400000000000000000002437231514270232600235410ustar00rootroot00000000000000AT_BANNER([tunnel_push_pop]) AT_SETUP([tunnel_push_pop - erspan]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=erspan \ options:remote_ip=1.1.2.92 options:key=123 options:erspan_ver=1 \ options:erspan_idx=3 ofport_request=2 \ -- add-port int-br t2 -- set Interface t2 type=erspan \ options:remote_ip=1.1.2.92 options:key=567 options:erspan_ver=2 \ options:erspan_dir=1 options:erspan_hwid=0x7 ofport_request=3\ -- add-port int-br t3 -- set Interface t3 type=erspan \ options:remote_ip=flow options:erspan_ver=2 options:key=456 \ options:erspan_hwid=flow options:erspan_dir=flow ofport_request=4\ -- add-port int-br t4 -- set Interface t4 type=erspan \ options:remote_ip=flow options:erspan_ver=2 options:key=56 \ options:erspan_ver=flow ofport_request=5\ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t1 2/3: (erspan: erspan_idx=0x3, erspan_ver=1, key=123, remote_ip=1.1.2.92) t2 3/3: (erspan: erspan_dir=1, erspan_hwid=0x7, erspan_ver=2, key=567, remote_ip=1.1.2.92) t3 4/3: (erspan: erspan_dir=flow, erspan_hwid=flow, erspan_ver=2, key=456, remote_ip=flow) t4 5/3: (erspan: erspan_dir=flow, erspan_hwid=flow, erspan_idx=flow, erspan_ver=flow, key=56, remote_ip=flow) ]) dnl Setup dummy interface IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) dnl Checking that a local routes for added IPs were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Check ARP request AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) dnl Wait for the two ARP requests to be sent. Sometimes the system dnl can be slow (e.g. under valgrind) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | sort | uniq | wc -l` -ge 1]) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([cat p0.pcap.txt | grep 101025 | uniq], [0], [dnl ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101025c ]) dnl Check ARP Snoop AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.94,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 1.1.2.93 f8:bc:12:44:34:b7 br0 1.1.2.94 f8:bc:12:44:34:b8 br0 ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: erspan_sys (3) ref_cnt=4 ]) dnl Check ERSPAN v1 tunnel push AT_CHECK([ovs-vsctl -- set Interface br0 options:pcap=br0.pcap]) AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=50,type=107,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=1,sid=0x7b,idx=0x3)),out_port(100)),1 ]) dnl Check ERSPAN v2 tunnel push AT_CHECK([ovs-ofctl mod-flows int-br action=3]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=54,type=107,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=2,sid=0x237,dir=1,hwid=0x7)),out_port(100)),1 ]) dnl Check ERSPAN v2 flow-based tunnel push AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:1->tun_erspan_dir,set_field:0x1->tun_erspan_hwid,4"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=54,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=2,sid=0x1c8,dir=1,hwid=0x1)),out_port(100)),1 ]) dnl Check ERSPAN v2 flow-based tunnel push, erspan_ver=flow dnl Dynamically set erspan v2 AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:2->tun_erspan_ver,set_field:1->tun_erspan_dir,set_field:0x1->tun_erspan_hwid,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=54,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=2,sid=0x38,dir=1,hwid=0x1)),out_port(100)),1 ]) dnl Dynamically set erspan v1 AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:1->tun_erspan_ver,set_field:1->tun_erspan_idx,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=50,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=1,sid=0x38,idx=0x1)),out_port(100)),1 ]) dnl Check ERSPAN v2 flow-based tunnel push AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:1->tun_erspan_dir,set_field:0x1->tun_erspan_hwid,4"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=54,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=2,sid=0x1c8,dir=1,hwid=0x1)),out_port(100)),1 ]) dnl Check ERSPAN v2 flow-based tunnel push, erspan_ver=flow dnl Dynamically set erspan v2 AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:2->tun_erspan_ver,set_field:1->tun_erspan_dir,set_field:0x1->tun_erspan_hwid,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=54,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=2,sid=0x38,dir=1,hwid=0x1)),out_port(100)),1 ]) dnl Dynamically set erspan v1 AT_CHECK([ovs-ofctl mod-flows int-br "action=set_field:1.1.2.94->tun_dst,set_field:1->tun_erspan_ver,set_field:1->tun_erspan_idx,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=50,type=107,eth(dst=f8:bc:12:44:34:b8,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.94,proto=47,tos=0,ttl=64,frag=0x4000),erspan(ver=1,sid=0x38,idx=0x1)),out_port(100)),1 ]) dnl Check ERSPAN tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.11.93,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(3) ]) AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Check decapsulation of ERSPAN v1 dnl Hex dump: GRE:(100088be) dnl ERSPAN: v1, session id = 0x7b (1000007b), index=3 (00000003) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c01010258100088be000000011000007b00000003fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 2'], [0], [dnl port 2: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation ERSPAN v2 dnl Hex dump: GRE:(100022eb) dnl ERSPAN: v2, session id = 0x237 (20000237), hwid =7, dir = 1 (00000078) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c01010258100022eb000000012000023710abcd0100000078fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl port 3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check ERSPAN encap in pcap file dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(2),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) dnl Output to tunnel from a int-br internal port AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=output:2"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br '50540000000a5054000000091234']) dnl 100088be: GRE with ERSPANv1, 00000001: Seqno, 1000007b: v1 with 0x7b session ID (key) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 100088be000000011000007b | wc -l` -ge 1]) AT_CHECK([ovs-ofctl mod-flows int-br "in_port=LOCAL,actions=output:3"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br '50540000000a5054000000091235']) dnl 100022eb: GRE with ERSPANv2, 00000001: Seqno, 20000237: v2 with 0x237 session ID (key) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 100022eb0000000120000237 | wc -l` -ge 1]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - action]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \ options:remote_ip=1.1.2.92 options:key=123 ofport_request=2\ -- add-port int-br t1 -- set Interface t1 type=gre \ options:remote_ip=1.1.2.92 options:key=456 ofport_request=3\ -- add-port int-br t3 -- set Interface t3 type=vxlan \ options:remote_ip=1.1.2.93 options:out_key=flow options:csum=true ofport_request=4\ -- add-port int-br t4 -- set Interface t4 type=geneve \ options:remote_ip=flow options:key=123 ofport_request=5\ -- add-port int-br t5 -- set Interface t5 type=geneve \ options:remote_ip=1.1.2.93 options:out_key=flow options:egress_pkt_mark=1234 ofport_request=6\ -- add-port int-br t6 -- set Interface t6 type=gre \ options:remote_ip=1.1.2.92 options:key=456 options:packet_type=legacy_l3 ofport_request=7\ -- add-port int-br t7 -- set Interface t7 type=vxlan \ options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe ofport_request=8\ -- add-port int-br t8 -- set Interface t8 type=gtpu \ options:remote_ip=1.1.2.92 options:key=123 ofport_request=9\ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t1 3/3: (gre: key=456, remote_ip=1.1.2.92) t2 2/4789: (vxlan: key=123, remote_ip=1.1.2.92) t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=1.1.2.93) t4 5/6081: (geneve: key=123, remote_ip=flow) t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93) t6 7/3: (gre: key=456, packet_type=legacy_l3, remote_ip=1.1.2.92) t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92) t8 9/2152: (gtpu: key=123, remote_ip=1.1.2.92) ]) dnl Setup dummy interface IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/24], [0], [OK ]) dnl Add a static route with a mark. AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0 pkt_mark=1234], [0], [OK ]) dnl Checking that local routes for added IPs and the static route with a mark dnl were successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2001:ca00::/24 dev br0 SRC 2001:cafe::88 Cached: 2001:cafe::88/128 dev br0 SRC 2001:cafe::88 local User: 1.1.2.0/24 MARK 1234 dev br0 SRC 1.1.2.88 ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Check ARP request AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) dnl Wait for the two ARP requests to be sent. Sometimes the system dnl can be slow (e.g. under valgrind) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | sort | uniq | wc -l` -ge 2]) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([cat p0.pcap.txt | grep 101025c | uniq], [0], [dnl ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101025c ]) AT_CHECK([cat p0.pcap.txt | grep 101025d | uniq], [0], [dnl ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101025d ]) dnl Check input range AT_CHECK([ovs-appctl tnl/neigh/aging 0], [2], [], [dnl bad aging value ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl tnl/neigh/aging 3601], [2], [], [dnl bad aging value ovs-appctl: ovs-vswitchd: server returned an error ]) AT_CHECK([ovs-appctl tnl/neigh/aging 1], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/aging 3600], [0], [OK ]) dnl Set the aging time to 5 seconds AT_CHECK([ovs-appctl tnl/neigh/aging 5], [0], [OK ]) dnl Read the current aging time AT_CHECK([ovs-appctl tnl/neigh/aging], [0], [5 ]) dnl Add an entry AT_CHECK([ovs-appctl tnl/neigh/set br0 1.1.2.92 aa:bb:cc:00:00:01], [0], [OK ]) AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 1.1.2.92 aa:bb:cc:00:00:01 br0 ]) ovs-appctl time/warp 5000 dnl Check the entry has been removed AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl ]) dnl Restore the aging time to 900s (default) AT_CHECK([ovs-appctl tnl/neigh/aging 900], [0], [OK ]) dnl Read the current aging time AT_CHECK([ovs-appctl tnl/neigh/aging], [0], [900 ]) dnl Check ARP Snoop AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache AT_CHECK([ovs-vsctl set port br0 tag=10]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 ]) dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 ]) dnl Receiving Gratuitous ARP request with correct VLAN id should alter tunnel neighbor cache AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.92,op=1,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:c8 br0 ]) dnl Receiving Gratuitous ARP reply with correct VLAN id should alter tunnel neighbor cache AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b2,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.92,op=2,sha=f8:bc:12:44:34:b2,tha=f8:bc:12:44:34:b2))']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b2 br0 ]) dnl Receive ARP reply without VLAN header AT_CHECK([ovs-vsctl set port br0 tag=0]) AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK ]) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 1.1.2.93 f8:bc:12:44:34:b7 br0 ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: genev_sys_6081 (6081) ref_cnt=2 gre_sys (3) ref_cnt=2 gtpu_sys_2152 (2152) ref_cnt=1 vxlan_sys_4789 (4789) ref_cnt=3 ]) dnl Check VXLAN tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4789)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(4789) ]) dnl Check GRE tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(3) ]) dnl Check Geneve tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=6081)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6081) ]) dnl Check Geneve tunnel (t6) pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.96,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=6081)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(6081) ]) dnl Check GTP-U tunnel pop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=2152)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(2152) ]) dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) dnl Check VXLAN GPE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=8]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000003,vni=0x159)),out_port(100)),1 ]) dnl Check VXLAN tunnel push set tunnel id by flow and checksum AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100)),1 ]) dnl Check GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=3]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)),1 ]) dnl Check L3GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=7]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_eth,tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x800),key=0x1c8)),out_port(100)),1 ]) dnl Check Geneve tunnel push AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),1 ]) dnl Check Geneve tunnel push with pkt-mark AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:234,6"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(skb_mark(0x4d2)),tnl_push(tnl_port(6081),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(vni=0xea)),out_port(100)),1 ]) dnl Check Geneve tunnel push with options AT_CHECK([ovs-ofctl add-tlv-map int-br "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,set_field:0xa->tun_metadata0,5"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)),1 ]) dnl Check GTP-U tunnel push AT_CHECK([ovs-ofctl add-flow int-br "actions=9"]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: pop_eth,tnl_push(tnl_port(2152),header(size=50,type=110,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=2152,csum=0x0),gtpu(flags=0x30,msgtype=255,teid=0x7b)),out_port(100)),1 ]) AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Check decapsulation of VXLAN packet AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a00000800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a02320800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a202e0c00000300015900fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep -E 'port [[248]]:' | sort], [0], [dnl port 2: rx pkts=2, bytes=84, drop=?, errs=?, frame=?, over=?, crc=? port 4: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? port 8: rx pkts=1, bytes=42, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Idem, with Rx cksum good AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_good=true]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a00000800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a02320800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a202e0c00000300015900fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_good=false]) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep -E 'port [[248]]:' | sort], [0], [dnl port 2: rx pkts=4, bytes=168, drop=?, errs=?, frame=?, over=?, crc=? port 4: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? port 8: rx pkts=2, bytes=84, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Idem, with Rx cksum partial AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_partial=true]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a00000800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a02320800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a202e0c00000300015900fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_partial=false]) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep -E 'port [[248]]:' | sort], [0], [dnl port 2: rx pkts=6, bytes=252, drop=?, errs=?, frame=?, over=?, crc=? port 4: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=? port 8: rx pkts=3, bytes=126, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation of GRE packet AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl port 3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Check decapsulation of L3GRE packet AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 7'], [0], [dnl port 7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl No drop so far. AT_CHECK([ovs-appctl coverage/read-counter datapath_drop_tunnel_pop_error], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_err], [0], [0 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_err], [0], [0 ]) dnl Yet csum validation happened on all previous packets. AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_checked], [0], [9 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_checked], [0], [6 ]) dnl Send various incorrect bad IP checksum packets. dnl GRE AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fDEAD0101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) dnl VxLAN AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e000100004011DEAD0101025c0101025812b512b5003a00000800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) dnl VxLAN-GPE AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e000100004011DEAD0101025c0101025812b512b5003a202e0c00000300015900fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) dnl Idem, with Rx cksum bad but with a valid and an invalid packet. AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_bad=true]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a02320800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fDEAD0101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-vsctl set interface p0 options:ol_ip_rx_csum_set_bad=false]) dnl Send various incorrect bad UDP checksum packets. AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003aDEAD0800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) dnl Idem, with Rx cksum bad but with a valid and an invalid packet. AT_CHECK([ovs-vsctl set interface p0 options:ol_l4_rx_csum_set_bad=true]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003a02320800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500004e00010000401173e90101025c0101025812b512b5003aDEAD0800000000007b00fe71d883724fbeb6f4e1494a08004500001c0001000040013ede1e0000011e0000020000ffff00000000']) AT_CHECK([ovs-vsctl set interface p0 options:ol_l4_rx_csum_set_bad=false]) ovs-appctl time/warp 5000 AT_CHECK([ovs-appctl coverage/read-counter datapath_drop_tunnel_pop_error], [0], [8 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_err], [0], [5 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_err], [0], [3 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l3csum_checked], [0], [15 ]) AT_CHECK([ovs-appctl coverage/read-counter native_tnl_l4csum_checked], [0], [7 ]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter drop_action_congestion ], [0], [dnl 1 ]) dnl Check GREL3 only accepts non-fragmented packets? AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port [[37]]' | sort], [0], [dnl port 3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=? port 7: rx pkts=5, bytes=434, drop=?, errs=?, frame=?, over=?, crc=? ]) dnl Send out packets received from L3GRE tunnel back to L3GRE tunnel AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-ofctl add-flow int-br "in_port=7,actions=set_field:3->in_port,7"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-vsctl -- set Interface br0 options:pcap=br0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 1000 AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) AT_CHECK([tail -6 p0.pcap.txt], [0], [dnl aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ]) dnl Check decapsulation of Geneve packet with options AT_CAPTURE_FILE([ofctl_monitor.log]) AT_CHECK([ovs-ofctl monitor int-br 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log]) AT_CHECK([ovs-ofctl del-flows int-br]) AT_CHECK([ovs-ofctl add-flow int-br "tun_metadata0=0xa/0xf,actions=5,controller"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab64080045000096794640004011ba5b0101025c01010258308817c1008200000400655800007b00ffff80010000000affff00010000000bfe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2]) OVS_APP_EXIT_AND_WAIT(ovs-ofctl) AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=98 tun_id=0x7b,tun_src=1.1.2.92,tun_dst=1.1.2.88,tun_metadata0=0xa,in_port=5 (via action) data_len=98 (unbuffered) icmp,vlan_tci=0x0000,dl_src=be:b6:f4:e1:49:4a,dl_dst=fe:71:d8:83:72:4f,nw_src=30.0.0.1,nw_dst=30.0.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,icmp_type=0,icmp_code=0 icmp_csum:4227 ]) AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 5'], [0], [dnl port 5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=? ]) AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)' | sed -e 's/recirc_id=[[0-9]]*/recirc_id=/g'], [0], [dnl recirc_id(0),tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(+key)),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=,rule_cookie=0,controller_id=0,max_len=65535)) ]) dnl Receive VXLAN with different MAC and verify that the neigh cache gets updated AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000f8bc1244cafe08004500004e00010000401173e90101025c01010258c85312b5003a8cd40c00000300007b00ffffffffffff00000000000008004500001c0001000040117cce7f0000017f0000010035003500080172']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:ca:fe,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:ca:fe br0 1.1.2.93 f8:bc:12:44:34:b7 br0 ]) dnl Restore and check the cache entries AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000f8bc124434b608004500004e00010000401173e90101025c01010258c85312b5003a8cd40c00000300007b00ffffffffffff00000000000008004500001c0001000040117cce7f0000017f0000010035003500080172']) ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 dnl Check VXLAN tunnel push AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) dnl Check VXLAN tunnel push with checksum. AT_CHECK([ovs-vsctl set Interface t2 options:csum=true]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=36:b1:ee:7c:01:01,dst=36:b1:ee:7c:01:02),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1 ]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 1.1.2.93 f8:bc:12:44:34:b7 br0 ]) ovs-appctl time/warp 10000 AT_CHECK([ovs-vsctl del-port int-br t3 \ -- del-port int-br t5 \ -- set Interface t1 type=vxlan \ -- set Interface t2 options:dst_port=4790 \ ], [0]) dnl Check tunnel lookup entries after deleting/reconfiguring some ports AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: genev_sys_6081 (6081) ref_cnt=1 gre_sys (3) ref_cnt=1 gtpu_sys_2152 (2152) ref_cnt=1 vxlan_sys_4789 (4789) ref_cnt=2 vxlan_sys_4790 (4790) ref_cnt=1 ]) AT_CHECK([ovs-vsctl del-port int-br t1 \ -- del-port int-br t2 \ -- del-port int-br t4 \ -- del-port int-br t6 \ -- del-port int-br t7 \ -- del-port int-br t8 \ ], [0]) dnl Check tunnel lookup entries after deleting all remaining tunnel ports AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: ]) OVS_VSWITCHD_STOP(["/dropping tunnel packet marked ECN CE but is not ECN capable/d /ip packet has invalid checksum/d"]) AT_CLEANUP AT_SETUP([tunnel_push_pop - packet_out]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=geneve \ options:remote_ip=1.1.2.92 options:key=123 ofport_request=2 \ ]) dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(2),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) dnl Output to tunnel from a int-br internal port AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=output:2"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br '50540000000a5054000000091234']) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 50540000000a5054000000091234 | wc -l` -ge 1]) dnl Output to tunnel from the controller AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out int-br CONTROLLER "output:2" '50540000000a5054000000091235']) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 50540000000a5054000000091235 | wc -l` -ge 1]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - packet_out debug_slow]) OVS_VSWITCHD_START( [add-port br0 p0 dnl -- set Interface p0 type=dummy ofport_request=1 dnl other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 dnl -- set Interface t2 type=geneve options:remote_ip=1.1.2.92 dnl options:key=123 ofport_request=2]) dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(2),dnl eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ]) AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) packet=50540000000a505400000009123 dnl Source port is based on a packet hash, so it may differ depending on the dnl compiler flags and CPU type. Masked with '....'. encap=f8bc124434b6aa55aa5500000800450000320000400040113406010102580101025c....17c1001e00000000655800007b00 dnl Output to tunnel from a int-br internal port. dnl Checking that the packet arrived and it was correctly encapsulated. AT_CHECK([ovs-ofctl add-flow int-br "in_port=LOCAL,actions=debug_slow,output:2"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"]) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep -E "${encap}${packet}4" | wc -l` -ge 1]) dnl Sending again to exercise the non-miss upcall path. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4"]) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep -E "${encap}${packet}4" | wc -l` -ge 2]) dnl Send two more packets at the same time to make sure they are distinct dnl memory buffers. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}4" "${packet}4"]) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep -E "${encap}${packet}4" | wc -l` -ge 4]) dnl Make sure all the packets are the same, i.e. have the same source port. AT_CHECK([ovs-pcap p0.pcap | sed 's/.$//' | sort | uniq \ | grep -E -c "${encap}${packet}"], [0], [1 ]) dnl Output to tunnel from the controller. AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out int-br CONTROLLER "debug_slow,output:2" "${packet}5"]) OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep -E "${encap}${packet}5" | wc -l` -ge 1]) dnl Datapath actions should not have tunnel push action. AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q tnl_push], [1]) dnl There should be slow_path action instead. AT_CHECK([ovs-appctl dpctl/dump-flows | grep -q 'slow_path(action)'], [0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - local_ip configuration]) OVS_VSWITCHD_START( [add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1 \ other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 \ -- set Interface t2 type=geneve \ options:local_ip=2.2.2.88 \ options:remote_ip=1.1.2.92 \ options:key=123 ofport_request=2]) dnl Setup multiple IP addresses. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 2.2.2.0/24 dev br0 SRC 2.2.2.88 Cached: 2.2.2.88/32 dev br0 SRC 2.2.2.88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ]) dnl Check that local_ip is used for encapsulation in the trace. AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ | grep -E 'tunnel|actions'], [0], [dnl -> output to native tunnel -> tunneling to 1.1.2.92 via br0 -> tunneling from aa:55:aa:55:00:00 2.2.2.88 to f8:bc:12:44:34:b6 1.1.2.92 Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=2.2.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),1 ]) dnl Now check that the packet actually has the local_ip in the header. AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) packet=50540000000a5054000000091234 eth=f8bc124434b6aa55aa5500000800 ip4=450000320000400040113305020202580101025c dnl Source port is based on a packet hash, so it may differ depending on the dnl compiler flags and CPU type. Masked with '....'. udp=....17c1001e0000 geneve=0000655800007b00 encap=${eth}${ip4}${udp}${geneve} dnl Output to tunnel from a int-br internal port. dnl Checking that the packet arrived and it was correctly encapsulated. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) dnl Sending again to exercise the non-miss upcall path. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) dnl Finally, checking that the datapath flow also has a local_ip. AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ | strip_ufid | strip_used], [0], [dnl recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl packets:1, bytes:14, used:0.0s, dnl actions:tnl_push(tnl_port(6081),header(size=50,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=2.2.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - underlay bridge match]) OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=gre \ options:remote_ip=1.1.2.92 options:key=456 options:seq=true ofport_request=3], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) t1 3/3: (gre: key=456, remote_ip=1.1.2.92, seq=true) ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local ]) AT_CHECK([ovs-ofctl add-flow br0 'arp,priority=1,action=normal']) AT_CHECK([ovs-appctl revalidator/wait]) dnl Use arp reply to achieve tunnel next hop mac binding AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 ]) AT_CHECK([ovs-ofctl add-flow br0 'ip,ip_proto=47,nw_tos=0,eth_src=aa:55:aa:55:00:00,eth_dst=f8:bc:12:44:34:b6,ip_src=1.1.2.88,ip_dst=1.1.2.92,priority=99,action=normal']) dnl Direct traffic from the integration bridge to the GRE tunnel AT_CHECK([ovs-ofctl add-flow int-br action=3]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4789)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=46,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x3000,proto=0x6558),key=0x1c8,seq=0x0)),out_port(100)),1 ]) dnl Verify outer L2 and L3 header flow fields can be matched in the underlay bridge AT_CHECK([ovs-appctl netdev-dummy/receive int-br '50540000000a5054000000091234']) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl) n_packets=1, n_bytes=42, priority=1,arp actions=NORMAL n_packets=1, n_bytes=60, priority=99,ip,dl_src=aa:55:aa:55:00:00,dl_dst=f8:bc:12:44:34:b6,nw_src=1.1.2.88,nw_dst=1.1.2.92,nw_proto=47,nw_tos=0 actions=NORMAL NXST_FLOW reply: ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - flow translation on unrelated bridges]) OVS_VSWITCHD_START( [add-port br0 p0 dnl -- set Interface p0 type=dummy ofport_request=1 dnl other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 dnl -- set Interface t2 type=geneve options:remote_ip=1.1.2.92 dnl options:key=123 ofport_request=2]) dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(2),dnl eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ]) dnl Creating a separate bridge that is completely unrelated to a tunnel dnl configuration. Ports in this bridge cannot be tunnel endpoints. AT_CHECK([ovs-vsctl add-br br-non-tunnel dnl -- set bridge br-non-tunnel datapath_type=dummy fail-mode=secure]) add_of_ports br-non-tunnel 7 8 AT_CHECK([ovs-ofctl del-flows br-non-tunnel]) AT_CHECK([ovs-ofctl add-flow br-non-tunnel in_port=p7,action=p8]) AT_CHECK([ovs-ofctl add-flow br-non-tunnel in_port=p8,action=p7]) dnl Checking that tunnel configuration doesn't impact flow translation dnl on this bridge (Megaflow should contain a bare minimum of fields dnl according to installed OF rules). AT_CHECK([ovs-appctl ofproto/trace br-non-tunnel in_port=p7], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,in_port=7,dl_type=0x0000 Datapath actions: 8 ]) AT_CHECK([ovs-appctl ofproto/trace br-non-tunnel in_port=p8], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,in_port=8,dl_type=0x0000 Datapath actions: 7 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - VXLAN access port]) dnl Create bridge that has a MAC address. OVS_VSWITCHD_START([set bridge br0 datapath_type=dummy dnl -- set Interface br0 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-port br0 p8 dnl -- set Interface p8 type=dummy ofport_request=8]) dnl Create another bridge. AT_CHECK([ovs-vsctl add-br ovs-tun0 -- set bridge ovs-tun0 datapath_type=dummy]) dnl Add VXLAN port to this bridge. AT_CHECK([ovs-vsctl add-port ovs-tun0 tun0 dnl -- set int tun0 type=vxlan options:remote_ip=10.0.0.11 dnl -- add-port ovs-tun0 p7 dnl -- set interface p7 type=dummy ofport_request=7]) dnl Set VLAN tags, so that br0 and its port p8 have the same tag, dnl but ovs-tun0's port p7 has a different tag. AT_CHECK([ovs-vsctl set port p8 tag=42 dnl -- set port br0 tag=42 dnl -- set port p7 tag=200]) dnl Set an IP address for br0. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 10.0.0.2/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 10.0.0.0/24 dev br0 SRC 10.0.0.2 Cached: 10.0.0.2/32 dev br0 SRC 10.0.0.2 local ]) dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded dnl to learned port. AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),dnl eth(src=aa:55:aa:66:00:00,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=10.0.0.11,tip=10.0.0.2,op=2,sha=aa:55:aa:66:00:00,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl ofproto/trace ovs-tun0 in_port=p7], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,in_port=7,dl_src=00:00:00:00:00:00,dnl dl_dst=00:00:00:00:00:00,dl_type=0x0000 Datapath actions: push_vlan(vid=200,pcp=0),1,tnl_push(tnl_port(4789),dnl header(size=50,type=4,eth(dst=aa:55:aa:66:00:00,src=aa:55:aa:55:00:00,dnl dl_type=0x0800),ipv4(src=10.0.0.2,dst=10.0.0.11,proto=17,tos=0,ttl=64,dnl frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),dnl out_port(100)),8 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - no clone when is_last_action]) dnl Create bridge that has a MAC address. OVS_VSWITCHD_START([set bridge br0 datapath_type=dummy dnl -- set Interface br0 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-port br0 p8 dnl -- set Interface p8 type=dummy ofport_request=8]) dnl Create another bridge. AT_CHECK([ovs-vsctl add-br ovs-tun0 -- set bridge ovs-tun0 datapath_type=dummy]) dnl Add VXLAN port to this bridge. AT_CHECK([ovs-vsctl add-port ovs-tun0 tun0 dnl -- set int tun0 type=vxlan options:remote_ip=10.0.0.11 dnl -- add-port ovs-tun0 p7 dnl -- set interface p7 type=dummy ofport_request=7]) dnl Set an IP address for br0. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 10.0.0.2/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 10.0.0.0/24 dev br0 SRC 10.0.0.2 Cached: 10.0.0.2/32 dev br0 SRC 10.0.0.2 local ]) dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded dnl to learned port. AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows ovs-tun0]) AT_CHECK([ovs-ofctl add-flow ovs-tun0 "in_port=p7,actions=tun0"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),dnl eth(src=aa:55:aa:66:00:00,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=10.0.0.11,tip=10.0.0.2,op=2,sha=aa:55:aa:66:00:00,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl ofproto/trace ovs-tun0 in_port=p7], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,in_port=7,dl_type=0x0000 Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,dnl eth(dst=aa:55:aa:66:00:00,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=10.0.0.2,dst=10.0.0.11,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(100)),8 ]) dnl Perform a negative check to make sure clone is still present. AT_CHECK([ovs-ofctl del-flows ovs-tun0]) AT_CHECK([ovs-ofctl add-flow ovs-tun0 "in_port=p7,actions=tun0,in_port"]) AT_CHECK([ovs-appctl revalidator/wait]) AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),dnl eth(src=aa:55:aa:66:00:00,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=10.0.0.11,tip=10.0.0.2,op=2,sha=aa:55:aa:66:00:00,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl ofproto/trace ovs-tun0 in_port=p7], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,in_port=7,dl_type=0x0000 Datapath actions: clone(tnl_push(tnl_port(4789),header(size=50,dnl type=4,eth(dst=aa:55:aa:66:00:00,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=10.0.0.2,dst=10.0.0.11,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(100)),8),7 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - use non-local port as tunnel endpoint]) OVS_VSWITCHD_START([add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1]) dnl Adding another port separately to ensure that it gets an dnl aa:55:aa:55:00:03 MAC address (dummy port number 3). AT_CHECK([ovs-vsctl add-port br0 vtep0 \ -- set interface vtep0 type=dummy ofport_request=2]) AT_CHECK([ovs-vsctl \ -- add-br int-br \ -- set bridge int-br datapath_type=dummy \ -- set Interface int-br ofport_request=3]) AT_CHECK([ovs-vsctl \ -- add-port int-br t1 \ -- set Interface t1 type=gre ofport_request=4 \ options:remote_ip=1.1.2.92 ]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) vtep0 2/2: (dummy) int-br: int-br 65534/3: (dummy-internal) t1 4/4: (gre: remote_ip=1.1.2.92) ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr vtep0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev vtep0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev vtep0 SRC 1.1.2.88 local ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Use arp request and reply to achieve tunnel next hop mac binding. dnl By default, vtep0's MAC address is aa:55:aa:55:00:03. AT_CHECK([ovs-appctl netdev-dummy/receive vtep0 'recirc_id(0),in_port(2),dnl eth(dst=ff:ff:ff:ff:ff:ff,src=aa:55:aa:55:00:03),eth_type(0x0806),dnl arp(tip=1.1.2.92,sip=1.1.2.88,op=1,sha=aa:55:aa:55:00:03,tha=00:00:00:00:00:00)']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:03),eth_type(0x0806),dnl arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=aa:55:aa:55:00:03)']) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.92 f8:bc:12:44:34:b6 br0 ]) dnl Check GRE tunnel pop. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:03),eth_type(0x0800),dnl ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_pop(4) ]) dnl Check GRE tunnel push. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),dnl eth(dst=f9:bc:12:44:34:b6,src=af:55:aa:55:00:03),eth_type(0x0800),dnl ipv4(src=1.1.3.88,dst=1.1.3.92,proto=1,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4),header(size=38,type=3,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:03,dl_type=0x0800),dnl ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),dnl gre((flags=0x0,proto=0x6558))),out_port(2)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This is a regression test for outer header checksum offloading dnl with recirculation. AT_SETUP([tunnel_push_pop - recirculation after encapsulation]) OVS_VSWITCHD_START( [add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1 \ other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) AT_CHECK([ovs-vsctl add-port int-br t2 \ -- set Interface t2 type=geneve \ options:remote_ip=1.1.2.92 \ options:key=123 ofport_request=2]) dnl Setup an IP address. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local ]) dnl Add a dp-hash selection group. AT_CHECK([ovs-ofctl add-group br0 \ 'group_id=1234,type=select,selection_method=dp_hash,bucket=weight=1,output:p0']) AT_CHECK([ovs-ofctl add-flow br0 in_port=br0,action=group:1234]) AT_CHECK([ovs-ofctl add-flow br0 in_port=p0,action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl This ARP reply from p0 has two effects: dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ]) dnl Check that selection group is used in the trace. AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ | grep -E 'tunnel|actions'], [0], [dnl -> output to native tunnel -> tunneling to 1.1.2.92 via br0 -> tunneling from aa:55:aa:55:00:00 1.1.2.88 to f8:bc:12:44:34:b6 1.1.2.92 Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),dnl hash(l4(0)),recirc(0x1) ]) dnl Now check that the packet is actually encapsulated and delivered. AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) packet=50540000000a5054000000091234 eth=f8bc124434b6aa55aa5500000800 ip4=450000320000400040113406010102580101025c dnl Source port is based on a packet hash, so it may differ depending on the dnl compiler flags and CPU type. Masked with '....'. udp=....17c1001e0000 geneve=0000655800007b00 encap=${eth}${ip4}${udp}${geneve} dnl Output to tunnel from a int-br internal port. dnl Checking that the packet arrived and it was correctly encapsulated. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) dnl Sending again to exercise the non-miss upcall path. AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) dnl Finally, checking that the datapath flow is also correct. AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ | strip_ufid | strip_used], [0], [dnl recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl packets:1, bytes:14, used:0.0s, dnl actions:tnl_push(tnl_port(6081),header(size=50,type=5,dnl eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),dnl hash(l4(0)),recirc(0x2) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - Mirror over tunnels]) OVS_VSWITCHD_START([dnl add-br br-ext -- set bridge br-ext datapath_type=dummy \ other-config:hwaddr=aa:55:aa:55:00:00 \ -- add-port br0 t1 -- set Interface t1 type=geneve \ options:remote_ip=1.1.1.1 \ -- add-port br0 t2 -- set Interface t2 type=erspan \ options:remote_ip=1.1.1.2 options:key=flow options:erspan_ver=1 \ options:erspan_idx=flow \ -- add-port br0 p0 -- set Interface p0 type=dummy \ -- add-port br0 p1 -- set Interface p1 type=dummy \ -- add-port br-ext p-ext -- set Interface p-ext type=dummy \ options:pcap=ext.pcap]) dnl Configure mirroring over the UDP and ERSPAN tunnels. AT_CHECK([dnl ovs-vsctl \ set Bridge br0 mirrors=@m1,@m2 -- \ --id=@t1 get Port t1 -- \ --id=@t2 get Port t2 -- \ --id=@m1 create Mirror name=vxlan select_all=true output_port=@t1 -- \ --id=@m2 create Mirror name=erspan select_all=true output_port=@t2], [0], [stdout]) AT_CHECK([ovs-ofctl add-flow br-ext actions=normal]) AT_CHECK([ovs-ofctl add-flow br0 actions=normal]) AT_CHECK([ovs-appctl revalidator/wait]) dnl Make sure ephemeral ports stay static across tests. AT_CHECK([ovs-appctl tnl/egress_port_range 35190 35190], [0], [OK ]) dnl Setup an IP address for the local side of the tunnel. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br-ext 1.1.1.3/24], [0], [OK ]) dnl Send two arp replies to populate arp table with tunnel remote endpoints. AT_CHECK([ovs-appctl netdev-dummy/receive p-ext dnl 'eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.1.1,tip=1.1.1.3,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ]) AT_CHECK([ovs-appctl netdev-dummy/receive p-ext dnl 'eth(src=f8:bc:12:44:34:b3,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl arp(sip=1.1.1.2,tip=1.1.1.3,op=2,sha=f8:bc:12:44:34:b3,tha=00:00:00:00:00:00)' ]) m4_define([FLOW], [m4_join([,], [in_port(p1)], [eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800)], [ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)], [icmp(type=8,code=0)])]) m4_define([ERSPAN_ACT], [m4_join([,], [clone(tnl_push(tnl_port(erspan_sys)], [header(size=50,type=107], [eth(dst=f8:bc:12:44:34:b3,src=aa:55:aa:55:00:00,dl_type=0x0800)], [ipv4(src=1.1.1.3,dst=1.1.1.2,proto=47,tos=0,ttl=64,frag=0x4000)], [erspan(ver=1,sid=0x0,idx=0x0))], [out_port(br-ext))], [p-ext)])]) m4_define([GENEVE_ACT], [m4_join([,], [clone(tnl_push(tnl_port(genev_sys_6081)], [header(size=50,type=5], [eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800)], [ipv4(src=1.1.1.3,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x4000)], [udp(src=0,dst=6081,csum=0x0)], [geneve(vni=0x0))], [out_port(br-ext))], [p-ext)])]) dnl Verify packet is mirrored to both tunnels. Tunnel actions may happen dnl in any order. AT_CHECK([ovs-appctl ofproto/trace --names ovs-dummy "FLOW"], [0], [stdout]) AT_CHECK([grep -q "ERSPAN_ACT" stdout]) AT_CHECK([grep -q "GENEVE_ACT" stdout]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - tunnel drop]) OVS_VSWITCHD_START([dnl -- add-port br0 t1 -- set Interface t1 type=geneve \ options:remote_ip=1.1.1.1]) add_of_ports br0 1 2 AT_CHECK([ovs-ofctl add-flow br0 in_port=p1,action=t1,p2]) AT_CHECK([ovs-appctl ofproto/trace --names br0 in_port=p1], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: clone(drop),p2 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel_push_pop - use two VTEPs in same subnet with rules]) dnl Create bridge that has a MAC address. OVS_VSWITCHD_START([set bridge br0 datapath_type=dummy dnl -- set Interface br0 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-port br0 p0 \ -- set Interface p0 type=dummy ofport_request=1]) AT_CHECK([ovs-vsctl add-br br1 \ -- set bridge br1 datapath_type=dummy \ -- set Interface br1 other-config:hwaddr=aa:66:aa:66:00:00]) AT_CHECK([ovs-vsctl add-port br1 p1 \ -- set interface p1 type=dummy ofport_request=2]) AT_CHECK([ovs-vsctl \ -- add-br int-br \ -- set bridge int-br datapath_type=dummy \ -- set Interface int-br other-config:hwaddr=aa:77:aa:77:00:00]) AT_CHECK([ovs-vsctl \ -- add-port int-br t0 \ -- set Interface t0 type=gre ofport_request=3 \ options:local_ip=1.1.2.80 \ options:remote_ip=1.1.2.90 ]) AT_CHECK([ovs-vsctl \ -- add-port int-br t1 \ -- set Interface t1 type=gre ofport_request=4 \ options:local_ip=1.1.2.81 \ options:remote_ip=1.1.2.91 ]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) br1: br1 65534/101: (dummy-internal) p1 2/2: (dummy) int-br: int-br 65534/3: (dummy-internal) t0 3/4: (gre: local_ip=1.1.2.80, remote_ip=1.1.2.90) t1 4/4: (gre: local_ip=1.1.2.81, remote_ip=1.1.2.91) ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.80/24], [0], [OK ]) AT_CHECK([ovs-appctl netdev-dummy/ip4addr br1 1.1.2.81/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed dnl on br1 only. AT_CHECK([ovs-appctl ovs/route/show | grep Cached], [0], [dnl Cached: 1.1.2.80/32 dev br0 SRC 1.1.2.80 local Cached: 1.1.2.81/32 dev br1 SRC 1.1.2.81 local Cached: 1.1.2.0/24 dev br1 SRC 1.1.2.81 ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl add-flow br1 action=normal]) AT_CHECK([ovs-ofctl add-flow int-br action=normal]) dnl Use arp reply to achieve tunnel next hop mac binding. AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl 'recirc_id(0),in_port(1),dnl eth(src=f8:bc:12:44:34:b0,dst=aa:55:aa:55:00:00),eth_type(0x0806),dnl arp(sip=1.1.2.90,tip=1.1.2.80,op=2,sha=f8:bc:12:44:34:b0,tha=00:00:00:00:00:00)' ]) AT_CHECK([ovs-appctl netdev-dummy/receive p1 dnl 'recirc_id(0),in_port(2),dnl eth(src=f8:bc:12:44:34:b1,dst=aa:66:aa:66:00:00),eth_type(0x0806),dnl arp(sip=1.1.2.91,tip=1.1.2.81,op=2,sha=f8:bc:12:44:34:b1,tha=00:00:00:00:00:00)' ]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 1.1.2.90 f8:bc:12:44:34:b0 br0 1.1.2.91 f8:bc:12:44:34:b1 br1 ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.2.0/24 br0 src=1.1.2.80 table=10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/add 1.1.2.0/24 br1 src=1.1.2.81 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/show table=all | sort], [0], [dnl Cached: 1.1.2.0/24 dev br1 SRC 1.1.2.81 Cached: 1.1.2.80/32 dev br0 SRC 1.1.2.80 local Cached: 1.1.2.81/32 dev br1 SRC 1.1.2.81 local User: 1.1.2.0/24 dev br0 SRC 1.1.2.80 table 10 User: 1.1.2.0/24 dev br1 SRC 1.1.2.81 table 11 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(int-br),dnl eth(src=00:00:00:00:00:00,dst=f8:bc:12:44:34:b0),eth_type(0x0800),dnl ipv4(src=1.1.3.100,dst=1.1.3.200,proto=1,tos=0,ttl=64,frag=no),dnl icmp(type=8,code=0)' | grep 'tunneling to.*via'], [0], [dnl -> tunneling to 1.1.2.90 via br1 -> tunneling to 1.1.2.91 via br1 ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=1.1.2.80/32 table=10], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/add from=1.1.2.81/32 table=11], [0], [OK ]) AT_CHECK([ovs-appctl ovs/route/rule/show], [0], [dnl Cached: 0: from all lookup local User: 32764: from 1.1.2.81 lookup 11 User: 32765: from 1.1.2.80 lookup 10 Cached: 32766: from all lookup main Cached: 32767: from all lookup default ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(int-br),dnl eth(src=00:00:00:00:00:00,dst=f8:bc:12:44:34:b0),eth_type(0x0800),dnl ipv4(src=1.1.3.100,dst=1.1.3.200,proto=1,tos=0,ttl=64,frag=no),dnl icmp(type=8,code=0)' | grep 'tunneling to.*via'], [0], [dnl -> tunneling to 1.1.2.90 via br0 -> tunneling to 1.1.2.91 via br1 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/tunnel.at000066400000000000000000002042011514270232600217540ustar00rootroot00000000000000AT_BANNER([tunnel]) AT_SETUP([tunnel - input]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=gre \ options:local_ip=2.2.2.2 options:remote_ip=1.1.1.1 \ ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=gre \ options:remote_ip=2.2.2.2 ofport_request=3]) AT_DATA([flows.txt], [dnl actions=IN_PORT ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1) p3 3/1: (gre: remote_ip=2.2.2.2) ]) dnl remote_ip AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,flags(df))),1 ]) dnl local_ip, remote_ip AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df))),1 ]) dnl reconfigure, local_ip, remote_ip AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \ options:df_default=false options:ttl=1 options:csum=true \ -- set Interface p3 type=vxlan]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1) p3 3/4789: (vxlan: remote_ip=2.2.2.2) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,flags(df))),1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(src=2.2.2.3,dst=1.1.1.1,ttl=1,flags(csum))),1 ]) dnl nonexistent tunnel AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=5.5.5.5,dst=6.6.6.6,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl no OpenFlow tunnel port for this packet ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"]) AT_CLEANUP AT_SETUP([tunnel - ECN decapsulation]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) AT_DATA([flows.txt], [dnl actions=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/2: (dummy) ]) dnl Tunnel CE and encapsulated packet CE AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=3,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_flags=-key,in_port=1,nw_ecn=3,nw_frag=no Datapath actions: 2 ]) dnl Tunnel CE and encapsulated packet ECT(1) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_flags=-key,in_port=1,nw_ecn=1,nw_frag=no Datapath actions: set(ipv4(tos=0x3/0x3)),2 ]) dnl Tunnel CE and encapsulated packet ECT(2) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=2,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_flags=-key,in_port=1,nw_ecn=2,nw_frag=no Datapath actions: set(ipv4(tos=0x3/0x3)),2 ]) dnl Tunnel CE and encapsulated packet Non-ECT AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [Final flow: unchanged Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_flags=-key,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: drop ]) OVS_VSWITCHD_STOP(["/dropping tunnel packet marked ECN CE but is not ECN capable/d"]) AT_CLEANUP AT_SETUP([tunnel - input with matching tunnel mask]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 \ ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/2: (dummy) ]) AT_CHECK([ovs-appctl dpctl/add-flow "tunnel(dst=1.1.1.1,src=3.3.3.200/255.255.255.0,tp_dst=123,tp_src=1,ttl=64),recirc_id(0),in_port(1),eth(),eth_type(0x0800),ipv4()" "2"]) AT_CHECK([ovs-appctl dpctl/dump-flows | tail -1], [0], [dnl recirc_id(0),tunnel(src=3.3.3.200/255.255.255.0,dst=1.1.1.1,ttl=64,tp_src=1,tp_dst=123),in_port(1),eth(),eth_type(0x0800), packets:0, bytes:0, used:never, actions:2 ]) OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"]) AT_CLEANUP AT_SETUP([tunnel - too long nested attributes]) OVS_VSWITCHD_START([add-port br0 p1 \ -- set Interface p1 type=gre options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/2: (dummy) ]) dst_single="dst=1.1.1.1" dst_rep=${dst_single} dnl Size of one OVS_TUNNEL_KEY_ATTR_IPV4_DST is 4 bytes + NLA_HDRLEN (4 bytes). dnl One nested message has room for UINT16_MAX - NLA_HDRLEN (4) bytes, i.e. dnl (UINT16_MAX - NLA_HDRLEN) / (4 + NLA_HDRLEN) = 8191.375 of dst addresses. for i in `seq 1 8192` ; do dst_rep="${dst_rep},${dst_single}" done AT_CHECK([ovs-appctl dpctl/add-flow "tunnel(${dst_rep})" "2" 2>&1 | dnl sed "s/${dst_single},//g"], [], [dnl ovs-vswitchd: parsing flow key (syntax error at tunnel(dst=1.1.1.1)) (Argument list too long) ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - output]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) AT_DATA([flows.txt], [dnl actions=output:1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1) p2 2/2: (dummy) ]) dnl Basic AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) dnl ECN AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df|key))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - unencrypted tunnel and not setting skb_mark]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) AT_DATA([flows.txt], [dnl actions=output:1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - unencrypted tunnel and setting skb_mark to 1]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) AT_DATA([flows.txt], [dnl actions=load:0x1->NXM_NX_PKT_MARK[[]],output:1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),set(skb_mark(0x1)),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - unencrypted tunnel and setting skb_mark to 2]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1\ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) AT_DATA([flows.txt], [dnl actions=load:0x2->NXM_NX_PKT_MARK[[]],output:1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),set(skb_mark(0x2)),1 ]) AT_CHECK([ovs-appctl netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ovs-appctl time/warp 5000 AT_CHECK([ ovs-appctl coverage/read-counter datapath_drop_invalid_port ], [0], [dnl 1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - ToS and TTL inheritance]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:tos=inherit \ options:ttl=inherit ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) AT_DATA([flows.txt], [dnl actions=output:1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit) p2 2/2: (dummy) ]) dnl Basic AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,tos=0x4,ttl=128,flags(df))),1 ]) dnl ECN AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=5,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,tos=0x5,ttl=128,flags(df))),1 ]) dnl non-IP AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,flags(df))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - set_tunnel]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=gre options:key=flow \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=gre options:key=flow \ options:remote_ip=2.2.2.2 ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=gre options:key=flow \ options:remote_ip=3.3.3.3 ofport_request=3 \ -- add-port br0 p4 -- set Interface p4 type=gre options:key=flow \ options:remote_ip=4.4.4.4 ofport_request=4]) AT_DATA([flows.txt], [dnl actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tunnel:5,output:4 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: key=flow, remote_ip=1.1.1.1) p2 2/1: (gre: key=flow, remote_ip=2.2.2.2) p3 3/1: (gre: key=flow, remote_ip=3.3.3.3) p4 4/1: (gre: key=flow, remote_ip=4.4.4.4) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x2,dst=2.2.2.2,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x3,dst=3.3.3.3,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x5,dst=4.4.4.4,ttl=64,flags(df|key))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - set_tunnel VXLAN]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=vxlan options:key=flow \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \ options:remote_ip=2.2.2.2 ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \ options:remote_ip=3.3.3.3 ofport_request=3 \ -- add-port br0 p4 -- set Interface p4 type=vxlan options:key=flow \ options:remote_ip=4.4.4.4 ofport_request=4]) AT_DATA([flows.txt], [dnl actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tunnel:5,output:4 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/4789: (vxlan: key=flow, remote_ip=1.1.1.1) p2 2/4789: (vxlan: key=flow, remote_ip=2.2.2.2) p3 3/4789: (vxlan: key=flow, remote_ip=3.3.3.3) p4 4/4789: (vxlan: key=flow, remote_ip=4.4.4.4) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,tp_dst=4789,flags(df|key))),4789,dnl set(tunnel(tun_id=0x2,dst=2.2.2.2,ttl=64,tp_dst=4789,flags(df|key))),4789,dnl set(tunnel(tun_id=0x3,dst=3.3.3.3,ttl=64,tp_dst=4789,flags(df|key))),4789,dnl set(tunnel(tun_id=0x5,dst=4.4.4.4,ttl=64,tp_dst=4789,flags(df|key))),4789 ]) dnl With pre-existing tunnel metadata. AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x1,src=1.1.1.1,dst=5.5.5.5,tp_src=12345,tp_dst=4789,ttl=64,flags(key)),in_port(4789),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x2,dst=2.2.2.2,ttl=64,tp_dst=4789,flags(df|key))),4789,dnl set(tunnel(tun_id=0x3,dst=3.3.3.3,ttl=64,tp_dst=4789,flags(df|key))),4789,dnl set(tunnel(tun_id=0x5,dst=4.4.4.4,ttl=64,tp_dst=4789,flags(df|key))),4789 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - key]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=gre options:key=1 \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=gre options:in_key=2 \ options:out_key=3 options:remote_ip=1.1.1.1 ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=gre options:out_key=5 \ options:remote_ip=1.1.1.1 ofport_request=3]) AT_DATA([flows.txt], [dnl actions=IN_PORT,output:1,output:2,output:3 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: key=1, remote_ip=1.1.1.1) p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1) p3 3/1: (gre: out_key=5, remote_ip=1.1.1.1) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x5,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x5,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl set(tunnel(tun_id=0x5,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,flags(df|key))),1,dnl set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,flags(df|key))),1 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0xf,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl no OpenFlow tunnel port for this packet ovs-appctl: ovs-vswitchd: server returned an error ]) OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"]) AT_CLEANUP AT_SETUP([tunnel - key match]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=gre options:key=flow \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=gre options:key=3 \ options:remote_ip=3.3.3.3 ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 \ -- add-port br0 p4 -- set Interface p4 type=dummy ofport_request=4 \ -- add-port br0 p5 -- set Interface p5 type=dummy ofport_request=5]) AT_DATA([flows.txt], [dnl tun_id=2,actions=output:3 tun_id=3,actions=output:4,set_tunnel:2,resubmit:99,set_tunnel:4,output:2,resubmit:99 tun_id=4,actions=output:5 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (gre: key=flow, remote_ip=1.1.1.1) p2 2/1: (gre: key=3, remote_ip=3.3.3.3) p3 3/3: (dummy) p4 4/4: (dummy) p5 5/5: (dummy) ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 3 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 4,3,set(tunnel(tun_id=0x3,dst=3.3.3.3,ttl=64,flags(df|key))),1,5 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x3,src=3.3.3.3,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 4,3,5 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: drop ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Geneve]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=5000]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/5000: (geneve: dst_port=5000, remote_ip=1.1.1.1) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - VXLAN]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=1.1.1.1 ofport_request=1]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/4789: (vxlan: remote_ip=1.1.1.1) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - table version]) dnl check if changes in the egress bridge flow table affects dnl discovering the link layer address of tunnel endpoints. OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00]) AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0]) AT_CHECK([ovs-vsctl add-port int-br v1 -- set Interface v1 type=vxlan \ options:remote_ip=172.31.1.2 options:key=123 \ ofport_request=2 \ -- add-port int-br v2 -- set Interface v2 type=internal \ ofport_request=3 \ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl dummy@ovs-dummy: hit:0 missed:0 br0: br0 65534/100: (dummy-internal) p0 1/1: (dummy) int-br: int-br 65534/2: (dummy-internal) v1 2/4789: (vxlan: key=123, remote_ip=172.31.1.2) v2 3/3: (dummy-internal) ]) dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 172.31.1.1/24], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: 172.31.1.0/24 dev br0 SRC 172.31.1.1 Cached: 172.31.1.1/32 dev br0 SRC 172.31.1.1 local ]) dnl change the flow table to bump the internal table version AT_CHECK([ovs-ofctl add-flow int-br action=normal]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) dnl Check Neighbour discovery. AT_CHECK([ovs-vsctl -- set Interface p0 options:pcap=p0.pcap]) AT_CHECK([ovs-appctl netdev-dummy/receive int-br 'in_port(2),eth(src=aa:55:aa:55:00:00,dst=f8:bc:12:ff:ff:ff),eth_type(0x0800),ipv4(src=1.1.3.92,dst=1.1.3.88,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)']) AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1]) dnl When the wrong version is used, the flow is not visible and the dnl packet is dropped. AT_CHECK([cat p0.pcap.txt | grep ffffffffffffaa55aa55000008060001080006040001aa55aa550000ac1f0101000000000000ac1f0102 | uniq], [0], [dnl ffffffffffffaa55aa55000008060001080006040001aa55aa550000ac1f0101000000000000ac1f0102 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - ERSPAN]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=erspan \ options:remote_ip=1.1.1.1 options:key=1 options:erspan_ver=1 \ options:erspan_idx=0x0 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=erspan \ options:remote_ip=1.1.1.1 ofport_request=2 \ options:key=flow options:erspan_ver=1 options:erspan_idx=flow \ -- add-port br0 p3 -- set Interface p3 type=erspan \ options:remote_ip=1.1.1.1 ofport_request=3 \ options:key=10 options:erspan_ver=2 options:erspan_dir=flow \ options:erspan_hwid=flow \ -- add-port br0 p4 -- set Interface p4 type=erspan \ options:remote_ip=1.2.3.4 ofport_request=4 \ options:key=flow options:erspan_ver=flow\ ]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (erspan: erspan_idx=0x0, erspan_ver=1, key=1, remote_ip=1.1.1.1) p2 2/1: (erspan: erspan_idx=flow, erspan_ver=1, key=flow, remote_ip=1.1.1.1) p3 3/1: (erspan: erspan_dir=flow, erspan_hwid=flow, erspan_ver=2, key=10, remote_ip=1.1.1.1) p4 4/1: (erspan: erspan_dir=flow, erspan_hwid=flow, erspan_idx=flow, erspan_ver=flow, key=flow, remote_ip=1.2.3.4) ]) dnl Check ERSPAN v1 flow-based tunnel push AT_CHECK([ovs-ofctl add-flow br0 "in_port=1, actions=set_tunnel:11,set_field:0x1->tun_erspan_idx,2"]) dnl Check ERSPAN v2 flow-based tunnel push AT_CHECK([ovs-ofctl add-flow br0 "in_port=2, actions=set_field:1->tun_erspan_dir,set_field:0x0->tun_erspan_hwid,3"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=3, actions=set_field:2->tun_erspan_ver,set_field:1->tun_erspan_dir,set_field:0x0->tun_erspan_hwid,4"]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: in_port=1 actions=set_tunnel:0xb,set_field:0x1->tun_erspan_idx,output:2 in_port=2 actions=set_field:1->tun_erspan_dir,set_field:0->tun_erspan_hwid,output:3 in_port=3 actions=set_field:2->tun_erspan_ver,set_field:1->tun_erspan_dir,set_field:0->tun_erspan_hwid,output:4 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - different VXLAN UDP port]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/4341: (vxlan: dst_port=4341, remote_ip=1.1.1.1) ]) dnl change UDP port AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=5000]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/5000: (vxlan: dst_port=5000, remote_ip=1.1.1.1) ]) dnl change UDP port to default AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=4789]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/4789: (vxlan: remote_ip=1.1.1.1) ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - set_field - tun_src/tun_dst/tun_id]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=gre options:key=flow \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=gre options:key=flow \ options:remote_ip=flow ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=gre options:key=flow \ options:remote_ip=flow options:local_ip=flow ofport_request=3 \ -- add-port br0 p4 -- set Interface p4 type=gre options:key=3 \ options:remote_ip=flow ofport_request=4 \ -- add-port br0 p5 -- set Interface p5 type=gre options:key=flow \ options:remote_ip=5.5.5.5 ofport_request=5]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP add_of_ports br0 90 AT_DATA([flows.txt], [dnl in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5 in_port=1 actions=set_field:42->tun_id,output:1 in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2 in_port=3 actions=set_field:1.1.1.1->tun_src,set_field:4.4.4.4->tun_dst,output:3 in_port=4 actions=set_field:2.2.2.2->tun_dst,output:4 in_port=5 actions=set_field:5->tun_id ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x2a,dst=1.1.1.1,ttl=64,flags(df|key))),1,set(tunnel(tun_id=0x2a,dst=3.3.3.3,ttl=64,flags(df|key))),1,set(tunnel(tun_id=0x2a,src=1.1.1.1,dst=4.4.4.4,ttl=64,flags(df|key))),1,set(tunnel(tun_id=0x3,dst=2.2.2.2,ttl=64,flags(df|key))),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - ERSPAN v1/v2 metadata]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) # Add these ports separately to ensure that they get the datapath port # number expected below. ovs-vsctl -- add-port br0 p3 \ -- set Interface p3 type=erspan \ ofport_request=3 \ options:remote_ip=1.1.1.1 \ options:key=1 options:erspan_ver=1 \ options:erspan_idx=7 \ -- add-port br0 p4 \ -- set Interface p4 type=erspan \ ofport_request=4 \ options:remote_ip=1.1.1.2 \ options:key=2 options:erspan_ver=2 \ options:erspan_dir=1 \ options:erspan_hwid=7 OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/2: (dummy) p3 3/3: (erspan: erspan_idx=0x7, erspan_ver=1, key=1, remote_ip=1.1.1.1) p4 4/3: (erspan: erspan_dir=1, erspan_hwid=0x7, erspan_ver=2, key=2, remote_ip=1.1.1.2) ]) AT_DATA([flows.txt], [dnl in_port=1,actions=3 in_port=2,actions=4 in_port=3,tun_erspan_ver=1,tun_erspan_idx=0x7,actions=1 in_port=4,tun_erspan_ver=2,tun_erspan_dir=1,tun_erspan_hwid=0xf/0x1,actions=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl test encap: in_port=1,actions=3 (erspan v1 port) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))),3 ]) dnl test encap: in_port=2,actions=4 (erspan v2 port) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x2,dst=1.1.1.2,ttl=64,erspan(ver=2,dir=1,hwid=0x7),flags(df|key))),3 ]) dnl receive packet from ERSPAN port with v1 metadata AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,erspan(ver=1,idx=0x7),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0x1,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=1,tun_erspan_idx=0x7,tun_flags=+key,in_port=3,nw_frag=no Datapath actions: 1 ]) dnl receive packet from ERSPAN port with wrong v1 metadata AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,erspan(ver=1,idx=0xabcd),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0x1,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=1,tun_erspan_idx=0xabcd,tun_flags=+key,in_port=3,nw_frag=no Datapath actions: drop ]) dnl receive packet from ERSPAN port with v2 metadata AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=1,hwid=0x7),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=2,tun_erspan_dir=1,tun_erspan_hwid=0x1,tun_flags=+key,in_port=4,nw_frag=no Datapath actions: 2 ]) dnl receive packet from ERSPAN port with wrong v2 metadata AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=0,hwid=0x17),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=2,tun_erspan_dir=0,tun_erspan_hwid=0x1,tun_flags=+key,in_port=4,nw_frag=no Datapath actions: drop ]) dnl test wildcard mask: recevie all v2 regardless of its metadata AT_CHECK([ovs-ofctl del-flows br0 in_port=4,tun_erspan_ver=2,tun_erspan_dir=1,tun_erspan_hwid=0xf/0x1]) AT_CHECK([ovs-ofctl add-flow br0 in_port=4,tun_erspan_ver=2,tun_erspan_dir=0/0,tun_erspan_hwid=0x0/0x0,actions=2]) AT_CHECK([ovs-ofctl --sort=in_port dump-flows br0 | ofctl_strip], [0], [dnl in_port=1 actions=output:3 in_port=2 actions=output:4 tun_erspan_ver=1,tun_erspan_idx=0x7,in_port=3 actions=output:1 tun_erspan_ver=2,in_port=4 actions=output:2 ]) dnl this time it won't drop AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=0,hwid=0x17),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=2,tun_flags=+key,in_port=4,nw_frag=no Datapath actions: 2 ]) dnl flow-based erspan_idx options AT_CHECK([ovs-vsctl add-port br0 p5 -- set Interface p5 type=erspan \ options:remote_ip=1.1.1.2 ofport_request=5 \ options:key=flow options:erspan_ver=1 options:erspan_idx=flow]) AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1, actions=set_tunnel:11,set_field:0x7->tun_erspan_idx,5"]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: in_port=1 actions=set_tunnel:0xb,set_field:0x7->tun_erspan_idx,output:5 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0xb,dst=1.1.1.2,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))),3 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Geneve metadata]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=8}->tun_metadata1"]) AT_DATA([flows.txt], [dnl in_port=2,actions=set_field:0xa->tun_metadata0,set_field:0x1234567890abcdef->tun_metadata1,1 tun_metadata0=0xb/0xf,actions=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) dnl Option generation AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0xa}{class=0xffff,type=0x1,len=8,0x1234567890abcdef}),flags(df))),6081 ]) dnl Option match AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no Datapath actions: 2 ]) dnl Skip unknown option AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}{class=0xffff,type=2,len=4,0xc}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no Datapath actions: 2 ]) dnl Check mapping table constraints AT_CHECK([ovs-ofctl del-flows br0]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=2,len=124}->tun_metadata2,{class=0xffff,type=3,len=124}->tun_metadata3"], [1], [ignore], [OFPT_ERROR (xid=0x4): NXTTMFC_TABLE_FULL NXT_TLV_TABLE_MOD (xid=0x4): ADD mapping table: class type length match field ------ ---- ------ -------------- 0xffff 0x2 124 tun_metadata2 0xffff 0x3 124 tun_metadata3 ]) AT_CHECK([ovs-ofctl add-flow br0 "tun_metadata0,tun_metadata0,actions=drop"], [1], [ignore], [ovs-ofctl: field tun_metadata0 set multiple times ]) AT_CHECK([ovs-ofctl add-flow br0 "tun_metadata0=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,tun_metadata1=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,tun_metadata2=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,tun_metadata3=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,tun_metadata4=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,actions=drop"], [1], [ignore], [ovs-ofctl: field tun_metadata4 exceeds maximum size for tunnel metadata (used 320, max 256) ]) dnl Allocation and match with fragmented address space AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=2,len=124}->tun_metadata2"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata3"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=4,len=112}->tun_metadata4"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata3"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=3,len=8}->tun_metadata3"]) AT_CHECK([ovs-ofctl add-flow br0 tun_metadata3=0x1234567890abcdef,actions=2]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=3,len=8,0x1234567890abcdef}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata3=0x1234567890abcdef,in_port=1,nw_frag=no Datapath actions: 2 ]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [dnl NXST_FLOW reply: tun_metadata3=0x1234567890abcdef actions=output:2 ]) dnl A TLV mapping should not be removed if any active flow uses the mapping. AT_CHECK([ovs-ofctl del-tlv-map br0], [1], [], [dnl OFPT_ERROR (xid=0x4): NXTTMFC_INVALID_TLV_DEL NXT_TLV_TABLE_MOD (xid=0x4): CLEAR ]) AT_CHECK([ovs-ofctl del-flows br0], [0]) AT_CHECK([ovs-ofctl del-tlv-map br0], [0]) dnl Flow modification AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=1,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=2,len=4}->tun_metadata1"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata2"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 actions=multipath(eth_src,50,modulo_n,1,0,tun_metadata0[[0..31]])"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=push:tun_metadata1[[0..31]],clone(move:tun_metadata2[[0..31]]->reg0[[0..31]])"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 actions=output:4"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=push:tun_metadata2[[0..31]]"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=2,len=4}->tun_metadata1"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata2"], [1], [], [dnl OFPT_ERROR (xid=0x4): NXTTMFC_INVALID_TLV_DEL NXT_TLV_TABLE_MOD (xid=0x4): DEL mapping table: class type length match field ------ ---- ------ -------------- 0xffff 0x3 4 tun_metadata2 ]) AT_CHECK([ovs-ofctl del-flows br0], [0]) AT_CHECK([ovs-ofctl del-tlv-map br0], [0]) dnl Learn action AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=1,len=4}->tun_metadata1"]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=2,len=4}->tun_metadata2"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2, eth_src=00:00:00:00:00:01 actions=learn(tun_metadata1[[0..31]]=reg1, output:NXM_OF_IN_PORT[[]])"]) AT_CHECK([ovs-ofctl add-flow br0 "in_port=2, eth_src=00:00:00:00:00:02 actions=learn(reg1[[0..31]]=0xFF, load:reg1[[0..31]]->tun_metadata2[[0..31]])"]) flow1="in_port(2),eth(src=00:00:00:00:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0800)" flow2="in_port(2),eth(src=00:00:00:00:00:02,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0800)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow1" -generate], [0], [stdout]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow2" -generate], [0], [stdout]) dnl Delete flows with learn action AT_CHECK([ovs-ofctl del-flows br0 "in_port=2"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=1,len=4}->tun_metadata1"], [1], [], [dnl OFPT_ERROR (xid=0x4): NXTTMFC_INVALID_TLV_DEL NXT_TLV_TABLE_MOD (xid=0x4): DEL mapping table: class type length match field ------ ---- ------ -------------- 0xffff 0x1 4 tun_metadata1 ]) AT_CHECK([ovs-ofctl del-flows br0 "tun_metadata1"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=1,len=4}->tun_metadata1"]) AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=2,len=4}->tun_metadata2"], [1], [], [dnl OFPT_ERROR (xid=0x4): NXTTMFC_INVALID_TLV_DEL NXT_TLV_TABLE_MOD (xid=0x4): DEL mapping table: class type length match field ------ ---- ------ -------------- 0xffff 0x2 4 tun_metadata2 ]) AT_CHECK([ovs-ofctl del-flows br0 "reg1=0xFF"]) AT_CHECK([ovs-ofctl del-tlv-map br0], [0]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Geneve option present]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=0}->tun_metadata1,{class=0xffff,type=2,len=4}->tun_metadata2"]) AT_DATA([flows.txt], [dnl priority=1,tun_metadata0,actions=2 priority=2,tun_metadata1=0,actions=IN_PORT priority=3,tun_metadata2=0,actions=drop ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl priority=1,tun_metadata0 actions=output:2 priority=2,tun_metadata1 actions=IN_PORT priority=3,tun_metadata2=0 actions=drop NXST_FLOW reply: ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0,tun_metadata1=NP,tun_metadata2=NP,in_port=1,nw_frag=no Datapath actions: 2 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=1,len=0}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata1,tun_metadata2=NP,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0x1,len=0}),flags(df))),6081 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Delete Geneve option]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=4}->tun_metadata1,{class=0xffff,type=2,len=4}->tun_metadata3"]) AT_DATA([flows.txt], [dnl table=0,tun_metadata0=0x11112222,actions=set_field:0x55556666->tun_metadata1,resubmit(,1) table=0,tun_metadata0=0x33334444,actions=delete_field:tun_metadata0,set_field:0x77778888->tun_metadata1,resubmit(,1) table=0,tun_metadata0=0x88889999,actions=delete_field:tun_metadata3,resubmit(,1) table=1,actions=IN_PORT ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x11112222}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0x11112222,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0x11112222}{class=0xffff,type=0x1,len=4,0x55556666}),flags(df))),6081 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x33334444}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0x33334444,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0x1,len=4,0x77778888}),flags(df))),6081 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x88889999}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0x88889999,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0x88889999}),flags(df))),6081 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - concomitant IPv6 and IPv4 tunnels]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=vxlan \ options:remote_ip=2001:cafe::1 ofport_request=2]) AT_DATA([flows.txt], [dnl in_port=1,actions=2 in_port=2,actions=1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0,src=1.1.1.1,dst=1.1.1.2,ttl=64),in_port(4789)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(ipv6_dst=2001:cafe::1,ttl=64,tp_dst=4789,flags(df|csum))),4789 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,ipv6_src=2001:cafe::1,ipv6_dst=2001:cafe::2,ttl=64),in_port(4789)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=4789,flags(df))),4789 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - concomitant IPv6 and IPv4 flow based tunnels]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=vxlan options:key=4 \ options:local_ip=flow options:remote_ip=flow ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=6 \ options:local_ip=flow options:remote_ip=flow ofport_request=2]) AT_DATA([flows.txt], [dnl in_port=1,actions=load:0->tun_src,load:0->tun_dst,set_field:2001:cafe::1->tun_ipv6_src,set_field:fc00::2->tun_ipv6_dst,2 in_port=2,actions=load:0->tun_ipv6_src,load:0->tun_ipv6_dst,set_field:1.1.1.1->tun_src,set_field:4.4.4.4->tun_dst,1 ]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_CHECK([ovs-ofctl -OOpenFlow15 add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ 'tunnel(tun_id=4,src=4.4.4.4,dst=1.1.1.1,ttl=64,flags(df|csum|key)),in_port(4789)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tun_id=0x4,tun_src=4.4.4.4,tun_dst=1.1.1.1,tun_tos=0,tun_flags=+key,in_port=1,dl_type=0x05ff Datapath actions: set(tunnel(tun_id=0x6,ipv6_src=2001:cafe::1,ipv6_dst=fc00::2,ttl=64,tp_dst=4789,flags(df|csum|key))),4789 ]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \ 'tunnel(tun_id=6,ipv6_src=fc00::2,ipv6_dst=2001:cafe::1,ttl=64,flags(df|csum|key)),in_port(4789)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [dnl Megaflow: recirc_id=0,eth,tun_id=0x6,tun_ipv6_src=fc00::2,tun_ipv6_dst=2001:cafe::1,tun_tos=0,tun_flags=+key,in_port=2,dl_type=0x05ff Datapath actions: set(tunnel(tun_id=0x4,src=1.1.1.1,dst=4.4.4.4,ttl=64,tp_dst=4789,flags(df|key))),4789 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - concomitant incompatible tunnels on the same port]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=flow ofport_request=1]) if test "$IS_WIN32" = "yes"; then AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=vxlan \ options:remote_ip=flow options:exts=gbp options:key=1 ofport_request=2], [160], [], [ignore]) else AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=vxlan \ options:remote_ip=flow options:exts=gbp options:key=1 ofport_request=2], [65], [], [ignore]) fi AT_CHECK([grep 'p2: could not set configuration (File exists)' ovs-vswitchd.log | sed "s/^.*\(p2:.*\)$/\1/"], [0], [p2: could not set configuration (File exists) ]) OVS_VSWITCHD_STOP(["/p2: VXLAN-GBP, and non-VXLAN-GBP tunnels can't be configured on the same dst_port/d /p2: could not set configuration (File exists)/d"]) AT_CLEANUP AT_SETUP([tunnel - concomitant incompatible tunnels on different ports]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=flow ofport_request=1]) AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=vxlan options:dst_port=9000 \ options:remote_ip=flow options:exts=gbp ofport_request=2]) AT_CHECK([grep -q 'bridge br0: added interface p2 on port 2' ovs-vswitchd.log]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Mix Geneve/GRE options]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 options:csum=true ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2 \ -- add-port br0 p3 -- set Interface p3 type=gre \ options:remote_ip=2.2.2.2 options:csum=false options:key=123 ofport_request=3]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP AT_DATA([flows.txt], [dnl priority=1,in_port=1,actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl priority=1,in_port=1 actions=output:3 NXST_FLOW reply: ]) dnl the input packet from geneve tunnel has flags(-df+csum+key) flags, making dnl sure that the output gre tunnel has (+df-csum+key). AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(csum|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(tun_id=0x7b,dst=2.2.2.2,ttl=64,flags(df|key))),1 ]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl del-flows br0]) AT_DATA([flows2.txt], [dnl priority=1,in_port=1,tun_metadata0=0x123, actions=3 ]) AT_CHECK([ovs-ofctl add-flows br0 flows2.txt]) AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x123}),flags(csum|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+key,tun_metadata0=0x123,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(tun_id=0x7b,dst=2.2.2.2,ttl=64,flags(df|key))),1 ]) dnl without the fix, the actions have geneve options: dnl set(tunnel(tun_id=0x7b,dst=2.2.2.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x123}),flags(df|key))),1 dnl which is not correct OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - neighbor entry add and deletion]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \ options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \ options:key=5 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=gre \ options:local_ip=3.3.3.3 options:remote_ip=4.4.4.4 \ ofport_request=2]) AT_CHECK([ovs-vsctl add-br br1 -- set bridge br1 datapath_type=dummy], [0]) dnl Populate tunnel neighbor cache table AT_CHECK([ ovs-appctl tnl/arp/set p1 10.0.0.1 00:00:10:00:00:01 ovs-appctl tnl/arp/set p1 10.0.0.2 00:00:10:00:00:02 ovs-appctl tnl/arp/set p2 10.0.1.1 00:00:10:00:01:01 ovs-appctl tnl/arp/set br0 10.0.2.1 00:00:10:00:02:01 ovs-appctl tnl/arp/set br0 10.0.2.2 00:00:10:00:02:02 ovs-appctl tnl/arp/set br1 20.0.0.1 00:00:20:00:00:01 ], [0], [stdout]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 10.0.0.1 00:00:10:00:00:01 p1 10.0.0.2 00:00:10:00:00:02 p1 10.0.1.1 00:00:10:00:01:01 p2 10.0.2.1 00:00:10:00:02:01 br0 10.0.2.2 00:00:10:00:02:02 br0 20.0.0.1 00:00:20:00:00:01 br1 ]) dnl neighbor table after deleting port p1 AT_CHECK([ovs-vsctl del-port br0 p1],[0], [stdout]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | grep -w p1 | sort], [0], [dnl ]) dnl neighbor table after deleting bridge br0 AT_CHECK([ovs-vsctl del-br br0],[0], [stdout]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl 20.0.0.1 00:00:20:00:00:01 br1 ]) dnl neighbor table after deleting bridge br1 AT_CHECK([ovs-vsctl del-br br1],[0], [stdout]) AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - GTP-U basic]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtpu \ options:remote_ip=1.1.1.1 \ options:key=123 ofport_request=1]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/2152: (gtpu: key=123, remote_ip=1.1.1.1) ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: gtpu_sys_2152 (2152) ref_cnt=1 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - GTP-U push and pop]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2]) # Add these ports separately to ensure that they get the datapath port # number expected below. ovs-vsctl -- add-port br0 p3 \ -- set Interface p3 type=gtpu \ ofport_request=3 \ options:remote_ip=1.1.1.1 \ options:key=3 \ options:packet_type=legacy_l3 ovs-vsctl -- add-port br0 p4 \ -- set Interface p4 type=gtpu \ ofport_request=4 \ options:remote_ip=1.1.1.2 \ options:key=4 \ options:packet_type=legacy_l3 OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP dnl AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl p1 1/1: (dummy) p2 2/2: (dummy) p3 3/2152: (gtpu: key=3, remote_ip=1.1.1.1) p4 4/2152: (gtpu: key=4, remote_ip=1.1.1.2) ]) AT_DATA([flows.txt], [dnl in_port=1,actions=3 in_port=2,actions=4 in_port=3,tun_gtpu_flags=0x30,tun_gtpu_msgtype=255,actions=1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: gtpu_sys_2152 (2152) ref_cnt=2 ]) dnl Encap: in_port=1,actions=3 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2152 ]) dnl receive packet from GTP-U port, match it, and output to layer3 GRE AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,ttl=64,gtpu(flags=0x30,msgtype=255),flags(df|key)),in_port(2152),packet_type(ns=1,id=0),skb_mark(0),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: recirc_id=0,packet_type=(1,0),tun_id=0x3,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,gtpu_flags=0x30,gtpu_msgtype=255,tun_flags=+key,in_port=3,dl_type=0x0000 Datapath actions: push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),1 ]) OVS_VSWITCHD_STOP AT_CLEANUP dnl This test configures two tunnels, then deletes the second and re-uses its dnl name for different types of ports. This was introduced to detect errors dnl where port configuration persists even when the port is deleted and dnl readded. AT_SETUP([tunnel - re-create port with different types]) OVS_VSWITCHD_START( [add-port br0 p0 -- set int p0 type=gre options:remote_ip=127.0.0.1 -- \ add-port br0 p1 -- set int p1 type=dummy -- \ add-port br0 p2 -- set int p2 type=dummy]) AT_CHECK([ovs-vsctl set int p1 type=gre options:remote_ip=127.0.0.1]) AT_CHECK([ovs-vsctl del-port p1]) AT_CHECK([ovs-vsctl add-port br0 p1 -- set int p1 type=dummy]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server])] AT_CLEANUP AT_SETUP([tunnel - re-create port with different name]) OVS_VSWITCHD_START( [add-port br0 p0 -- set int p0 type=vxlan options:remote_ip=10.10.10.1]) AT_CHECK([ovs-vsctl --if-exists del-port p0 -- \ add-port br0 p1 -- \ set int p1 type=vxlan options:remote_ip=10.10.10.1]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) OVS_APP_EXIT_AND_WAIT([ovsdb-server])] AT_CLEANUP AT_SETUP([tunnel - SRV6 basic]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=srv6 \ options:remote_ip=flow \ ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP dnl Setup dummy interface IP address. AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 fc00::1/64], [0], [OK ]) dnl Checking that a local route for added IP was successfully installed. AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl Cached: fc00::/64 dev br0 SRC fc00::1 Cached: fc00::1/128 dev br0 SRC fc00::1 local ]) AT_DATA([flows.txt], [dnl in_port=1,actions=set_field:fc00::2->tun_ipv6_dst,output:2 in_port=2,actions=1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl br0 65534/100: (dummy-internal) p1 1/1: (dummy) p2 2/6: (srv6: remote_ip=flow) ]) AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl Listening ports: srv6_sys (6) ref_cnt=1 srv6_sys (6) ref_cnt=1 ]) AT_CHECK([ovs-appctl ofproto/list-tunnels], [0], [dnl port 6: p2 (srv6: ::->flow, key=0, legacy_l3, dp port=6, ttl=64) ]) dnl Encap: ipv4 inner packet AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(ipv6_dst=fc00::2,ttl=64,flags(df))),pop_eth,6 ]) dnl Encap: ipv6 inner packet AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=47,tclass=0x0,hlimit=64)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tunnel(ipv6_dst=fc00::2,ttl=64,flags(df))),pop_eth,6 ]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([tunnel - Geneve metadata mirror]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ -- add-port br0 p2 -- set Interface p2 type=dummy \ ofport_request=2 ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP add_of_ports br0 90 AT_CHECK([ovs-vsctl \ set Bridge br0 mirrors=@m --\ --id=@p90 get Port p90 --\ --id=@m create Mirror name=mymirror select_all=true output_port=@p90], [0], [stdout]) AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=8}->tun_metadata1"]) AT_DATA([flows.txt], [dnl in_port=2,actions=set_field:0xa->tun_metadata0,set_field:0x1234567890abcdef->tun_metadata1,1 tun_metadata0=0xb/0xf,actions=2 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 90,set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0xa}{class=0xffff,type=0x1,len=8,0x1234567890abcdef}),flags(df))),6081 ]) OVS_VSWITCHD_STOP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/unixctl-py.at000066400000000000000000000131311514270232600225630ustar00rootroot00000000000000AT_BANNER([unixctl]) m4_define([APPCTL], [ovs-appctl --timeout 20]) m4_define([PYAPPCTL_PY], [$PYTHON3 $srcdir/appctl.py --timeout 20]) AT_SETUP([unixctl ovs-vswitchd exit - Python3]) AT_KEYWORDS([python unixctl]) OVS_VSWITCHD_START AT_CHECK([PYAPPCTL_PY -t ovs-vswitchd exit], [0], []) OVS_WAIT_WHILE([test -s ovs-vswitchd.pid]) AT_CHECK([PYAPPCTL_PY -t ovsdb-server exit], [0], []) OVS_WAIT_WHILE([test -s ovsdb-server.pid]) AT_CLEANUP AT_SETUP([unixctl ovs-vswitchd list-commands - Python3]) OVS_VSWITCHD_START AT_CHECK([APPCTL list-commands], [0], [stdout]) AT_CHECK([head -1 stdout], [0], [dnl The available commands are: ]) mv stdout expout AT_CHECK([PYAPPCTL_PY list-commands], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP] AT_SETUP([unixctl ovs-vswitchd arguments - Python3]) OVS_VSWITCHD_START AT_CHECK([APPCTL bond/hash], [2], [], [stderr]) AT_CHECK([head -1 stderr], [0], [dnl "bond/hash" command requires at least 1 arguments ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY bond/hash], [2], [], [experr]) AT_CHECK([APPCTL bond/hash mac], [2], [], [stderr]) AT_CHECK([head -1 stderr], [0], [dnl invalid mac ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY bond/hash mac], [2], [], [experr]) AT_CHECK([APPCTL bond/hash mac vlan], [2], [], [stderr]) AT_CHECK([head -1 stderr], [0], [dnl invalid vlan ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY bond/hash mac vlan], [2], [], [experr]) AT_CHECK([APPCTL bond/hash mac vlan basis], [2], [], [stderr]) AT_CHECK([head -1 stderr], [0], [dnl invalid vlan ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY bond/hash vlan basis], [2], [], [experr]) AT_CHECK([APPCTL bond/hash mac vlan basis extra], [2], [], [stderr]) AT_CHECK([head -1 stderr], [0], [dnl "bond/hash" command takes at most 3 arguments ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY bond/hash mac vlan basis extra], [2], [], [experr]) OVS_VSWITCHD_STOP AT_CLEANUP AT_SETUP([unixctl bad target - Python3]) AT_CHECK([PYAPPCTL_PY -t bogus doit], [1], [], [stderr]) AT_CHECK_UNQUOTED([tail -1 stderr], [0], [dnl appctl.py: cannot read pidfile "`pwd`/bogus.pid" (No such file or directory) ]) if test "$IS_WIN32" = "no"; then AT_CHECK([PYAPPCTL_PY -t /bogus/path.pid doit], [1], [], [stderr]) AT_CHECK([tail -1 stderr], [0], [dnl appctl.py: cannot connect to "/bogus/path.pid" (No such file or directory) ]) else AT_CHECK([PYAPPCTL_PY -t c:/bogus/path.pid doit], [1], [], [stderr]) AT_CHECK([tail -1 stderr], [0], [dnl appctl.py: cannot connect to "c:/bogus/path.pid" (No such file or directory) ]) fi AT_CLEANUP AT_SETUP([unixctl server - Python3]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CAPTURE_FILE([`pwd`/test-unixctl.py.log]) AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py help], [0], [stdout]) AT_CHECK([cat stdout], [0], [dnl The available commands are: block echo [[arg ...]] echo_error [[arg ...]] exit help log [[arg ...]] set-options [[--format text|json]] version vlog/close vlog/list vlog/reopen vlog/set spec ]) mv stdout expout AT_CHECK([PYAPPCTL_PY -t test-unixctl.py help], [0], [expout]) AT_CHECK([ovs-vsctl --version | sed 's/ovs-vsctl/test-unixctl.py/' | head -1 > expout]) AT_CHECK([APPCTL -t test-unixctl.py version], [0], [expout]) AT_CHECK([PYAPPCTL_PY -t test-unixctl.py version], [0], [expout]) AT_CHECK_UNQUOTED([PYAPPCTL_PY -t test-unixctl.py --format json version], [0], [dnl {"reply":"$(cat expout)","reply-format":"plain"} ]) AT_CHECK_UNQUOTED([PYAPPCTL_PY -t test-unixctl.py --format JSON version], [0], [dnl {"reply":"$(cat expout)","reply-format":"plain"} ]) AT_CHECK_UNQUOTED([PYAPPCTL_PY -t test-unixctl.py --format json --pretty version], [0], [dnl { "reply":"$(cat expout)", "reply-format":"plain" } ]) AT_CHECK([APPCTL -t test-unixctl.py echo robot ninja], [0], [stdout]) AT_CHECK([cat stdout | sed -e "s/u'/'/g"], [0], [dnl [['robot', 'ninja']] ]) mv stdout expout AT_CHECK([PYAPPCTL_PY -t test-unixctl.py echo robot ninja], [0], [expout]) AT_CHECK([APPCTL -t test-unixctl.py echo_error robot ninja], [2], [], [stderr]) AT_CHECK([cat stderr | sed -e "s/u'/'/g"], [0], [dnl [['robot', 'ninja']] ovs-appctl: test-unixctl.py: server returned an error ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY -t test-unixctl.py echo_error robot ninja], [2], [], [experr]) AT_CHECK([APPCTL -t test-unixctl.py echo], [2], [], [stderr]) AT_CHECK([cat stderr], [0], [dnl "echo" command requires at least 1 arguments ovs-appctl: test-unixctl.py: server returned an error ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY -t test-unixctl.py echo], [2], [], [experr]) AT_CHECK([APPCTL -t test-unixctl.py echo robot ninja pirates], [2], [], [stderr]) AT_CHECK([cat stderr], [0], [dnl "echo" command takes at most 2 arguments ovs-appctl: test-unixctl.py: server returned an error ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY -t test-unixctl.py echo robot ninja pirates], [2], [], [experr]) AT_CHECK([APPCTL -t test-unixctl.py bogus], [2], [], [stderr]) AT_CHECK([cat stderr], [0], [dnl "bogus" is not a valid command ovs-appctl: test-unixctl.py: server returned an error ]) sed 's/ovs-appctl/appctl.py/' stderr > experr AT_CHECK([PYAPPCTL_PY -t test-unixctl.py bogus], [2], [], [experr]) AT_CHECK([APPCTL -t test-unixctl.py exit]) AT_CLEANUP AT_SETUP([unixctl server errors - Python3]) AT_CHECK($PYTHON3 $srcdir/test-unixctl.py --unixctl "`pwd`"/bogus/path, [1], [], [ignore]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/uuid.at000066400000000000000000000020241514270232600214140ustar00rootroot00000000000000AT_BANNER([UUID unit tests]) m4_define([UUID_REGEX], [[[0-9a-f]\{8\}-[0-9a-f]\{4\}-4[0-9a-f]\{3\}-[89ab][0-9a-f]\{3\}-[0-9a-f]\{12\}$]]) m4_define([CHECK_UUID], [if expr "$uuid" : 'UUID_REGEX' > /dev/null then : else echo "$uuid: not a random UUID" exit 1 fi]) AT_SETUP([UUID generation, parsing, serialization]) AT_KEYWORDS([UUID]) AT_CHECK([ uuids= for i in m4_for([count], [1], [100], [1], [count ]); do # Generate random UUID and check that it is in the expected format. uuid=`ovstest test-uuid` CHECK_UUID # Verify that $uuid does not duplicate any UUID generated so far. case $uuids in *$uuid*) echo "$uuid: generated duplicate UUID" exit 1 esac uuids="$uuids $uuid" # Verify that test-uuid parses and re-serializes this UUID correctly. serialized=`ovstest test-uuid $uuid` if test "$uuid" != "$serialized"; then echo "$uuid: test-uuid serialized this as $serialized" exit 1 fi done], [0]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/uuidfilt.py000077500000000000000000000044751514270232600223360ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2020 VMware, Inc. # # 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. # Breaks lines read from stdin into groups using blank lines as # group separators, then sorts lines within the groups for # reproducibility. import re import sys def lookup_uuid(uuids, match): return "<%s>" % uuids.setdefault(match.group(0), len(uuids)) int_re = re.compile(r'\d+') def sort_set(match): s = match.group(0) uuids = sorted([int(x) for x in int_re.findall(s)]) return '["set",[' + ','.join('["uuid","<%s>"]' % x for x in uuids) + ']]' u = '[0-9a-fA-F]' uuid_re = re.compile(r'%s{8}(?"\])+\]\])') def filter_uuids(src, dst): uuids = {} def lf(match): return lookup_uuid(uuids, match) while True: line = src.readline() if not line: break line = uuid_re.sub(lf, line) # Sort sets like this: # [["uuid","<1>"],["uuid","<0>"]] # to look like this: # [["uuid","<0>"],["uuid","<1>"]] line = set_re.sub(sort_set, line) dst.write(line) if __name__ == '__main__': if '--help' in sys.argv: print("""\ uuidfilt, for filtering UUIDs into numbered markers usage: %s [INPUT..] > OUTPUT or %s < INPUT > OUTPUT Reads each INPUT, locates UUIDs in the standard textual form, and converts them into numbered markers <0>, <1>, ..., , where <0> stands in for each instance of the first unique UUID found, <1> for each instance of the second, and so on. UUIDs that begin with ffffffff are not converted to markers. """) elif len(sys.argv) > 1: for src in sys.argv[1:]: filter_uuids(open(src), sys.stdout) else: filter_uuids(sys.stdin, sys.stdout) openvswitch-3.7.0~git20260211.8c6ebf8/tests/valgrind-wrapper.in000077500000000000000000000014671514270232600237510ustar00rootroot00000000000000#! /bin/sh wrap_program=`basename '@wrap_program@'` # Strip the first directory from $PATH that contains $wrap_program, # so that below we run the real $wrap_program, not ourselves. not_found=true new_path= first=true save_IFS=$IFS IFS=: for dir in $PATH; do IFS=$save_IFS if $not_found && test -x "$dir/$wrap_program"; then not_found=false else if $first; then first=false new_path=$dir else new_path=$new_path:$dir fi fi done IFS=$save_IFS if $not_found; then echo "$0: error: cannot find $wrap_program in \$PATH" >&2 exit 1 fi PATH=$new_path export PATH : ${VALGRIND:=valgrind -q --log-file=valgrind.%p --leak-check=full} exec $VALGRIND $wrap_program "$@" echo "$0: failed to execute $VALGRIND $wrap_program" "$@" >&2 exit 1 openvswitch-3.7.0~git20260211.8c6ebf8/tests/vconn.at000066400000000000000000000012531514270232600215740ustar00rootroot00000000000000m4_define([TEST_VCONN_CLASS], [AT_BANNER([vconn library -- $1 class]) m4_foreach( [testname], [[refuse-connection], [accept-then-close], [read-hello], [send-plain-hello], [send-long-hello], [send-echo-hello], [send-short-hello], [send-invalid-version-hello]], [AT_SETUP([$1 vconn - m4_bpatsubst(testname, [-], [ ])]) m4_if([$1], [ssl], [ AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) AT_CHECK([cp $abs_top_builddir/tests/testpki*.pem .])]) AT_CHECK([ovstest test-vconn testname $1], [0], [], [ignore]) AT_CLEANUP])]) TEST_VCONN_CLASS([unix]) TEST_VCONN_CLASS([tcp]) TEST_VCONN_CLASS([ssl]) openvswitch-3.7.0~git20260211.8c6ebf8/tests/vlog.at000066400000000000000000000352031514270232600214220ustar00rootroot00000000000000AT_BANNER([vlog]) AT_SETUP([vlog - Python3]) AT_CAPTURE_FILE([log_file]) AT_CAPTURE_FILE([stderr_log]) AT_CHECK([$PYTHON3 $srcdir/test-vlog.py --log-file log_file \ -v dbg module_1:info module_2:warn syslog:off 2>stderr_log]) AT_CHECK([sed -e 's/.*-.*-.*T..:..:..Z |//' \ -e 's/File ".*", line [[0-9]][[0-9]]*,/File , line ,/' \ -e '/\^/d' \ stderr_log], [0], [dnl 0 | module_0 | EMER | emergency 1 | module_0 | ERR | error 2 | module_0 | WARN | warning 3 | module_0 | INFO | information 4 | module_0 | DBG | debug 5 | module_0 | EMER | emergency exception Traceback (most recent call last): File , line , in main assert fail AssertionError 6 | module_0 | ERR | error exception Traceback (most recent call last): File , line , in main assert fail AssertionError 7 | module_0 | WARN | warn exception Traceback (most recent call last): File , line , in main assert fail AssertionError 8 | module_0 | INFO | information exception Traceback (most recent call last): File , line , in main assert fail AssertionError 9 | module_0 | DBG | debug exception Traceback (most recent call last): File , line , in main assert fail AssertionError 10 | module_0 | ERR | exception Traceback (most recent call last): File , line , in main assert fail AssertionError 11 | module_1 | EMER | emergency 12 | module_1 | ERR | error 13 | module_1 | WARN | warning 14 | module_1 | INFO | information 16 | module_1 | EMER | emergency exception Traceback (most recent call last): File , line , in main assert fail AssertionError 17 | module_1 | ERR | error exception Traceback (most recent call last): File , line , in main assert fail AssertionError 18 | module_1 | WARN | warn exception Traceback (most recent call last): File , line , in main assert fail AssertionError 19 | module_1 | INFO | information exception Traceback (most recent call last): File , line , in main assert fail AssertionError 21 | module_1 | ERR | exception Traceback (most recent call last): File , line , in main assert fail AssertionError 22 | module_2 | EMER | emergency 23 | module_2 | ERR | error 24 | module_2 | WARN | warning 27 | module_2 | EMER | emergency exception Traceback (most recent call last): File , line , in main assert fail AssertionError 28 | module_2 | ERR | error exception Traceback (most recent call last): File , line , in main assert fail AssertionError 29 | module_2 | WARN | warn exception Traceback (most recent call last): File , line , in main assert fail AssertionError 32 | module_2 | ERR | exception Traceback (most recent call last): File , line , in main assert fail AssertionError ]) AT_CLEANUP m4_divert_push([PREPARE_TESTS]) vlog_filt () { sed 's/.*\(opened log file\).*/\1/ s/.*|//' "$@" } m4_divert_pop([PREPARE_TESTS]) AT_SETUP([vlog - vlog/reopen - C]) # This test won't work as-is on Windows because Windows doesn't allow # files that are open to be renamed. AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill `cat test-unixctl.pid`' AT_CAPTURE_FILE([log]) AT_CAPTURE_FILE([log.old]) AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach --no-chdir], [0], [], [stderr]) AT_CHECK([vlog_filt stderr], [0], [opened log file ]) AT_CHECK([APPCTL -t test-unixctl log message]) mv log log.old AT_CHECK([APPCTL -t test-unixctl log message2]) AT_CHECK([APPCTL -t test-unixctl vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl log message3]) AT_CHECK([APPCTL -t test-unixctl exit]) AT_CHECK([vlog_filt log.old], [0], [dnl opened log file Entering run loop. message message2 closing log file ]) AT_CHECK([vlog_filt log], [0], [dnl opened log file message3 ]) AT_CLEANUP AT_SETUP([vlog - vlog/reopen - Python3]) # This test won't work as-is on Windows because Windows doesn't allow # files that are open to be renamed. AT_SKIP_IF([test "$IS_WIN32" = "yes"]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CAPTURE_FILE([log]) AT_CAPTURE_FILE([log.old]) AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py log message]) mv log log.old AT_CHECK([APPCTL -t test-unixctl.py log message2]) AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl.py log message3]) AT_CHECK([APPCTL -t test-unixctl.py exit]) AT_CHECK([sed 's/.*|//' log.old], [0], [dnl Entering run loop. message message2 ]) AT_CHECK([sed 's/.*|//' log], [0], [dnl message3 ]) AT_CLEANUP AT_SETUP([vlog - vlog/reopen without log file - C]) on_exit 'kill `cat test-unixctl.pid`' AT_CHECK([ovstest test-unixctl --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl vlog/reopen], [2], [], [Logging to file not configured ovs-appctl: test-unixctl: server returned an error ]) OVS_APP_EXIT_AND_WAIT([test-unixctl]) AT_CLEANUP AT_SETUP([vlog - vlog/reopen without log file - Python3]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen], [0], [Logging to file not configured ]) AT_CLEANUP dnl This checks that if vlog/reopen can't reopen the log file, dnl nothing particularly bad (e.g. a crash) happens. AT_SETUP([vlog - vlog/reopen can't reopen log file - C]) # Verify that /dev/full is a character device that fails writes. AT_SKIP_IF([test ! -c /dev/full]) AT_SKIP_IF([echo > /dev/full]) on_exit 'kill `cat test-unixctl.pid`' AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach --no-chdir], [0], [], [stderr]) AT_CHECK([vlog_filt stderr], [0], [opened log file ]) AT_CHECK([APPCTL -t test-unixctl log message]) mv log log.old ln -s /dev/full log AT_CHECK([APPCTL -t test-unixctl vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl log message2]) rm log AT_CHECK([APPCTL -t test-unixctl vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl log message3]) AT_CHECK([APPCTL -t test-unixctl exit]) AT_CHECK([vlog_filt log.old], [0], [dnl opened log file Entering run loop. message closing log file ]) AT_CHECK([vlog_filt log], [0], [dnl opened log file message3 ]) AT_CLEANUP dnl This checks that if vlog/reopen can't reopen the log file, dnl nothing particularly bad (e.g. Python throws an exception and dnl aborts the program) happens. AT_SETUP([vlog - vlog/reopen can't reopen log file - Python3]) # Verify that /dev/full is a character device that fails writes. AT_SKIP_IF([test ! -c /dev/full]) AT_SKIP_IF([echo > /dev/full]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py log message]) mv log log.old ln -s /dev/full log AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl.py log message2]) rm log AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl.py log message3]) AT_CHECK([APPCTL -t test-unixctl.py exit]) AT_CHECK([sed 's/.*|//' log.old], [0], [dnl Entering run loop. message ]) AT_CHECK([sed 's/.*|//' log], [0], [dnl message3 ]) AT_CLEANUP AT_SETUP([vlog - vlog/close - C]) on_exit 'kill `cat test-unixctl.pid`' AT_CAPTURE_FILE([log]) AT_CAPTURE_FILE([log.old]) AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach --no-chdir], [0], [], [ignore]) AT_CHECK([vlog_filt log], [0], [opened log file Entering run loop. ]) AT_CHECK([APPCTL -t test-unixctl log message]) AT_CHECK([APPCTL -t test-unixctl log message2]) # After closing the log file, message3 won't appear anywhere. AT_CHECK([APPCTL -t test-unixctl vlog/close]) mv log log.old AT_CHECK([APPCTL -t test-unixctl log message3]) # Closing the log file again is harmless. AT_CHECK([APPCTL -t test-unixctl vlog/close]) AT_CHECK([APPCTL -t test-unixctl log message4]) # After reopening the log file, further messages start appearing again. AT_CHECK([APPCTL -t test-unixctl vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl log message5]) AT_CHECK([APPCTL -t test-unixctl exit]) AT_CHECK([vlog_filt log.old], [0], [dnl opened log file Entering run loop. message message2 ]) AT_CHECK([vlog_filt log], [0], [dnl opened log file message5 ]) AT_CLEANUP AT_SETUP([vlog - vlog/close - Python3]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CAPTURE_FILE([log]) AT_CAPTURE_FILE([log.old]) AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py log message]) AT_CHECK([APPCTL -t test-unixctl.py log message2]) # After closing the log file, message3 won't appear anywhere. AT_CHECK([APPCTL -t test-unixctl.py vlog/close]) mv log log.old AT_CHECK([APPCTL -t test-unixctl.py log message3]) # Closing the log file again is harmless. AT_CHECK([APPCTL -t test-unixctl.py vlog/close]) AT_CHECK([APPCTL -t test-unixctl.py log message4]) # After reopening the log file, further messages start appearing again. AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen]) AT_CHECK([APPCTL -t test-unixctl.py log message5]) AT_CHECK([APPCTL -t test-unixctl.py exit]) AT_CHECK([sed 's/.*|//' log.old], [0], [dnl Entering run loop. message message2 ]) AT_CHECK([sed 's/.*|//' log], [0], [dnl message5 ]) AT_CLEANUP AT_SETUP([vlog - vlog/set and vlog/list - C]) on_exit 'kill `cat test-unixctl.pid`' AT_CAPTURE_FILE([log]) AT_CHECK([ovstest test-unixctl --log-file=`pwd`/log --pidfile --detach --no-chdir], [0], [], [ignore]) AT_CHECK([vlog_filt log], [0], [opened log file Entering run loop. ]) AT_CHECK([APPCTL -t test-unixctl vlog/list | sed -n '1,2p /test_unixctl /p; /daemon /p'], [0], [dnl console syslog file ------- ------ ------ daemon OFF INFO INFO test_unixctl OFF INFO INFO ]) AT_CHECK([APPCTL -t test-unixctl vlog/set daemon:syslog:err]) AT_CHECK([APPCTL -t test-unixctl vlog/set file:dbg]) AT_CHECK([APPCTL -t test-unixctl vlog/set nonexistent], [2], [], [no destination, level, or module "nonexistent" ovs-appctl: test-unixctl: server returned an error ]) AT_CHECK([APPCTL -t test-unixctl vlog/list | sed -n '1,2p /test_unixctl /p; /daemon /p'], [0], [dnl console syslog file ------- ------ ------ daemon OFF ERR DBG test_unixctl OFF INFO DBG ]) AT_CHECK([APPCTL -t test-unixctl vlog/set pattern], [2], [], [missing destination ovs-appctl: test-unixctl: server returned an error ]) AT_CHECK([APPCTL -t test-unixctl vlog/set pattern:nonexistent], [2], [], [unknown destination "nonexistent" ovs-appctl: test-unixctl: server returned an error ]) AT_CHECK([APPCTL -t test-unixctl vlog/set pattern:file:'I<3OVS|%m']) AT_CHECK([APPCTL -t test-unixctl log patterntest]) AT_CHECK([grep -q 'I<3OVS' log]) OVS_APP_EXIT_AND_WAIT([test-unixctl]) AT_CLEANUP AT_SETUP([vlog - vlog/set and vlog/list - Python3]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CAPTURE_FILE([log]) AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach --no-chdir]) AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl console syslog file ------- ------ ------ daemon info info info dns_resolve info info info fatal-signal info info info jsonrpc info info info poller info info info reconnect info info info socket_util info info info stream info info info test-unixctl info info info unixctl_server info info info ]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set daemon:syslog:err]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set file:dbg]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set nonexistent], [0], [no destination, level, or module "nonexistent" ]) AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl console syslog file ------- ------ ------ daemon info err dbg dns_resolve info info dbg fatal-signal info info dbg jsonrpc info info dbg poller info info dbg reconnect info info dbg socket_util info info dbg stream info info dbg test-unixctl info info dbg unixctl_server info info dbg ]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set pattern], [0], [Please supply a valid pattern and destination ]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set pattern:nonexistent], [0], [Destination nonexistent does not exist ]) AT_CHECK([APPCTL -t test-unixctl.py vlog/set pattern:file:'I<3OVS|%m']) AT_CHECK([APPCTL -t test-unixctl.py log patterntest]) AT_CHECK([grep -q 'I<3OVS' log]) AT_CLEANUP AT_SETUP([vlog - RFC5424 facility]) on_exit 'kill `cat ovsdb-server.pid`' dnl Create database. touch .conf.db.~lock~ AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \ --remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \ --log-file], [0], [], [stderr]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) # A default facility of LOG_LOCAL0 while writing to file. AT_CHECK([head -1 ovsdb-server.log | awk '{print $1}'], [0], [<133>1 ]) rm ovsdb-server.log AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \ --remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \ -vFACILITY:daemon --log-file], [0], [], [stderr]) AT_CHECK([head -1 ovsdb-server.log | awk '{print $1}'], [0], [<29>1 ]) AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:invalid], [2], [], [invalid facility ovs-appctl: ovsdb-server: server returned an error ]) AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:local7]) AT_CHECK([ovs-appctl -t ovsdb-server vlog/set ANY:file:DBG]) OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CHECK([tail -1 ovsdb-server.log | awk '{print $1}'], [0], [<191>1 ]) AT_CLEANUP AT_SETUP([vlog - RFC5424 facility - Python3]) on_exit 'kill `cat test-unixctl.py.pid`' AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \ -vFACILITY:invalid --detach --no-chdir], [1], [], [test-unixctl.py: processing "FACILITY:invalid": Facility invalid is invalid ]) AT_CHECK([$PYTHON3 $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \ -vFACILITY:daemon --detach --no-chdir]) AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:invalid], [0], [Facility invalid is invalid ]) AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:local0]) AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/tests/vtep-ctl.at000066400000000000000000000714431514270232600222170ustar00rootroot00000000000000dnl VTEP_OVSDB_INIT([$1]) dnl dnl Creates an empty database named $1. m4_define([VTEP_OVSDB_INIT], [AT_CHECK( [ovsdb-tool create $1 $abs_top_srcdir/vtep/vtep.ovsschema], [0], [stdout], [ignore]) AT_CHECK( [[ovsdb-tool transact $1 \ '["hardware_vtep", {"op": "insert", "table": "Global", "row": {}}]']], [0], [ignore], [ignore])]) dnl VTEP_CTL_SETUP dnl dnl Creates an empty database in the current directory and then starts dnl an ovsdb-server on it for vtep-ctl to connect to. m4_define([VTEP_CTL_SETUP], [VTEP_OVSDB_INIT([db]) AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile --remote=punix:socket db], [0], [ignore], [ignore])]) dnl VTEP_CTL_CLEANUP dnl dnl Kills off the database server. m4_define([VTEP_CTL_CLEANUP], [OVSDB_SERVER_SHUTDOWN]) dnl RUN_VTEP_CTL(COMMAND, ...) dnl dnl Executes each vtep-ctl COMMAND. m4_define([RUN_VTEP_CTL], [m4_foreach([command], [$@], [vtep-ctl -vreconnect:emer --db=unix:socket command ])]) m4_define([RUN_VTEP_CTL_ONELINE], [m4_foreach([command], [$@], [vtep-ctl -vreconnect:emer --db=unix:socket --oneline -- command ])]) dnl RUN_VTEP_CTL_TOGETHER(COMMAND, ...) dnl dnl Executes each vtep-ctl COMMAND in a single run of vtep-ctl. m4_define([RUN_VTEP_CTL_TOGETHER], [vtep-ctl -vreconnect:emer --db=unix:socket --oneline dnl m4_foreach([command], [$@], [ -- command])]) dnl CHECK_PSWITCHES([PSWITCH], ...) dnl dnl Verifies that "vtep-ctl list-ps" prints the specified list of dnl physical switches, which must be in alphabetical order. m4_define([CHECK_PSWITCHES], [dnl Check that the pswitches appear on list-ps, without --oneline. AT_CHECK( [RUN_VTEP_CTL([list-ps])], [0], [m4_foreach([psinfo], [$@], [m4_car(psinfo) ])], [], [VTEP_CTL_CLEANUP]) dnl Check that the pswitches appear on list-ps, with --oneline. AT_CHECK( [RUN_VTEP_CTL_ONELINE([list-ps])], [0], [m4_join([\n], m4_foreach([psinfo], [$@], [m4_car(psinfo),])) ], [], [VTEP_CTL_CLEANUP]) dnl Check that each pswitch exists according to ps-exists and that dnl a pswitch that should not exist does not. m4_foreach([psinfo], [$@], [AT_CHECK([RUN_VTEP_CTL([ps-exists m4_car(psinfo)])], [0], [], [], [VTEP_CTL_CLEANUP])]) AT_CHECK([RUN_VTEP_CTL([ps-exists nonexistent])], [2], [], [], [VTEP_CTL_CLEANUP])]) dnl CHECK_PORTS(PSWITCH, PORT[, PORT...]) dnl dnl Verifies that "vtep-ctl list-ports PSWITCH" prints the specified dnl list of ports, which must be in alphabetical order. m4_define([CHECK_PORTS], [dnl Check ports without --oneline. AT_CHECK( [RUN_VTEP_CTL([list-ports $1])], [0], [m4_foreach([port], m4_cdr($@), [port ])], [], [VTEP_CTL_CLEANUP]) dnl Check ports with --oneline. AT_CHECK( [RUN_VTEP_CTL_ONELINE([list-ports $1])], [0], [m4_join([\n], m4_shift($@)) ], [], [VTEP_CTL_CLEANUP])]) dnl CHECK_LSWITCHES([LSWITCH], ...) dnl dnl Verifies that "vtep-ctl list-ls" prints the specified list of dnl logical switches, which must be in alphabetical order. m4_define([CHECK_LSWITCHES], [dnl Check that the lswitches appear on list-ls, without --oneline. AT_CHECK( [RUN_VTEP_CTL([list-ls])], [0], [m4_foreach([lsinfo], [$@], [m4_car(lsinfo) ])], [], [VTEP_CTL_CLEANUP]) dnl Check that the lswitches appear on list-ls, with --oneline. AT_CHECK( [RUN_VTEP_CTL_ONELINE([list-ls])], [0], [m4_join([\n], m4_foreach([lsinfo], [$@], [m4_car(lsinfo),])) ], [], [VTEP_CTL_CLEANUP]) dnl Check that each lswitch exists according to ls-exists and that dnl a pswitch that should not exist does not. m4_foreach([lsinfo], [$@], [AT_CHECK([RUN_VTEP_CTL([ls-exists m4_car(lsinfo)])], [0], [], [], [VTEP_CTL_CLEANUP])]) AT_CHECK([RUN_VTEP_CTL([ls-exists nonexistent])], [2], [], [], [VTEP_CTL_CLEANUP])]) dnl CHECK_LROUTERS([LROUTER], ...) dnl dnl Verifies that "vtep-ctl list-lr" prints the specified list of dnl logical routers, which must be in alphabetical order. m4_define([CHECK_LROUTERS], [dnl Check that the lrouters appear on list-lr, without --oneline. AT_CHECK( [RUN_VTEP_CTL([list-lr])], [0], [m4_foreach([lrinfo], [$@], [m4_car(lrinfo) ])], [], [VTEP_CTL_CLEANUP]) dnl Check that the lswitches appear on list-lr, with --oneline. AT_CHECK( [RUN_VTEP_CTL_ONELINE([list-lr])], [0], [m4_join([\n], m4_foreach([lrinfo], [$@], [m4_car(lrinfo),])) ], [], [VTEP_CTL_CLEANUP]) dnl Check that each lrouter exists according to lr-exists and that dnl a prouter that should not exist does not. m4_foreach([lrinfo], [$@], [AT_CHECK([RUN_VTEP_CTL([lr-exists m4_car(lrinfo)])], [0], [], [], [VTEP_CTL_CLEANUP])]) AT_CHECK([RUN_VTEP_CTL([lr-exists nonexistent])], [2], [], [], [VTEP_CTL_CLEANUP])]) dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- physical switch tests]) AT_SETUP([add-ps a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ps a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, add-ps a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ps a])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([add-ps a])], [1], [], [vtep-ctl: cannot create physical switch a because it already exists ], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, add-ps b]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ps a], [add-ps b])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a], [b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, add-ps b, del-ps a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ps a], [add-ps b], [del-ps a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, del-ps a, add-ps a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL_TOGETHER( [add-ps a], [del-ps a], [add-ps a])], [0], [ ], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, add-port a a1, add-port a a2]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps a], [--if-exists del-ps b], [add-port a a1], [add-port a a2])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a]) CHECK_PORTS([a], [a1], [a2]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a, add-port a a1, add-port a a1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps a], [add-port a a1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([add-port a a1])], [1], [], [vtep-ctl: cannot create a port named a1 on a because a port with that name already exists ], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a b, add-port a a1, add-port b b1, del-ps a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL_TOGETHER( [add-ps a], [add-ps b], [add-port a a1], [add-port b b1], [--if-exists del-port b b2], [del-ps a])], [0], [ ], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([b]) CHECK_PORTS([b], [b1]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a b, add-port a a1, add-port b b1, del-port a a1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps a], [add-ps b], [add-port a a1], [--may-exist add-port b b1], [del-port a a1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([--may-exist add-port b b1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a], [b]) CHECK_PORTS([a]) CHECK_PORTS([b], [b1]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ps a b, add-port a p1, add-port b p1, del-port a p1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps a], [add-ps b], [add-port a p1], [add-port b p1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a], [b]) CHECK_PORTS([a], [p1]) CHECK_PORTS([b], [p1]) AT_CHECK([RUN_VTEP_CTL([del-port a p1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([a], [b]) CHECK_PORTS([a]) CHECK_PORTS([b], [p1]) VTEP_CTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- logical switch tests]) AT_SETUP([add-ls a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ls a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([a]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, add-ls a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ls a])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([add-ls a])], [1], [], [vtep-ctl: cannot create logical switch a because it already exists ], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, add-ls b]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ls a], [add-ls b])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([a], [b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, add-ls b, del-ls a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-ls a], [add-ls b], [del-ls a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, del-ls a, add-ls a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL_TOGETHER( [add-ls a], [del-ls a], [add-ls a])], [0], [ ], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([a]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, get-replication-mode a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls a], [get-replication-mode a])], [0], [[(null)] ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, set-replication-mode a source_node]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls a],[set-replication-mode a source_node], [get-replication-mode a])], [0], [source_node ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ls a, set-replication-mode a service_node]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls a],[set-replication-mode a service_node], [get-replication-mode a])], [0], [service_node ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- logical router tests]) AT_SETUP([add-lr a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-lr a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LROUTERS([a]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-lr a, add-lr a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-lr a])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([add-lr a])], [1], [], [vtep-ctl: cannot create logical router a because it already exists ], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-lr a, add-lr b]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-lr a], [add-lr b])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LROUTERS([a], [b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-lr a, add-lr b, del-lr a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL([add-lr a], [add-lr b], [del-lr a])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LROUTERS([b]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-lr a, del-lr a, add-lr a]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL_TOGETHER( [add-lr a], [del-lr a], [add-lr a])], [0], [ ], [], [VTEP_CTL_CLEANUP]) CHECK_LROUTERS([a]) VTEP_CTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- logical binding tests]) AT_SETUP([bind-ls ps1 pp1 300 ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps ps1], [add-port ps1 pp1], [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([ps1]) CHECK_PORTS([ps1], [pp1]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [bind-ls ps1 pp1 300 ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-bindings ps1 pp1])], [0], [0300 ls1 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([bind-ls ps1 pp1 300 ls1, bind-ls ps1 pp1 400 ls2]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps ps1], [add-port ps1 pp1], [add-ls ls1], [add-ls ls2])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([ps1]) CHECK_PORTS([ps1], [pp1]) CHECK_LSWITCHES([ls1], [ls2]) AT_CHECK([RUN_VTEP_CTL( [bind-ls ps1 pp1 300 ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [bind-ls ps1 pp1 400 ls2])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-bindings ps1 pp1])], [0], [0300 ls1 0400 ls2 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([bind-ls ps1 pp1 300, bind-ls ps2 pp2 300 ls2]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ps ps1], [add-ps ps2], [add-port ps1 pp1], [add-port ps2 pp2], [add-ls ls1], [add-ls ls2])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_PSWITCHES([ps1], [ps2]) CHECK_PORTS([ps1], [pp1]) CHECK_PORTS([ps2], [pp2]) CHECK_LSWITCHES([ls1], [ls2]) AT_CHECK([RUN_VTEP_CTL( [bind-ls ps1 pp1 300 ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [bind-ls ps2 pp2 300 ls2])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-bindings ps1 pp1])], [0], [0300 ls1 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-bindings ps2 pp2])], [0], [0300 ls2 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- MAC binding tests]) AT_SETUP([add-ucast-local ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-local ls1 00:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-local ls1, overwrite]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-local ls1, del-ucast-local ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-local ls1 00:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [del-ucast-local ls1 00:11:22:33:44:55]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-remote ls1 00:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-remote ls1, overwrite]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-remote ls1, del-ucast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-remote ls1 00:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [del-ucast-remote ls1 00:11:22:33:44:55]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-ucast-local ls1, add-ucast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-ucast-local ls1 00:11:22:33:44:66 10.0.0.11], [add-ucast-remote ls1 02:11:22:33:44:55 10.0.0.10], [add-ucast-remote ls1 02:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 00:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 02:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 02:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-mcast-local ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-mcast-local ls1, del-mcast-local ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.12], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.13]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.13 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [del-mcast-local ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.13 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-mcast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-mcast-remote ls1, del-mcast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.12], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.13]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.13 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [del-mcast-remote ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.13 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add-mcast-local ls1, add-mcast-remote ls1]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:66 10.0.0.11], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.12], [add-mcast-remote ls1 03:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 03:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-remote ls1 03:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote 03:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 03:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 03:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add local and remote macs, clear-local-macs]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.12], [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [clear-local-macs ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local mcast-mac-local ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([add local and remote macs, clear-remote-macs]) AT_KEYWORDS([vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [add-ls ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) CHECK_LSWITCHES([ls1]) AT_CHECK([RUN_VTEP_CTL( [add-ucast-local ls1 00:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-local ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-local ls1 01:11:22:33:44:55 10.0.0.12], [add-ucast-remote ls1 00:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.10], [add-mcast-remote ls1 01:11:22:33:44:66 vxlan_over_ipv4 10.0.0.11], [add-mcast-remote ls1 01:11:22:33:44:55 10.0.0.12]) ], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-remote 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL( [clear-remote-macs ls1])], [0], [], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-local-macs ls1])], [0], [ucast-mac-local 00:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 mcast-mac-local 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.10 01:11:22:33:44:55 -> vxlan_over_ipv4/10.0.0.12 01:11:22:33:44:66 -> vxlan_over_ipv4/10.0.0.11 ], [], [VTEP_CTL_CLEANUP]) AT_CHECK([RUN_VTEP_CTL([list-remote-macs ls1])], [0], [ucast-mac-remote mcast-mac-remote ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP dnl ---------------------------------------------------------------------- AT_BANNER([vtep-ctl unit tests -- manager commands]) AT_SETUP([managers]) AT_KEYWORDS([manager vtep-ctl]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL_TOGETHER( [del-manager], [get-manager], [set-manager tcp:4.5.6.7], [get-manager], [set-manager tcp:8.9.10.11 tcp:5.4.3.2], [get-manager], [--inactivity-probe=30000 set-manager tcp:1.2.3.4], [get-manager], [del-manager], [get-manager])], [0], [ tcp:4.5.6.7 tcp:5.4.3.2\ntcp:8.9.10.11 tcp:1.2.3.4 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP AT_SETUP([show command]) AT_KEYWORDS([vtep-ctl show]) VTEP_CTL_SETUP AT_CHECK([RUN_VTEP_CTL( [set-manager tcp:4.5.6.7], [add-ps a], [add-port a a1], [add-ls ls1], [bind-ls a a1 100 ls1], [set Physical_Switch a management_ips=[[4.3.2.1]] tunnel_ips=[[1.2.3.4]]])], [0], [ignore], [], [VTEP_CTL_CLEANUP]) AT_CHECK([vtep-ctl -vreconnect:emer --db=unix:socket show | tail -n+2 | sed 's/=[[a-f0-9-]][[a-f0-9-]]*}/=}/' ], [0], [dnl Manager "tcp:4.5.6.7" Physical_Switch a management_ips: [["4.3.2.1"]] tunnel_ips: [["1.2.3.4"]] Physical_Port a1 vlan_bindings: 100=ls1 ], [], [VTEP_CTL_CLEANUP]) VTEP_CTL_CLEANUP AT_CLEANUP openvswitch-3.7.0~git20260211.8c6ebf8/third-party/000077500000000000000000000000001514270232600212275ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/third-party/.gitignore000066400000000000000000000000271514270232600232160ustar00rootroot00000000000000/Makefile /Makefile.in openvswitch-3.7.0~git20260211.8c6ebf8/third-party/README.rst000066400000000000000000000043041514270232600227170ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ================================ Third-party software integration ================================ This directory contains third-party software that may be useful for debugging. tcpdump ------- The ``ofp-tcpdump.patch`` patch adds the ability to parse OpenFlow messages to tcpdump. These instructions assume that tcpdump 4.3.0 is going to be used, but it should work with other versions that are not substantially different. To begin, download tcpdump and apply the patch: :: $ wget http://www.tcpdump.org/release/tcpdump-4.3.0.tar.gz $ tar xzf tcpdump-4.3.0.tar.gz $ ln -s tcpdump-4.3.0 tcpdump $ patch -p0 < ofp-tcpdump.patch Then build the new version of tcpdump: :: $ cd tcpdump $ ./configure $ make Clearly, tcpdump can only parse unencrypted packets, so you will need to connect the controller and datapath using plain TCP. To look at the traffic, tcpdump will be started in a manner similar to the following: :: $ sudo ./tcpdump -s0 -i eth0 port 6653 The ``-s0`` flag indicates that tcpdump should capture the entire packet. If the OpenFlow message is not received in its entirety, ``[|openflow]`` will be printed instead of the OpenFlow message contents. The verbosity of the output may be increased by adding additional ``-v`` flags. If ``-vvv`` is used, the raw OpenFlow data is also printed in hex and ASCII. openvswitch-3.7.0~git20260211.8c6ebf8/third-party/automake.mk000066400000000000000000000001111514270232600233570ustar00rootroot00000000000000EXTRA_DIST += \ third-party/ofp-tcpdump.patch \ third-party/README.rst openvswitch-3.7.0~git20260211.8c6ebf8/third-party/ofp-tcpdump.patch000066400000000000000000000103641514270232600245120ustar00rootroot00000000000000--- tcpdump/interface.h 2007-06-13 18:03:20.000000000 -0700 +++ tcpdump/interface.h 2008-04-15 18:28:55.000000000 -0700 @@ -130,7 +130,8 @@ extern const char *dnaddr_string(u_short); -extern void error(const char *, ...) +#define error(fmt, args...) tcpdump_error(fmt, ## args) +extern void tcpdump_error(const char *, ...) __attribute__((noreturn, format (printf, 1, 2))); extern void warning(const char *, ...) __attribute__ ((format (printf, 1, 2))); @@ -163,6 +164,7 @@ extern void hex_print_with_offset(const char *, const u_char *, u_int, u_int); extern void hex_print(const char *, const u_char *, u_int); extern void telnet_print(const u_char *, u_int); +extern void openflow_print(const u_char *, u_int); extern int llc_print(const u_char *, u_int, u_int, const u_char *, const u_char *, u_short *); extern int snap_print(const u_char *, u_int, u_int, u_int); --- tcpdump/Makefile.in 2012-06-13 04:56:20.000000000 +1200 +++ tcpdump/Makefile.in 2012-08-29 21:36:37.000000000 +1200 @@ -43,7 +43,7 @@ CC = @CC@ PROG = tcpdump CCOPT = @V_CCOPT@ -INCLS = -I. @V_INCLS@ +INCLS = -I. @V_INCLS@ -I../../include DEFS = @DEFS@ @CPPFLAGS@ @V_DEFS@ # Standard CFLAGS @@ -51,10 +51,10 @@ FULL_CFLAGS = $(CCOPT) $(DEFS) $(INCLS) $(CFLAGS) # Standard LDFLAGS -LDFLAGS = @LDFLAGS@ +LDFLAGS = @LDFLAGS@ -L../../lib # Standard LIBS -LIBS = @LIBS@ +LIBS = @LIBS@ -lopenvswitch -lssl -lrt -lm INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -93,7 +93,8 @@ print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \ print-timed.c print-tipc.c print-token.c print-udld.c print-udp.c \ print-usb.c print-vjc.c print-vqp.c print-vrrp.c print-vtp.c \ - print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c + print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c \ + print-openflow.c LIBNETDISSECT_SRC=print-isakmp.c LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o) @@ -363,7 +364,7 @@ all: $(PROG) $(PROG): $(OBJ) @rm -f $@ - $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + libtool --mode=link $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(LIBNETDISSECT): $(LIBNETDISSECT_OBJ) @rm -f $@ --- tcpdump/print-openflow.c 1969-12-31 16:00:00.000000000 -0800 +++ tcpdump/print-openflow.c 2009-05-11 15:38:41.000000000 -0700 @@ -0,0 +1,45 @@ +/* Copyright (C) 2007, 2008, 2009 Nicira, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "interface.h" +#include "openflow/openflow.h" +#include "openvswitch/ofp-print.h" + +void +openflow_print(const u_char *sp, u_int length) +{ + const struct ofp_header *ofp = (struct ofp_header *)sp; + + if (!TTEST2(*sp, ntohs(ofp->length))) + goto trunc; + + ofp_print(stdout, sp, length, vflag); + return; + +trunc: + printf("[|openflow]"); +} --- tcpdump/print-tcp.c 2006-09-19 12:07:57.000000000 -0700 +++ tcpdump/print-tcp.c 2009-05-11 15:38:25.000000000 -0700 @@ -56,6 +56,8 @@ #include "nameser.h" +#include "openflow/openflow.h" + #ifdef HAVE_LIBCRYPTO #include #include @@ -669,7 +672,9 @@ } else if (length > 0 && (sport == LDP_PORT || dport == LDP_PORT)) { ldp_print(bp, length); - } + } else if (sport == OFP_PORT || dport == OFP_PORT) { + openflow_print(bp, length); + } return; bad: openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/000077500000000000000000000000001514270232600206235ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/.gitignore000066400000000000000000000000111514270232600226030ustar00rootroot00000000000000sandbox/ openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/automake.mk000066400000000000000000000004511514270232600227620ustar00rootroot00000000000000EXTRA_DIST += \ tutorial/ovs-sandbox \ tutorial/t-setup \ tutorial/t-stage0 \ tutorial/t-stage1 \ tutorial/t-stage2 \ tutorial/t-stage3 \ tutorial/t-stage4 sandbox: all cd $(srcdir)/tutorial && MAKE=$(MAKE) HAVE_OPENSSL=$(HAVE_OPENSSL) \ ./ovs-sandbox -b $(abs_builddir) $(SANDBOXFLAGS) openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/ovs-sandbox000077500000000000000000000206301514270232600230150ustar00rootroot00000000000000#! /bin/sh # # Copyright (c) 2013, 2015, 2016 Nicira, Inc. # # 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. set -e run() { echo "$@" (cd "$sandbox" && "$@") || exit 1 } run_xterm() { title=$1; shift run xterm -T "$title" -e "$@" & } rungdb() { under_gdb=$1 gdb_run=$2 shift shift # Remove the --detach and to put the process under gdb control. # Also remove --vconsole:off to allow error message to show up # on the console. # Use "DISPLAY" variable to determine out if X is supported if $under_gdb && [ "$DISPLAY" ]; then args=`echo $@ |sed s/--detach//g | sed s/--vconsole:off//g` xterm_title=$1 gdb_cmd="" if $gdb_run; then gdb_cmd="-ex run" fi run_xterm $xterm_title gdb $gdb_cmd --args $args else run $@ fi } gdb_vswitchd=false gdb_ovsdb=false gdb_vswitchd_ex=false gdb_ovsdb_ex=false builddir= srcdir= schema= installed=false built=false dummy=override for option; do # This option-parsing mechanism borrowed from a Autoconf-generated # configure script under the following license: # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2009, 2013 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # If the previous option needs an argument, assign it. if test -n "$prev"; then eval $prev=\$option prev= continue fi case $option in *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;; *) optarg=yes ;; esac case $dashdash$option in --) dashdash=yes ;; -h|--help) cat <&2 exit 1 ;; *) echo "$option: non-option arguments not supported (use --help for help)" >&2 exit 1 ;; esac shift done if $installed && $built; then echo "sorry, conflicting options (use --help for help)" >&2 exit 1 elif $installed || $built; then : elif test -e vswitchd/ovs-vswitchd; then built=: builddir=. elif (ovs-vswitchd --version) >/dev/null 2>&1; then installed=: else echo "can't find an OVS build or install (use --help for help)" >&2 exit 1 fi if $built; then if test ! -e "$builddir"/vswitchd/ovs-vswitchd; then echo "$builddir does not appear to be an OVS build directory" >&2 exit 1 fi builddir=`cd $builddir && pwd` # Find srcdir. case $srcdir in '') srcdir=$builddir if test ! -e "$srcdir"/README.rst; then srcdir=`cd $builddir/.. && pwd` fi ;; /*) ;; *) srcdir=`pwd`/$srcdir ;; esac schema=$srcdir/vswitchd/vswitch.ovsschema if test ! -e "$schema"; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi # Put built tools early in $PATH. if test ! -e $builddir/vswitchd/ovs-vswitchd; then echo >&2 'build not found, please change set $builddir or change directory' exit 1 fi PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$builddir/vtep:$PATH export PATH else case $schema in '') for schema in \ /usr/local/share/openvswitch/vswitch.ovsschema \ /usr/share/openvswitch/vswitch.ovsschema \ none; do if test -r $schema; then break fi done ;; /*) ;; *) schema=`pwd`/$schema ;; esac if test ! -r "$schema"; then echo "can't find vswitch.ovsschema, please specify --schema" >&2 exit 1 fi fi # Create sandbox. rm -rf sandbox mkdir sandbox sandbox=`cd sandbox && pwd` # Set up environment for OVS programs to sandbox themselves. OVS_RUNDIR=$sandbox; export OVS_RUNDIR OVS_LOGDIR=$sandbox; export OVS_LOGDIR OVS_DBDIR=$sandbox; export OVS_DBDIR OVS_SYSCONFDIR=$sandbox; export OVS_SYSCONFDIR if $built; then # Easy access to OVS manpages. (cd "$builddir" && ${MAKE-make} install-man install-man-rst mandir="$sandbox"/man) MANPATH=$sandbox/man:; export MANPATH fi # Ensure cleanup. trap 'kill `cat "$sandbox"/*.pid`' 0 1 2 3 13 14 15 # Create database and start ovsdb-server. touch "$sandbox"/.conf.db.~lock~ run ovsdb-tool create conf.db "$schema" ovsdb_server_args= rungdb $gdb_ovsdb $gdb_ovsdb_ex ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file -vsyslog:off \ --remote=punix:"$sandbox"/db.sock \ --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ $ovsdb_server_args #Add a small delay to allow ovsdb-server to launch. sleep 0.1 #Wait for ovsdb-server to finish launching. if test ! -e "$sandbox"/db.sock; then printf "Waiting for ovsdb-server to start..." while test ! -e "$sandbox"/db.sock; do sleep 1; done echo " Done" fi # Initialize database. run ovs-vsctl --no-wait -- init # Start ovs-vswitchd. rungdb $gdb_vswitchd $gdb_vswitchd_ex ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file -vsyslog:off \ --enable-dummy=$dummy -vvconn -vnetdev_dummy cat <NXM_NX_REG0[0..15]), \ resubmit(,3)" openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/t-stage3000077500000000000000000000003431514270232600222000ustar00rootroot00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 \ "table=3 priority=50 actions=resubmit(,10), resubmit(,4)" ovs-ofctl add-flow br0 \ "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 \ actions=resubmit(,4)" openvswitch-3.7.0~git20260211.8c6ebf8/tutorial/t-stage4000077500000000000000000000007001514270232600221760ustar00rootroot00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1" ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=2 actions=strip_vlan,2 table=4 reg0=3 actions=strip_vlan,3 table=4 reg0=4 actions=strip_vlan,4 EOF ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2 table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4 table=4 reg0=0 priority=50 actions=1 EOF openvswitch-3.7.0~git20260211.8c6ebf8/utilities/000077500000000000000000000000001514270232600207735ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/.gitignore000066400000000000000000000007731514270232600227720ustar00rootroot00000000000000/Makefile /Makefile.in /nlmon /ovs-appctl /ovs-appctl.8 /ovs-cfg-mod /ovs-cfg-mod.8 /ovs-check-dead-ifs /ovs-testcontroller /ovs-testcontroller.8 /ovs-ctl /ovs-dpctl /ovs-dpctl.8 /ovs-dpctl-top /ovs-dpctl-top.8 /ovs-kmod-ctl /ovs-l3ping /ovs-l3ping.8 /ovs-lib /ovs-ofctl /ovs-ofctl.8 /ovs-parse-backtrace /ovs-pcap /ovs-pcap.1 /ovs-pki /ovs-pki-cgi /ovs-pki.8 /ovs-test /ovs-test.8 /ovs-tcpdump /ovs-tcpdump.8 /ovs-tcpundump /ovs-tcpundump.1 /ovs-vlan-test /ovs-vlan-test.8 /ovs-vsctl /ovs-vsctl.8 /ovs-sim openvswitch-3.7.0~git20260211.8c6ebf8/utilities/automake.mk000066400000000000000000000111041514270232600231270ustar00rootroot00000000000000bin_PROGRAMS += \ utilities/ovs-appctl \ utilities/ovs-testcontroller \ utilities/ovs-dpctl \ utilities/ovs-ofctl \ utilities/ovs-vsctl bin_SCRIPTS += utilities/ovs-docker \ utilities/ovs-pki \ utilities/ovs-pcap \ utilities/ovs-tcpdump \ utilities/ovs-tcpundump \ utilities/ovs-dpctl-top \ utilities/ovs-l3ping \ utilities/ovs-parse-backtrace \ utilities/ovs-test \ utilities/ovs-vlan-test scripts_SCRIPTS += \ utilities/ovs-check-dead-ifs \ utilities/ovs-ctl \ utilities/ovs-kmod-ctl \ utilities/ovs-save scripts_DATA += utilities/ovs-lib usdt_SCRIPTS += \ utilities/usdt-scripts/bridge_loop.bt \ utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/kernel_delay.py \ utilities/usdt-scripts/kernel_delay.rst \ utilities/usdt-scripts/reval_monitor.py \ utilities/usdt-scripts/upcall_cost.py \ utilities/usdt-scripts/upcall_monitor.py \ utilities/usdt-scripts/usdt_lib.py completion_SCRIPTS += \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash check_SCRIPTS += \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash EXTRA_DIST += utilities/ovs-sim.in noinst_SCRIPTS += utilities/ovs-sim utilities/ovs-lib: $(top_builddir)/config.status EXTRA_DIST += \ utilities/gdb/ovs_gdb.py \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-ctl.in \ utilities/ovs-dev.py \ utilities/ovs-docker \ utilities/ovs-dpctl-top.in \ utilities/ovs-kmod-ctl.in \ utilities/ovs-l3ping.in \ utilities/ovs-lib.in \ utilities/ovs-parse-backtrace.in \ utilities/ovs-pcap.in \ utilities/ovs-pipegen.py \ utilities/ovs-pki.in \ utilities/ovs-save \ utilities/ovs-tcpdump.in \ utilities/ovs-tcpundump.in \ utilities/ovs-test.in \ utilities/ovs-vlan-test.in \ utilities/ovs-vsctl-bashcomp.bash \ utilities/checkpatch.py \ utilities/checkpatch_dict.txt \ utilities/docker/Makefile \ utilities/docker/ovs-override.conf \ utilities/docker/start-ovs \ utilities/docker/create_ovs_db.sh \ utilities/docker/debian/Dockerfile \ utilities/docker/debian/build-kernel-modules.sh \ utilities/usdt-scripts/bridge_loop.bt \ utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/kernel_delay.py \ utilities/usdt-scripts/kernel_delay.rst \ utilities/usdt-scripts/reval_monitor.py \ utilities/usdt-scripts/upcall_cost.py \ utilities/usdt-scripts/upcall_monitor.py \ utilities/usdt-scripts/usdt_lib.py MAN_ROOTS += \ utilities/ovs-testcontroller.8.in \ utilities/ovs-dpctl.8.in \ utilities/ovs-dpctl-top.8.in \ utilities/ovs-kmod-ctl.8 \ utilities/ovs-ofctl.8.in \ utilities/ovs-pcap.1.in \ utilities/ovs-vsctl.8.in CLEANFILES += \ utilities/ovs-ctl \ utilities/ovs-check-dead-ifs \ utilities/ovs-testcontroller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-dpctl-top \ utilities/ovs-dpctl-top.8 \ utilities/ovs-kmod-ctl \ utilities/ovs-l3ping \ utilities/ovs-lib \ utilities/ovs-ofctl.8 \ utilities/ovs-parse-backtrace \ utilities/ovs-pcap \ utilities/ovs-pcap.1 \ utilities/ovs-pki \ utilities/ovs-sim \ utilities/ovs-tcpdump \ utilities/ovs-tcpundump \ utilities/ovs-test \ utilities/ovs-vlan-test \ utilities/ovs-vsctl.8 man_MANS += \ utilities/ovs-testcontroller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-dpctl-top.8 \ utilities/ovs-kmod-ctl.8 \ utilities/ovs-ofctl.8 \ utilities/ovs-pcap.1 \ utilities/ovs-vsctl.8 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c utilities_ovs_appctl_LDADD = lib/libopenvswitch.la utilities_ovs_testcontroller_SOURCES = utilities/ovs-testcontroller.c utilities_ovs_testcontroller_LDADD = lib/libopenvswitch.la $(SSL_LIBS) utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c utilities_ovs_dpctl_LDADD = lib/libopenvswitch.la utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c utilities_ovs_ofctl_LDADD = \ ofproto/libofproto.la \ lib/libopenvswitch.la utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c utilities_ovs_vsctl_LDADD = lib/libopenvswitch.la if LINUX noinst_PROGRAMS += utilities/nlmon utilities_nlmon_SOURCES = utilities/nlmon.c utilities_nlmon_LDADD = lib/libopenvswitch.la endif FLAKE8_PYFILES += utilities/ovs-pcap.in \ utilities/checkpatch.py utilities/ovs-dev.py \ utilities/gdb/ovs_gdb.py \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-tcpdump.in \ utilities/ovs-pipegen.py \ utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/kernel_delay.py \ utilities/usdt-scripts/upcall_monitor.py \ utilities/usdt-scripts/upcall_cost.py include utilities/bugtool/automake.mk openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/000077500000000000000000000000001514270232600224465ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/.gitignore000066400000000000000000000000341514270232600244330ustar00rootroot00000000000000/ovs-bugtool /ovs-bugtool.8 openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/automake.mk000066400000000000000000000042011514270232600246020ustar00rootroot00000000000000sbin_SCRIPTS += utilities/bugtool/ovs-bugtool CLEANFILES += utilities/bugtool/ovs-bugtool man_MANS += utilities/bugtool/ovs-bugtool.8 MAN_ROOTS += utilities/bugtool/ovs-bugtool.8.in CLEANFILES += utilities/bugtool/ovs-bugtool.8 bugtool_plugins = \ utilities/bugtool/plugins/kernel-info/openvswitch.xml \ utilities/bugtool/plugins/network-status/openvswitch.xml \ utilities/bugtool/plugins/system-configuration.xml \ utilities/bugtool/plugins/system-logs/openvswitch.xml \ utilities/bugtool/plugins/system-configuration/openvswitch.xml bugtool_scripts = \ utilities/bugtool/ovs-bugtool-fdb-show \ utilities/bugtool/ovs-bugtool-tc-class-show \ utilities/bugtool/ovs-bugtool-daemons-ver \ utilities/bugtool/ovs-bugtool-ovs-ofctl-loop-over-bridges \ utilities/bugtool/ovs-bugtool-ovs-appctl-dpif \ utilities/bugtool/ovs-bugtool-ovs-bridge-datapath-type \ utilities/bugtool/ovs-bugtool-ovs-vswitchd-threads-affinity \ utilities/bugtool/ovs-bugtool-qos-configs \ utilities/bugtool/ovs-bugtool-get-dpdk-nic-numa \ utilities/bugtool/ovs-bugtool-get-port-stats scripts_SCRIPTS += $(bugtool_scripts) FLAKE8_PYFILES += utilities/bugtool/ovs-bugtool.in bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins INSTALL_DATA_LOCAL += bugtool-install-data-local bugtool-install-data-local: for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ $(MKDIR_P) "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ $(INSTALL_DATA) "$(srcdir)/$$plugin" "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ done UNINSTALL_LOCAL += bugtool-uninstall-local bugtool-uninstall-local: for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ rm -f "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ done for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ if [ ! -z "$$dir" ]; then \ rm -rf "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ fi \ done; exit 0 EXTRA_DIST += \ $(bugtool_plugins) \ $(bugtool_scripts) \ utilities/bugtool/ovs-bugtool.in openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-daemons-ver000077500000000000000000000015501514270232600270730ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012 Nicira, Inc. for f in `cd /var/run/openvswitch/; ls *.pid 2>/dev/null` do if [ -n "${f%.pid}" ]; then ovs-appctl -t "${f%.pid}" version 2>&1 fi echo done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-fdb-show000077500000000000000000000015241514270232600263650ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2014 Nicira, Inc. for bridge in `ovs-vsctl -- --real list-br` do echo "ovs-appctl fdb/show ${bridge}" ovs-appctl fdb/show "${bridge}" echo "" done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-get-dpdk-nic-numa000077500000000000000000000017211514270232600300570ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2017 Ericsson AB for PCI_ADDRESS in $(lspci -D | cut -d' ' -f1) do if [ -n "$(find /sys/bus/pci/drivers | grep drivers.*$PCI_ADDRESS | grep -e uio_pci_generic -e igb_uio -e vfio-pci)" ] then grep -H "" $(find /sys/devices/ -path "*$PCI_ADDRESS*numa_node") fi done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-get-port-stats000077500000000000000000000006131514270232600275470ustar00rootroot00000000000000#! /bin/bash #Iterate through each port of every bridge and print #the port statistics for bridge in `ovs-vsctl -- --real list-br` do echo "${bridge} : " echo " ${bridge} : `ovs-vsctl get interface ${bridge} statistics`" for iface in `ovs-vsctl list-ifaces ${bridge}` do echo " ${iface} : `ovs-vsctl get interface ${iface} statistics`" done echo -e "\n" done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-ovs-appctl-dpif000077500000000000000000000016271514270232600276700ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. echo "ovs-appctl dpif/show" ovs-appctl dpif/show for bridge in `ovs-vsctl -- --real list-br` do echo "ovs-appctl dpif/dump-flows -m ${bridge}" ovs-appctl dpif/dump-flows -m "$bridge" echo "" done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-ovs-bridge-datapath-type000077500000000000000000000015021514270232600314540ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2017 Ericsson AB for br in `ovs-vsctl -- --real list-br` do echo bridge $br datapath_type=$(ovs-vsctl get bridge $br datapath_type) done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-ovs-ofctl-loop-over-bridges000077500000000000000000000023621514270232600321260ustar00rootroot00000000000000#! /bin/bash # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2017 Ericsson AB for bridge in `ovs-vsctl -- --real list-br` do PROTOCOLS="$(ovs-vsctl get Bridge "$bridge" protocols)" if [ "$PROTOCOLS" = "[]" ] then echo "ovs-ofctl $1 ${bridge} $2" eval ovs-ofctl $1 "${bridge}" $2 else LENGTHPRO="$(expr length "$PROTOCOLS")" COMMAIND="$(expr index "$PROTOCOLS" ,)" echo "ovs-ofctl $1 -O${PROTOCOLS:${COMMAIND}+2:${LENGTHPRO}-${COMMAIND}-4} ${bridge} $2" eval ovs-ofctl $1 -O"${PROTOCOLS:${COMMAIND}+2:${LENGTHPRO}-${COMMAIND}-4}" "${bridge}" $2 fi echo "" done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-ovs-vswitchd-threads-affinity000077500000000000000000000014711514270232600325540ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2017 Ericsson AB for pid in $(pgrep ovs-vswitchd) do for i in $(ls /proc/$pid/task) do taskset -pc $i done done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-qos-configs000077500000000000000000000020261514270232600271020ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2017 Ericsson AB for br in `ovs-vsctl -- --real list-br` do for iface in $(ovs-vsctl list-ifaces ${br}) do echo "Rate limiting configuration for ${iface} (of ${br}):" ovs-vsctl list interface ${iface} | grep ingress echo "Interface options:" ovs-vsctl get Interface ${iface} options echo "" done done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool-tc-class-show000077500000000000000000000017251514270232600273460ustar00rootroot00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2011 Nicira, Inc. for iface in $(cd /sys/class/net && echo *); do if [ -d /sys/class/net/$iface ]; then echo Interface $iface: # indent tc output so it's clear which interface it pertains to /sbin/tc -s -d class show dev $iface | /bin/sed 's/^/ /' fi done openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool.8.in000066400000000000000000000050471514270232600254320ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-bugtool 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-bugtool . .SH NAME ovs\-bugtool \- Open vSwitch bug reporting utility . .SH SYNOPSIS .B ovs\-bugtool . .SH DESCRIPTION Generates a debug bundle with useful information about Open vSwitch on this system and places it in \fB/var/log/ovs-bugtool\fR. . .SH "COLLECTION OPTIONS" .PP These options influence what categories of data \fBovs\-bugtool\fR collects. . .IP "\fB\-\-entries=\fIlist\fR" Collect the capabilities specified in the comma-separated \fIlist\fR. .IP "\fB\-\-all\fR" Collect all available capabilities. .IP "\fB\-\-ovs\fR" In addition to Open vSwitch configuration and status, \fBovs\-bugtool\fR can collect a variety of relevant system information. This option limits collection to Open vSwitch-specific categories. .IP "\fB\-\-log\-days=\fIdays\fR" Include the logs with last modification time in the previous \fIdays\fR days in the debug bundle. The number of log files included has a big impact on the eventual bundle size. The default value is 20 days. .IP "\fB\-y\fR" .IQ "\fB\-\-yestoall\fR" Answer yes to all prompts. .IP "\fB\-\-capabilities\fR" Writes the categories that \fBovs\-bugtool\fR can collect on stdout in XML, then exits. . .SH "OUTPUT OPTIONS" .PP These options influence the format and destination of \fBovs\-bugtool\fR output. . .IP "\fB\-\-output=\fIfiletype\fR" Generates a debug bundle with the specified file type. Options include \fBtar\fR, \fBtar.gz\fR, \fBtar.bz2\fR, and \fBzip\fR. .IP "\fB\-\-outfile=\fIfile\fR" Write output to \fIfile\fR. Mutually exclusive with \fB\-\-outfd\fR. .IP "\fB\-\-outfd=\fIfd\fR" Write output to file descriptor \fIfd\fR. This option must be used with \fB\-\-output=tar\fR. .IP "\fB\-\-unlimited\fR" Do not exclude files which are too large. Also skip checking free disk space. By default up to 90 percent of the free disk space can be used. .IP "\fB\-\-debug\fR" Print verbose debugging output. . .SH "OTHER OPTIONS" . .IP "\fB\-s\fR" .IQ "\fB\-\-silent\fR" Suppress most output to stdout. .IP "\fB\-\-help\fR" Print a summary of \fBovs\-bugtool\fR usage to stdout, then exit. . .SH EXAMPLES .PP Here's a collection of some commonly useful options: .PP \fBovs\-bugtool \-y \-s \-\-output=tar.gz \-\-outfile=/var/log/bugtool-report.tgz\fR . .SH BUGS \fBovs\-bugtool\fR makes many assumptions about file locations and the availability of system utilities. It has been tested on Debian and Red Hat and derived distributions. On other distributions it is likely to be less useful. openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/ovs-bugtool.in000077500000000000000000001326441514270232600252730ustar00rootroot00000000000000#! @PYTHON3@ # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Copyright (c) 2005, 2007 XenSource Ltd. # Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016, 2017 Nicira, Inc. # # To add new entries to the bugtool, you need to: # # Create a new capability. These declare the new entry to the GUI, including # the expected size, time to collect, privacy implications, and whether the # capability should be selected by default. One capability may refer to # multiple files, assuming that they can be reasonably grouped together, and # have the same privacy implications. You need: # # A new CAP_ constant. # A cap() invocation to declare the capability. # # You then need to add calls to main() to collect the files. These will # typically be calls to the helpers file_output(), tree_output(), cmd_output(), # or func_output(). # from io import BytesIO import fcntl import getopt import hashlib import os import platform import re import sys import tarfile import time import warnings import zipfile from select import select from signal import SIGTERM from subprocess import PIPE, Popen, check_output from xml.dom.minidom import getDOMImplementation, parse warnings.filterwarnings(action="ignore", category=DeprecationWarning) OS_RELEASE = platform.release() # # Files & directories # APT_SOURCES_LIST = "/etc/apt/sources.list" APT_SOURCES_LIST_D = "/etc/apt/sources.list.d" BUG_DIR = "/var/log/ovs-bugtool" PLUGIN_DIR = "@pkgdatadir@/bugtool-plugins" GRUB_CONFIG = '/boot/grub/menu.lst' BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img' PROC_PARTITIONS = '/proc/partitions' FSTAB = '/etc/fstab' PROC_MOUNTS = '/proc/mounts' ISCSI_CONF = '/etc/iscsi/iscsid.conf' ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi' PROC_CPUINFO = '/proc/cpuinfo' PROC_MEMINFO = '/proc/meminfo' PROC_IOPORTS = '/proc/ioports' PROC_INTERRUPTS = '/proc/interrupts' PROC_SCSI = '/proc/scsi/scsi' PROC_VERSION = '/proc/version' PROC_MODULES = '/proc/modules' PROC_DEVICES = '/proc/devices' PROC_FILESYSTEMS = '/proc/filesystems' PROC_CMDLINE = '/proc/cmdline' PROC_CONFIG = '/proc/config.gz' PROC_USB_DEV = '/proc/bus/usb/devices' PROC_NET_BONDING_DIR = '/proc/net/bonding' IFCFG_RE = re.compile(r'^.*/ifcfg-.*') ROUTE_RE = re.compile(r'^.*/route-.*') SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf' SYSCONFIG_NETWORK = '/etc/sysconfig/network' SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts' INTERFACES = '/etc/network/interfaces' INTERFACESD = '/etc/network/interfaces.d' PROC_NET_VLAN_DIR = '/proc/net/vlan' PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat' MODPROBE_CONF = '/etc/modprobe.conf' MODPROBE_DIR = '/etc/modprobe.d' RESOLV_CONF = '/etc/resolv.conf' MPP_CONF = '/etc/mpp.conf' MULTIPATH_CONF = '/etc/multipath.conf' NSSWITCH_CONF = '/etc/nsswitch.conf' NTP_CONF = '/etc/ntp.conf' IPTABLES_CONFIG = '/etc/sysconfig/iptables-config' HOSTS = '/etc/hosts' HOSTS_ALLOW = '/etc/hosts.allow' HOSTS_DENY = '/etc/hosts.deny' DHCP_LEASE_DIR = ['/var/lib/dhclient', '/var/lib/dhcp3'] OPENVSWITCH_LOG_DIR = '@LOGDIR@/' OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch' # RHEL OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db' OPENVSWITCH_COMPACT_DB = '@DBDIR@/bugtool-compact-conf.db' OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid' VAR_LOG_DIR = '/var/log/' VAR_LOG_CORE_DIR = '/var/log/core' YUM_LOG = '/var/log/yum.log' YUM_REPOS_DIR = '/etc/yum.repos.d' # # External programs # os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' \ '/usr/sbin:/usr/bin:/sbin:/bin:@pkgdatadir@/scripts' ARP = 'arp' CAT = 'cat' CHKCONFIG = 'chkconfig' DATE = 'date' DF = 'df' DMESG = 'dmesg' DMIDECODE = 'dmidecode' DMSETUP = 'dmsetup' DPKG_QUERY = 'dpkg-query' ETHTOOL = 'ethtool' FDISK = 'fdisk' FIND = 'find' IP = 'ip' IPTABLES = 'iptables' ISCSIADM = 'iscsiadm' LOSETUP = 'losetup' LS = 'ls' LSPCI = 'lspci' MODINFO = 'modinfo' MPPUTIL = 'mppUtil' MULTIPATHD = 'multipathd' NETSTAT = 'netstat' OVS_DPCTL = 'ovs-dpctl' OVS_OFCTL = 'ovs-ofctl' OVS_VSCTL = 'ovs-vsctl' PS = 'ps' ROUTE = 'route' RPM = 'rpm' SG_MAP = 'sg_map' SHA256_SUM = 'sha256sum' SYSCTL = 'sysctl' TC = 'tc' UPTIME = 'uptime' ZCAT = 'zcat' # # PII -- Personally identifiable information. Of particular concern are # things that would identify customers, or their network topology. # Passwords are never to be included in any bug report, regardless of any PII # declaration. # # NO -- No PII will be in these entries. # YES -- PII will likely or certainly be in these entries. # MAYBE -- The user may wish to audit these entries for PII. # IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII, # but since we encourage customers to edit these files, PII may have been # introduced by the customer. This is used in particular for the networking # scripts in dom0. # PII_NO = 'no' PII_YES = 'yes' PII_MAYBE = 'maybe' PII_IF_CUSTOMIZED = 'if_customized' KEY = 0 PII = 1 MIN_SIZE = 2 MAX_SIZE = 3 MIN_TIME = 4 MAX_TIME = 5 MIME = 6 CHECKED = 7 HIDDEN = 8 MIME_DATA = 'application/data' MIME_TEXT = 'text/plain' INVENTORY_XML_ROOT = "system-status-inventory" INVENTORY_XML_SUMMARY = 'system-summary' INVENTORY_XML_ELEMENT = 'inventory-entry' CAP_XML_ROOT = "system-status-capabilities" CAP_XML_ELEMENT = 'capability' CAP_BOOT_LOADER = 'boot-loader' CAP_DISK_INFO = 'disk-info' CAP_HARDWARE_INFO = 'hardware-info' CAP_KERNEL_INFO = 'kernel-info' CAP_LOSETUP_A = 'loopback-devices' CAP_MULTIPATH = 'multipath' CAP_NETWORK_CONFIG = 'network-config' CAP_NETWORK_INFO = 'network-info' CAP_NETWORK_STATUS = 'network-status' CAP_OPENVSWITCH_LOGS = 'ovs-system-logs' CAP_PROCESS_LIST = 'process-list' CAP_SYSTEM_LOGS = 'system-logs' CAP_SYSTEM_SERVICES = 'system-services' CAP_YUM = 'yum' KB = 1024 MB = 1024 * 1024 caps = {} cap_sizes = {} unlimited_data = False dbg = False # Default value for the number of days to collect logs. log_days = 20 log_last_mod_time = None free_disk_space = None # Default value for delay between repeated commands command_delay = 10 def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1, max_time=-1, mime=MIME_TEXT, checked=True, hidden=False): caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime, checked, hidden) cap_sizes[key] = 0 cap(CAP_BOOT_LOADER, PII_NO, max_size=3 * KB, max_time=5) cap(CAP_DISK_INFO, PII_MAYBE, max_size=50 * KB, max_time=20) cap(CAP_HARDWARE_INFO, PII_MAYBE, max_size=2 * MB, max_time=20) cap(CAP_KERNEL_INFO, PII_MAYBE, max_size=120 * KB, max_time=5) cap(CAP_LOSETUP_A, PII_MAYBE, max_size=KB, max_time=5) cap(CAP_MULTIPATH, PII_MAYBE, max_size=20 * KB, max_time=10) cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED, min_size=0, max_size=5 * MB) cap(CAP_NETWORK_INFO, PII_YES, max_size=50 * MB, max_time=30) cap(CAP_NETWORK_STATUS, PII_YES, max_size=-1, max_time=30) cap(CAP_OPENVSWITCH_LOGS, PII_MAYBE, max_size=-1, max_time=5) cap(CAP_PROCESS_LIST, PII_YES, max_size=30 * KB, max_time=20) cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=200 * MB, max_time=5) cap(CAP_SYSTEM_SERVICES, PII_NO, max_size=5 * KB, max_time=20) cap(CAP_YUM, PII_IF_CUSTOMIZED, max_size=10 * KB, max_time=30) ANSWER_YES_TO_ALL = False SILENT_MODE = False entries = None data = {} dev_null = open('/dev/null', 'r+') def output(x): global SILENT_MODE if not SILENT_MODE: print(x) def output_ts(x): output("[%s] %s" % (time.strftime("%x %X %Z"), x)) def cmd_output(cap, args, label=None, filter=None, binary=False, repeat_count=1): if cap in entries: if not label: if isinstance(args, list): a = [aa for aa in args] a[0] = os.path.basename(a[0]) label = ' '.join(a) else: label = args data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter, 'binary': binary, 'repeat_count': repeat_count} def file_output(cap, path_list, newest_first=False, last_mod_time=None): """ If newest_first is True, the list of files in path_list is sorted by file modification time in descending order, else its sorted in ascending order. """ if cap in entries: path_entries = [] for path in path_list: try: s = os.stat(path) except OSError: continue if last_mod_time is None or s.st_mtime >= last_mod_time: path_entries.append((path, s)) def mtime(arg): (path, stat) = arg return stat.st_mtime path_entries.sort(key=mtime, reverse=newest_first) for p in path_entries: if check_space(cap, p[0], p[1].st_size): data[p] = {'cap': cap, 'filename': p[0]} def tree_output(cap, path, pattern=None, negate=False, newest_first=False, last_mod_time=None): """ Walks the directory tree rooted at path. Files in current dir are processed before files in sub-dirs. """ if cap in entries: if os.path.exists(path): for root, dirs, files in os.walk(path): fns = [fn for fn in [os.path.join(root, f) for f in files] if os.path.isfile(fn) and matches(fn, pattern, negate)] file_output(cap, fns, newest_first=newest_first, last_mod_time=last_mod_time) def prefix_output(cap, prefix, newest_first=False, last_mod_time=None): """ Output files with the same prefix. """ fns = [] for root, dirs, files in os.walk(os.path.dirname(prefix)): fns += [fn for fn in [os.path.join(root, f) for f in files] if fn.startswith(prefix)] file_output(cap, fns, newest_first=newest_first, last_mod_time=last_mod_time) def func_output(cap, label, func): if cap in entries: data[label] = {'cap': cap, 'func': func} def collect_data(): first_run = True while True: process_lists = {} for (k, v) in data.items(): cap = v['cap'] if 'cmd_args' in v: if 'output' not in v.keys(): v['output'] = BytesIOmtime() if v['repeat_count'] > 0: if cap not in process_lists: process_lists[cap] = [] process_lists[cap].append( ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter'], v['binary'])) v['repeat_count'] -= 1 if bool(process_lists): if not first_run: output_ts("Waiting %d sec between repeated commands" % command_delay) time.sleep(command_delay) else: first_run = False run_procs(process_lists.values()) else: break for (k, v) in data.items(): cap = v['cap'] if 'filename' in v and v['filename'].startswith('/proc/'): # proc files must be read into memory try: f = open(v['filename'], 'rb') s = f.read() f.close() if check_space(cap, v['filename'], len(s)): v['output'] = BytesIOmtime(s) except: pass elif 'func' in v: try: s = v['func'](cap) except Exception as e: s = str(e).encode() if check_space(cap, k, len(s)): if isinstance(s, str): v['output'] = BytesIOmtime(s.encode()) else: v['output'] = BytesIOmtime(s) def main(argv=None): global ANSWER_YES_TO_ALL, SILENT_MODE global entries, data, dbg, unlimited_data, free_disk_space global log_days, log_last_mod_time, command_delay # Filter flags only_ovs_info = False collect_all_info = True if '--help' in sys.argv: print(""" %(argv0)s: create status report bundles to assist in problem diagnosis usage: %(argv0)s OPTIONS By default, %(argv0)s prompts for permission to collect each form of status information and produces a .tar.gz file as output. The following options are available. --help display this help message, then exit -s, --silent suppress most output to stdout Options for categories of data to collect: --entries=CAP_A,CAP_B,... set categories of data to collect --all collect all categories --ovs collect only directly OVS-related info --log-days=DAYS collect DAYS worth of old logs --delay=DELAY set delay between repeated command -y, --yestoall suppress prompts to confirm collection --capabilities print categories as XML on stdout, then exit Output options: --output=FORMAT set output format to one of tar tar.bz2 tar.gz zip --outfile=FILE write output to FILE --outfd=FD write output to FD (requires --output=tar) --unlimited ignore default limits on sizes of data collected --debug print ovs-bugtool debug info on stdout\ """ % {'argv0': sys.argv[0]}) sys.exit(0) # we need access to privileged files, exit if we are not running as root if os.getuid() != 0: print("Error: ovs-bugtool must be run as root", file=sys.stderr) return 1 output_file = None output_type = 'tar.gz' output_fd = -1 if argv is None: argv = sys.argv try: (options, params) = getopt.gnu_getopt( argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=', 'output=', 'outfd=', 'outfile=', 'all', 'unlimited', 'debug', 'ovs', 'log-days=', 'delay=']) except getopt.GetoptError as opterr: print(opterr, file=sys.stderr) return 2 try: load_plugins(True) except: pass entries = [e for e in caps.keys() if caps[e][CHECKED]] for (k, v) in options: if k == '--capabilities': update_capabilities() print_capabilities() return 0 if k == '--output': if v in ['tar', 'tar.bz2', 'tar.gz', 'zip']: output_type = v else: print("Invalid output format '%s'" % v, file=sys.stderr) return 2 # "-s" or "--silent" means suppress output (except for the final # output filename at the end) if k in ['-s', '--silent']: SILENT_MODE = True if k == '--entries' and v != '': entries = v.split(',') # If the user runs the script with "-y" or "--yestoall" we don't ask # all the really annoying questions. if k in ['-y', '--yestoall']: ANSWER_YES_TO_ALL = True if k == '--outfd': output_fd = int(v) try: old = fcntl.fcntl(output_fd, fcntl.F_GETFD) fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) except: print("Invalid output file descriptor", output_fd, file=sys.stderr) return 2 if k == '--outfile': output_file = v elif k == '--all': entries = caps.keys() elif k == '--unlimited': unlimited_data = True elif k == '--debug': dbg = True ProcOutput.debug = True if k == '--ovs': only_ovs_info = True collect_all_info = False if k == '--log-days': log_days = int(v) if k == '--delay': command_delay = int(v) if len(params) != 1: print("Invalid additional arguments", str(params), file=sys.stderr) return 2 if output_fd != -1 and output_type != 'tar': print("Option '--outfd' only valid with '--output=tar'", file=sys.stderr) return 2 if output_fd != -1 and output_file is not None: print("Cannot set both '--outfd' and '--outfile'", file=sys.stderr) return 2 if output_file is not None and not unlimited_data: free_disk_space = get_free_disk_space(output_file) * 90 / 100 log_last_mod_time = int(time.time()) - log_days * 86400 if ANSWER_YES_TO_ALL: output("Warning: '--yestoall' argument provided, will not prompt " "for individual files.") output(''' This application will collate dmesg output, details of the hardware configuration of your machine, information about the build of openvswitch that you are using, plus, if you allow it, various logs. The collated information will be saved as a .%s for archiving or sending to a Technical Support Representative. The logs may contain private information, and if you are at all worried about that, you should exit now, or you should explicitly exclude those logs from the archive. ''' % output_type) # assemble potential data file_output(CAP_BOOT_LOADER, [GRUB_CONFIG]) cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot']) cmd_output(CAP_BOOT_LOADER, [SHA256_SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.sha256sum') cmd_output(CAP_DISK_INFO, [FDISK, '-l']) file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS]) file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR]) cmd_output(CAP_DISK_INFO, [DF, '-alT']) cmd_output(CAP_DISK_INFO, [DF, '-alTi']) if len(pidof('iscsid')) != 0: cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport']) cmd_output(CAP_DISK_INFO, [SG_MAP, '-x']) func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts) file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS]) cmd_output(CAP_HARDWARE_INFO, [DMIDECODE]) cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n']) cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv']) file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI]) file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF]) cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev']) file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, PROC_FILESYSTEMS, PROC_CMDLINE]) cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config') cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A']) file_output(CAP_KERNEL_INFO, [MODPROBE_CONF]) tree_output(CAP_KERNEL_INFO, MODPROBE_DIR) func_output(CAP_KERNEL_INFO, 'modinfo', module_info) cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a']) file_output(CAP_MULTIPATH, [MULTIPATH_CONF, MPP_CONF]) cmd_output(CAP_MULTIPATH, [DMSETUP, 'table']) func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology) cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a']) if CAP_MULTIPATH in entries and collect_all_info: dump_rdac_groups(CAP_MULTIPATH) tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE) tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE) tree_output(CAP_NETWORK_CONFIG, INTERFACESD) file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS, INTERFACES]) file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY]) file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_DEFAULT_SWITCH, OPENVSWITCH_SYSCONFIG_SWITCH]) cmd_output(CAP_NETWORK_INFO, [IP, 'addr', 'show']) cmd_output(CAP_NETWORK_INFO, [ROUTE, '-n']) cmd_output(CAP_NETWORK_INFO, [ARP, '-n']) cmd_output(CAP_NETWORK_INFO, [NETSTAT, '-an']) for dir in DHCP_LEASE_DIR: tree_output(CAP_NETWORK_INFO, dir) for table in ['filter', 'nat', 'mangle', 'raw', 'security']: cmd_output(CAP_NETWORK_INFO, [IPTABLES, '-t', table, '-nL']) for p in os.listdir('/sys/class/net/'): try: f = open('/sys/class/net/%s/type' % p, 'r') t = f.readline() f.close() if os.path.islink('/sys/class/net/%s/device' % p) and int(t) == 1: # ARPHRD_ETHER cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-S', p]) if not p.startswith('vif') and not p.startswith('tap'): cmd_output(CAP_NETWORK_INFO, [ETHTOOL, p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-k', p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-i', p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-c', p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-l', p]) if int(t) == 1: cmd_output(CAP_NETWORK_INFO, [TC, '-s', '-d', 'class', 'show', 'dev', p]) except: pass tree_output(CAP_NETWORK_INFO, PROC_NET_BONDING_DIR) tree_output(CAP_NETWORK_INFO, PROC_NET_VLAN_DIR) cmd_output(CAP_NETWORK_INFO, [TC, '-s', 'qdisc']) file_output(CAP_NETWORK_INFO, [PROC_NET_SOFTNET_STAT]) collect_ovsdb() if os.path.exists(OPENVSWITCH_VSWITCHD_PID): cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show', '-s']) for d in dp_list(): cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', '-m', d.decode()]) cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree') func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage) system_logs = ([VAR_LOG_DIR + x for x in ['crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot']]) for log in system_logs: prefix_output(CAP_SYSTEM_LOGS, log, last_mod_time=log_last_mod_time) ovs_logs = ([OPENVSWITCH_LOG_DIR + x for x in ['ovs-vswitchd.log', 'ovsdb-server.log', 'ovs-ctl.log']]) for log in ovs_logs: prefix_output(CAP_OPENVSWITCH_LOGS, log, last_mod_time=log_last_mod_time) cmd_output(CAP_SYSTEM_LOGS, [DMESG]) cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list']) tree_output(CAP_SYSTEM_LOGS, VAR_LOG_CORE_DIR) file_output(CAP_YUM, [YUM_LOG]) tree_output(CAP_YUM, YUM_REPOS_DIR) cmd_output(CAP_YUM, [RPM, '-qa']) file_output(CAP_YUM, [APT_SOURCES_LIST]) tree_output(CAP_YUM, APT_SOURCES_LIST_D) cmd_output(CAP_YUM, [DPKG_QUERY, '-W', '-f=${Package} ${Version} ${Status}\n'], 'dpkg-packages') # Filter out ovs relevant information if --ovs option passed # else collect all information filters = set() if only_ovs_info: filters.add('ovs') ovs_info_caps = [CAP_NETWORK_STATUS, CAP_SYSTEM_LOGS, CAP_OPENVSWITCH_LOGS, CAP_NETWORK_CONFIG] ovs_info_list = ['process-tree'] # We cannot use items(), since we modify 'data' as we pass through for (k, v) in list(data.items()): cap = v['cap'] if 'filename' in v: info = k[0] else: info = k if info not in ovs_info_list and cap not in ovs_info_caps: del data[k] if filters: filter = ",".join(filters) else: filter = None try: load_plugins(filter=filter) except: pass # permit the user to filter out data # We cannot use items(), since we modify 'data' as we pass through for (k, v) in list(data.items()): cap = v['cap'] if 'filename' in v: key = k[0] else: key = k if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % key): del data[k] # collect selected data now output_ts('Running commands to collect data') collect_data() subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S") # include inventory data['inventory.xml'] = {'cap': None, 'output': BytesIOmtime(make_inventory(data, subdir))} # create archive if output_fd == -1: if output_file is None: dirname = BUG_DIR else: dirname = os.path.dirname(output_file) if dirname and not os.path.exists(dirname): try: os.makedirs(dirname) except: pass if output_fd == -1: output_ts('Creating output file') if output_type.startswith('tar'): make_tar(subdir, output_type, output_fd, output_file) else: make_zip(subdir, output_file) if dbg: print("Category sizes (max, actual):\n", file=sys.stderr) for c in caps.keys(): print(" %s (%d, %d)" % (c, caps[c][MAX_SIZE], cap_sizes[c]), file=sys.stderr) cleanup_ovsdb() return 0 def dump_scsi_hosts(cap): output = '' scsi_list = os.listdir('/sys/class/scsi_host') scsi_list.sort() for h in scsi_list: procname = '' try: f = open('/sys/class/scsi_host/%s/proc_name' % h) procname = f.readline().strip("\n") f.close() except: pass modelname = None try: f = open('/sys/class/scsi_host/%s/model_name' % h) modelname = f.readline().strip("\n") f.close() except: pass output += "%s:\n" % h output += " %s%s\n" \ % (procname, modelname and (" -> %s" % modelname) or '') return output def module_info(cap): output = BytesIO() modules = open(PROC_MODULES, 'r') procs = [] for line in modules: module = line.split()[0] procs.append(ProcOutput([MODINFO, module], caps[cap][MAX_TIME], output)) modules.close() run_procs([procs]) return output.getvalue() def multipathd_topology(cap): pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, stdout=PIPE, stderr=dev_null) stdout, stderr = pipe.communicate('show topology') return stdout def dp_list(): output = BytesIO() procs = [ProcOutput([OVS_DPCTL, 'dump-dps'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)] run_procs([procs]) if not procs[0].timed_out: return output.getvalue().splitlines() return [] def collect_ovsdb(): if not os.path.isfile(OPENVSWITCH_CONF_DB): return max_size = 10 * MB try: if os.path.getsize(OPENVSWITCH_CONF_DB) > max_size: if os.path.isfile(OPENVSWITCH_COMPACT_DB): os.unlink(OPENVSWITCH_COMPACT_DB) output = BytesIO() max_time = 5 procs = [ProcOutput(['ovsdb-tool', 'compact', OPENVSWITCH_CONF_DB, OPENVSWITCH_COMPACT_DB], max_time, output)] run_procs([procs]) file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_COMPACT_DB]) else: file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_CONF_DB]) except OSError: return def cleanup_ovsdb(): try: if os.path.isfile(OPENVSWITCH_COMPACT_DB): os.unlink(OPENVSWITCH_COMPACT_DB) except: return def fd_usage(cap): output = '' fd_dict = {} for d in [p for p in os.listdir('/proc') if p.isdigit()]: try: fh = open('/proc/' + d + '/cmdline') name = fh.readline() num_fds = len(os.listdir(os.path.join('/proc/' + d + '/fd'))) if num_fds > 0: if num_fds not in fd_dict: fd_dict[num_fds] = [] fd_dict[num_fds].append(name.replace('\0', ' ').strip()) finally: fh.close() keys = fd_dict.keys() keys.sort(lambda a, b: int(b) - int(a)) for k in keys: output += "%s: %s\n" % (k, str(fd_dict[k])) return output def dump_rdac_groups(cap): output = BytesIO() procs = [ProcOutput([MPPUTIL, '-a'], caps[cap][MAX_TIME], output)] run_procs([procs]) if not procs[0].timed_out: proc_line = 0 for line in output.getvalue().splitlines(): if line.startswith('ID'): proc_line = 2 elif line.startswith('----'): proc_line -= 1 elif proc_line > 0: group, _ = line.split(None, 1) cmd_output(cap, [MPPUTIL, '-g', group]) def load_plugins(just_capabilities=False, filter=None): global log_last_mod_time def getText(nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc += node.data return rc def getBoolAttr(el, attr, default=False): ret = default val = el.getAttribute(attr).lower() if val in ['true', 'false', 'yes', 'no']: ret = val in ['true', 'yes'] return ret for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]: if dir not in caps: if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)): continue xmldoc = parse("%s/%s.xml" % (PLUGIN_DIR, dir)) assert xmldoc.documentElement.tagName == "capability" pii, min_size, max_size, min_time, max_time, mime = \ PII_MAYBE, -1, -1, -1, -1, MIME_TEXT if xmldoc.documentElement.getAttribute("pii") in \ [PII_NO, PII_YES, PII_MAYBE, PII_IF_CUSTOMIZED]: pii = xmldoc.documentElement.getAttribute("pii") if xmldoc.documentElement.getAttribute("min_size") != '': min_size = int( xmldoc.documentElement.getAttribute("min_size")) if xmldoc.documentElement.getAttribute("max_size") != '': max_size = int( xmldoc.documentElement.getAttribute("max_size")) if xmldoc.documentElement.getAttribute("min_time") != '': min_time = int(xmldoc.documentElement.getAttribute("min_time")) if xmldoc.documentElement.getAttribute("max_time") != '': max_time = int(xmldoc.documentElement.getAttribute("max_time")) if xmldoc.documentElement.getAttribute("mime") in \ [MIME_DATA, MIME_TEXT]: mime = xmldoc.documentElement.getAttribute("mime") checked = getBoolAttr(xmldoc.documentElement, 'checked', True) hidden = getBoolAttr(xmldoc.documentElement, 'hidden', False) cap(dir, pii, min_size, max_size, min_time, max_time, mime, checked, hidden) if just_capabilities: continue plugdir = os.path.join(PLUGIN_DIR, dir) for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]: xmldoc = parse(os.path.join(plugdir, file)) assert xmldoc.documentElement.tagName == "collect" for el in xmldoc.documentElement.getElementsByTagName("*"): filters_tmp = el.getAttribute("filters") if filters_tmp == '': filters = [] else: filters = filters_tmp.split(',') if not (filter is None or filter in filters): continue if el.tagName == "files": newest_first = getBoolAttr(el, 'newest_first') if el.getAttribute("type") == "logs": for fn in getText(el.childNodes).split(): prefix_output(dir, fn, newest_first=newest_first, last_mod_time=log_last_mod_time) else: file_output(dir, getText(el.childNodes).split(), newest_first=newest_first) elif el.tagName == "directory": pattern = el.getAttribute("pattern") if pattern == '': pattern = None negate = getBoolAttr(el, 'negate') newest_first = getBoolAttr(el, 'newest_first') if el.getAttribute("type") == "logs": tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate=negate, newest_first=newest_first, last_mod_time=log_last_mod_time) else: tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate=negate, newest_first=newest_first) elif el.tagName == "command": label = el.getAttribute("label") if label == '': label = None binary = getBoolAttr(el, 'binary') try: repeat_count = int(el.getAttribute("repeat")) if repeat_count > 1: cmd_output(dir, DATE + ';' + getText(el.childNodes), label, binary=binary, repeat_count=repeat_count) else: cmd_output(dir, getText(el.childNodes), label, binary=binary, repeat_count=repeat_count) except: cmd_output(dir, getText(el.childNodes), label, binary=binary) def make_tar(subdir, suffix, output_fd, output_file): global SILENT_MODE, data mode = 'w' if suffix == 'tar.bz2': mode = 'w:bz2' elif suffix == 'tar.gz': mode = 'w:gz' if output_fd == -1: if output_file is None: filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix) else: filename = output_file old_umask = os.umask(0o077) tf = tarfile.open(filename, mode) os.umask(old_umask) else: tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a')) try: for (k, v) in data.items(): try: tar_filename = os.path.join(subdir, construct_filename(k, v)) ti = tarfile.TarInfo(tar_filename) ti.uname = 'root' ti.gname = 'root' if 'output' in v: ti.mtime = v['output'].mtime ti.size = len(v['output'].getvalue()) v['output'].seek(0) tf.addfile(ti, v['output']) elif 'filename' in v: s = os.stat(v['filename']) ti.mtime = s.st_mtime ti.size = s.st_size tf.addfile(ti, open(v['filename'], 'rb')) except: pass finally: tf.close() if output_fd == -1: output('Writing tarball %s successful.' % filename) if SILENT_MODE: print(filename) def make_zip(subdir, output_file): global SILENT_MODE, data if output_file is None: filename = "%s/%s.zip" % (BUG_DIR, subdir) else: filename = output_file old_umask = os.umask(0o077) zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) os.umask(old_umask) try: for (k, v) in data.items(): try: dest = os.path.join(subdir, construct_filename(k, v)) if 'output' in v: zf.writestr(dest, v['output'].getvalue()) else: if os.stat(v['filename']).st_size < 50: compress_type = zipfile.ZIP_STORED else: compress_type = zipfile.ZIP_DEFLATED zf.write(v['filename'], dest, compress_type) except: pass finally: zf.close() output('Writing archive %s successful.' % filename) if SILENT_MODE: print(filename) def make_inventory(inventory, subdir): document = getDOMImplementation().createDocument( None, INVENTORY_XML_ROOT, None) # create summary entry s = document.createElement(INVENTORY_XML_SUMMARY) user = os.getenv('SUDO_USER', os.getenv('USER')) if user: s.setAttribute('user', user) s.setAttribute('date', time.strftime('%c')) s.setAttribute('hostname', platform.node()) s.setAttribute('uname', ' '.join(platform.uname())) s.setAttribute('uptime', check_output(UPTIME).decode()) document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s) map(lambda k_v: inventory_entry(document, subdir, k_v[0], k_v[1]), inventory.items()) return document.toprettyxml().encode() def inventory_entry(document, subdir, k, v): try: el = document.createElement(INVENTORY_XML_ELEMENT) el.setAttribute('capability', v['cap']) el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v))) el.setAttribute('sha256sum', sha256(v)) document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el) except: pass def sha256(d): m = hashlib.sha256() if 'filename' in d: f = open(d['filename']) data = f.read(1024) while len(data) > 0: m.update(data) data = f.read(1024) f.close() elif 'output' in d: m.update(d['output'].getvalue()) return m.hexdigest() def construct_filename(k, v): if 'filename' in v: if v['filename'][0] == '/': return v['filename'][1:] else: return v['filename'] s = k.replace(' ', '-') s = s.replace('--', '-') s = s.replace('/', '%') if s.find('.') == -1: s += '.out' return s def update_capabilities(): pass def update_cap_size(cap, size): update_cap(cap, MIN_SIZE, size) update_cap(cap, MAX_SIZE, size) update_cap(cap, CHECKED, size > 0) def update_cap(cap, k, v): global caps temp = list(caps[cap]) temp[k] = v caps[cap] = tuple(temp) def size_of_dir(d, pattern=None, negate=False): if os.path.isdir(d): return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)], pattern, negate) else: return 0 def size_of_all(files, pattern=None, negate=False): return sum([size_of(f, pattern, negate) for f in files]) def matches(f, pattern, negate): if negate: return not matches(f, pattern, False) else: return pattern is None or pattern.match(f) def size_of(f, pattern, negate): if os.path.isfile(f) and matches(f, pattern, negate): return os.stat(f)[6] else: return size_of_dir(f, pattern, negate) def print_capabilities(): document = getDOMImplementation().createDocument( "ns", CAP_XML_ROOT, None) map(lambda key: capability(document, key), [k for k in caps.keys() if not caps[k][HIDDEN]]) print(document.toprettyxml()) def capability(document, key): c = caps[key] el = document.createElement(CAP_XML_ELEMENT) el.setAttribute('key', c[KEY]) el.setAttribute('pii', c[PII]) el.setAttribute('min-size', str(c[MIN_SIZE])) el.setAttribute('max-size', str(c[MAX_SIZE])) el.setAttribute('min-time', str(c[MIN_TIME])) el.setAttribute('max-time', str(c[MAX_TIME])) el.setAttribute('content-type', c[MIME]) el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no') document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el) def prettyDict(d): format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()])) return '\n'.join([format % i for i in d.items()]) + '\n' def yes(prompt): yn = input(prompt) return len(yn) == 0 or yn.lower()[0] == 'y' partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)') def disk_list(): disks = [] try: f = open('/proc/partitions') f.readline() f.readline() for line in f.readlines(): (major, minor, blocks, name) = line.split() if int(major) < 254 and not partition_re.match(name): disks.append(name) f.close() except: pass return disks class ProcOutput(object): debug = False def __init__(self, command, max_time, inst=None, filter=None, binary=False): self.command = command self.max_time = max_time self.inst = inst self.running = False self.status = None self.timed_out = False self.failed = False self.timeout = int(time.time()) + self.max_time self.filter = filter self.filter_state = {} if binary: self.bufsize = 1048576 # 1MB buffer else: self.bufsize = 1 # line buffered def __del__(self): self.terminate() def cmdAsStr(self): return isinstance(self.command, list) \ and ' '.join(self.command) or self.command def run(self): self.timed_out = False try: if ProcOutput.debug: output_ts("Starting '%s'" % self.cmdAsStr()) self.proc = Popen(self.command, bufsize=self.bufsize, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str)) old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD) fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) self.running = True self.failed = False except: output_ts("'%s' failed" % self.cmdAsStr()) self.running = False self.failed = True def terminate(self): if self.running: try: self.proc.stdout.close() os.kill(self.proc.pid, SIGTERM) except: pass self.proc = None self.running = False self.status = SIGTERM def read_line(self): assert self.running if self.bufsize == 1: line = self.proc.stdout.readline() else: line = self.proc.stdout.read(self.bufsize) if line == b'': # process exited self.proc.stdout.close() self.status = self.proc.wait() self.proc = None self.running = False else: if self.filter: line = self.filter(line, self.filter_state) if self.inst: self.inst.write(line) def run_procs(procs): while True: pipes = [] active_procs = [] for pp in procs: for p in pp: if p.running: active_procs.append(p) pipes.append(p.proc.stdout) break elif p.status is None and not p.failed and not p.timed_out: p.run() if p.running: active_procs.append(p) pipes.append(p.proc.stdout) break if len(pipes) == 0: # all finished break (i, o, x) = select(pipes, [], [], 1.0) now = int(time.time()) # handle process output for p in active_procs: if p.proc.stdout in i: p.read_line() # handle timeout if p.running and now > p.timeout: output_ts("'%s' timed out" % p.cmdAsStr()) if p.inst: p.inst.write("\n** timeout **\n".encode()) p.timed_out = True p.terminate() def pidof(name): pids = [] for d in [p for p in os.listdir('/proc') if p.isdigit()]: try: if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name: pids.append(int(d)) except: pass return pids def check_space(cap, name, size): global free_disk_space if free_disk_space is not None and size > free_disk_space: output("Omitting %s, out of disk space (requested: %u, allowed: %u)" % (name, size, free_disk_space)) return False elif unlimited_data or caps[cap][MAX_SIZE] == -1 or \ cap_sizes[cap] < caps[cap][MAX_SIZE]: cap_sizes[cap] += size if free_disk_space is not None: free_disk_space -= size return True else: output("Omitting %s, size constraint of %s exceeded" % (name, cap)) return False def get_free_disk_space(path): path = os.path.abspath(path) while not os.path.exists(path): path = os.path.dirname(path) s = os.statvfs(path) return s.f_frsize * s.f_bfree class BytesIOmtime(BytesIO): def __init__(self, buf=b''): BytesIO.__init__(self, buf) self.mtime = time.time() def write(self, s): BytesIO.write(self, s) self.mtime = time.time() if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: print("\nInterrupted.") sys.exit(3) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/000077500000000000000000000000001514270232600241275ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/kernel-info/000077500000000000000000000000001514270232600263405ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/kernel-info/openvswitch.xml000066400000000000000000000014001514270232600314260ustar00rootroot00000000000000 /proc/slabinfo openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/network-status/000077500000000000000000000000001514270232600271415ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/network-status/openvswitch.xml000066400000000000000000000075411514270232600322430ustar00rootroot00000000000000 /usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show ovs-vsctl show ovs-vsctl --no-wait get Open_vSwitch . other_config ovsdb-client -f csv dump unix:/var/run/openvswitch/db.sock Open_vSwitch /usr/share/openvswitch/scripts/ovs-bugtool-fdb-show ovs-appctl lacp/show ovs-appctl cfm/show ovs-appctl bfd/show ovs-appctl dpctl/dump-conntrack ovs-appctl coverage/show ovs-appctl bond/show ovs-appctl memory/show /usr/share/openvswitch/scripts/ovs-bugtool-ovs-appctl-dpif ovs-appctl -t ovsdb-server ovsdb-server/list-dbs ovs-appctl dpctl/dump-flows netdev@ovs-netdev ovs-appctl dpctl/dump-flows -m netdev@ovs-netdev ovs-appctl dpctl/dump-flows system@ovs-system ovs-appctl dpctl/show -s /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "show" /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "dump-flows" /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "dump-ports" /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "dump-groups" /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "dump-group-stats" /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-loop-over-bridges "dump-tlv-map" /usr/share/openvswitch/scripts/ovs-bugtool-get-dpdk-nic-numa ip -s -s link show /usr/share/openvswitch/scripts/ovs-bugtool-get-port-stats openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/system-configuration.xml000066400000000000000000000016601514270232600310450ustar00rootroot00000000000000 openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/system-configuration/000077500000000000000000000000001514270232600303205ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/system-configuration/openvswitch.xml000066400000000000000000000053651514270232600334240ustar00rootroot00000000000000 date --rfc-3339=seconds /usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver ovs-appctl dpif/show dpkg -l | grep openvswitch dpkg -l | grep dpdk grep -H "" /sys/kernel/mm/hugepages/hugepages*/* grep -H "" /sys/devices/system/node/node*/hugepages/hugepages*/* ps -C ovs-vswitchd -f ps -C ovsdb-server -f ps H -C ovs-vswitchd -o 'pid tid cmd comm' ls -la /var/run/openvswitch/ lsof -U ovs-vsctl --version virsh list --all ovs-appctl dpif-netdev/pmd-rxq-show lscpu /usr/share/openvswitch/scripts/ovs-bugtool-ovs-vswitchd-threads-affinity /usr/share/openvswitch/scripts/ovs-bugtool-ovs-bridge-datapath-type /usr/share/openvswitch/scripts/ovs-bugtool-qos-configs /etc/default/openvswitch-switch /etc/default/qemu-kvm /etc/init/openvswitch-switch.conf /etc/init/ovs-delay.conf /etc/init.d/openvswitch-switch /usr/share/openvswitch/scripts/ovs-ctl openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/system-logs/000077500000000000000000000000001514270232600264155ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/bugtool/plugins/system-logs/openvswitch.xml000066400000000000000000000033571514270232600315200ustar00rootroot00000000000000 /etc/openvswitch /var/lib/openvswitch df -h ovs-appctl dpif-netdev/pmd-stats-show | grep pmd ovs-appctl vlog/list journalctl ulimit -a /var/log/syslog /var/log/daemon.log /var/log/upstart/libvirt-bin.log /var/log/upstart/ndevalarm.log /var/log/upstart/nova-compute.log /var/log/upstart/neutron-plugin-openvswitch-agent.log /var/log/upstart/openvswitch-switch.log /var/log/messages* /var/log/secure /var/log/dmesg* openvswitch-3.7.0~git20260211.8c6ebf8/utilities/checkpatch.py000077500000000000000000001310461514270232600234520ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2016, 2017 Red Hat, Inc. # Copyright (c) 2018 Nicira, Inc. # # 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. import email import getopt import os import re import subprocess import sys RETURN_CHECK_INITIAL_STATE = 0 RETURN_CHECK_STATE_WITH_RETURN = 1 RETURN_CHECK_AWAITING_BRACE = 2 EXIT_FAILURE = 1 __errors = 0 __warnings = 0 empty_return_check_state = 0 print_file_name = None check_authors_file = False checking_file = False total_line = 0 colors = False spellcheck = False quiet = False spell_check_dict = None missing_authors = [] codespell_dictionaries = ['dictionary.txt', 'dictionary_code.txt'] __codespell_dict_reset_once = True def open_spell_check_dict(): import enchant codespell_files = [] try: import codespell_lib codespell_dir = os.path.dirname(codespell_lib.__file__) for fn in codespell_dictionaries: fn = os.path.join(codespell_dir, 'data', fn) if os.path.exists(fn): codespell_files.append(fn) except: pass try: global spell_check_dict spell_check_dict = enchant.Dict("en_US") for fn in codespell_files: with open(fn) as f: for line in f.readlines(): words = line.strip().split('>')[1].strip(', ').split(',') for word in words: spell_check_dict.add_to_session(word.strip()) check_dict_file = os.path.join(get_top_directory(), "utilities", "checkpatch_dict.txt") if os.path.exists(check_dict_file): with open(check_dict_file, "r") as project_words: for check_word in project_words: spell_check_dict.add_to_session(check_word.strip()) return True except: return False def get_color_end(): global colors if colors: return "\033[00m" return "" def get_red_begin(): global colors if colors: return "\033[91m" return "" def get_yellow_begin(): global colors if colors: return "\033[93m" return "" def print_error(message): global __errors print("%sERROR%s: %s" % (get_red_begin(), get_color_end(), message)) __errors = __errors + 1 def print_warning(message): global __warnings print("%sWARNING%s: %s" % (get_yellow_begin(), get_color_end(), message)) __warnings = __warnings + 1 def reset_counters(): global __errors, __warnings, total_line __errors = 0 __warnings = 0 total_line = 0 # These are keywords whose names are normally followed by a space and # something in parentheses (usually an expression) then a left curly brace. # # 'do' almost qualifies but it's also used as "do { ... } while (...);". __parenthesized_constructs = 'if|for|while|switch|[_A-Z]+FOR_*EACH[_A-Z0-9]*' __regex_added_line = re.compile(r'^\+{1,2}[^\+][\w\W]*') __regex_subtracted_line = re.compile(r'^\-{1,2}[^\-][\w\W]*') __regex_leading_with_whitespace_at_all = re.compile(r'^\s+') __regex_leading_with_spaces = re.compile(r'^ +[\S]+') __regex_trailing_whitespace = re.compile(r'[^\S]+$') __regex_single_line_feed = re.compile(r'^\f$') __regex_for_if_missing_whitespace = re.compile(r' +(%s)[\(]' % __parenthesized_constructs) __regex_hash_define_for_each = re.compile( r'#define [_A-Z]+FOR_*EACH[_A-Z0-9]*\(') __regex_for_if_too_much_whitespace = re.compile(r' +(%s) +[\(]' % __parenthesized_constructs) __regex_for_if_parens_whitespace = \ re.compile(r' +(%s) \( +[\s\S]+\)' % __parenthesized_constructs) __regex_is_for_if_single_line_bracket = \ re.compile(r'^ +(%s) \(.*\)' % __parenthesized_constructs) __regex_ends_with_bracket = \ re.compile(r'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$') __regex_ptr_declaration_missing_whitespace = re.compile(r'[a-zA-Z0-9]\*[^*]') __regex_cast_missing_whitespace = re.compile(r'\)[a-zA-Z0-9]') __regex_is_comment_line = re.compile(r'^\s*(/\*|\*\s)') __regex_has_comment = re.compile(r'.*(/\*|\*\s)') __regex_has_c99_comment = re.compile(r'.*//.*$') __regex_trailing_operator = re.compile(r'^[^ ]* [^ ]*[?:]$') __regex_conditional_else_bracing = re.compile(r'^\s*else\s*{?$') __regex_conditional_else_bracing2 = re.compile(r'^\s*}\selse\s*$') __regex_has_xxx_mark = re.compile(r'.*xxx.*', re.IGNORECASE) __regex_added_doc_rst = re.compile( r'\ndiff .*Documentation/.*rst\nnew file mode') __regex_empty_return = re.compile(r'\s*return;') __regex_if_macros = re.compile(r'^ +(%s) \([\S]([\s\S]+[\S])*\) { +\\' % __parenthesized_constructs) __regex_nonascii_characters = re.compile("[^\u0000-\u007f]") __regex_efgrep = re.compile(r'.*[ef]grep.*$') skip_leading_whitespace_check = False skip_trailing_whitespace_check = False skip_gerrit_change_id_check = False skip_block_whitespace_check = False skip_signoff_check = False skip_committer_signoff_check = False # Don't enforce character limit on files that include these characters in their # name, as they may have legitimate reasons to have longer lines. # # Python isn't checked as flake8 performs these checks during build. line_length_ignore_list = re.compile( r'\.(am|at|etc|in|m4|mk|patch|py|yml)$|^debian/.*$') # Don't enforce a requirement that leading whitespace be all spaces on # files that include these characters in their name, since these kinds # of files need lines with leading tabs. leading_whitespace_ignore_list = re.compile(r'\.(mk|am|at)$|^debian/.*$') def is_subtracted_line(line): """Returns TRUE if the line in question has been removed.""" return __regex_subtracted_line.search(line) is not None def is_added_line(line): """Returns TRUE if the line in question is an added line. """ global checking_file return __regex_added_line.search(line) is not None or checking_file def added_line(line): """Returns the line formatted properly by removing diff syntax""" global checking_file if not checking_file: return line[1:] return line def leading_whitespace_is_spaces(line): """Returns TRUE if the leading whitespace in added lines is spaces """ if skip_leading_whitespace_check: return True if (__regex_leading_with_whitespace_at_all.search(line) is not None and __regex_single_line_feed.search(line) is None): return __regex_leading_with_spaces.search(line) is not None return True def trailing_whitespace_or_crlf(line): """Returns TRUE if the trailing characters is whitespace """ if skip_trailing_whitespace_check: return False return (__regex_trailing_whitespace.search(line) is not None and __regex_single_line_feed.search(line) is None) def if_and_for_whitespace_checks(line): """Return TRUE if there is appropriate whitespace after if, for, while """ if skip_block_whitespace_check: return True if (__regex_for_if_missing_whitespace.search(line) is not None and __regex_hash_define_for_each.search(line) is None): return False if (__regex_for_if_too_much_whitespace.search(line) is not None or __regex_for_if_parens_whitespace.search(line)): return False return True def if_and_for_end_with_bracket_check(line): """Return TRUE if there is not a bracket at the end of an if, for, while block which fits on a single line ie: 'if (foo)'""" def balanced_parens(line): """This is a rather naive counter - it won't deal with quotes""" balance = 0 for letter in line: if letter == '(': balance += 1 elif letter == ')': balance -= 1 return balance == 0 if __regex_is_for_if_single_line_bracket.search(line) is not None: if not balanced_parens(line): return True if __regex_ends_with_bracket.search(line) is None: if line.endswith("\\") and \ __regex_if_macros.match(line) is not None: return True else: return False if __regex_conditional_else_bracing.match(line) is not None: return False if __regex_conditional_else_bracing2.match(line) is not None: return False return True def pointer_whitespace_check(line): """Return TRUE if there is no space between a pointer name and the asterisk that denotes this is a apionter type, ie: 'struct foo*'""" return __regex_ptr_declaration_missing_whitespace.search(line) is not None def nonascii_character_check(line): """Return TRUE if inappropriate Unicode characters are detected """ return __regex_nonascii_characters.search(line) is not None def cast_whitespace_check(line): """Return TRUE if there is no space between the '()' used in a cast and the expression whose type is cast, i.e.: '(void *)foo'""" return __regex_cast_missing_whitespace.search(line) is not None def line_length_check(line): """Return TRUE if the line length is too long""" if len(line) > 79: print_warning("Line is %d characters long (recommended limit is 79)" % len(line)) return True return False def is_comment_line(line): """Returns TRUE if the current line is part of a block comment.""" return __regex_is_comment_line.match(line) is not None def has_comment(line): """Returns TRUE if the current line contains a comment or is part of a block comment.""" return __regex_has_comment.match(line) is not None def has_c99_comment(line): """Returns TRUE if the current line contains C99 style comment (//).""" return __regex_has_c99_comment.match(line) is not None def trailing_operator(line): """Returns TRUE if the current line ends with an operatorsuch as ? or :""" return __regex_trailing_operator.match(line) is not None def has_xxx_mark(line): """Returns TRUE if the current line contains 'xxx'.""" return __regex_has_xxx_mark.match(line) is not None def has_efgrep(line): """Returns TRUE if the current line contains 'egrep' or 'fgrep'.""" return __regex_efgrep.match(line) is not None def filter_comments(current_line, keep=False): """remove all of the c-style comments in a line""" STATE_NORMAL = 0 STATE_COMMENT_SLASH = 1 STATE_COMMENT_CONTENTS = 3 STATE_COMMENT_END_SLASH = 4 state = STATE_NORMAL sanitized_line = '' check_state = STATE_NORMAL only_whitespace = True if keep: check_state = STATE_COMMENT_CONTENTS for c in current_line: if c == '/': if state == STATE_NORMAL: state = STATE_COMMENT_SLASH elif state == STATE_COMMENT_SLASH: # This is for c++ style comments. We will warn later return sanitized_line[:1] elif state == STATE_COMMENT_END_SLASH: c = '' state = STATE_NORMAL elif c == '*': if only_whitespace: # just assume this is a continuation from the previous line # as a comment state = STATE_COMMENT_END_SLASH elif state == STATE_COMMENT_SLASH: state = STATE_COMMENT_CONTENTS sanitized_line = sanitized_line[:-1] elif state == STATE_COMMENT_CONTENTS: state = STATE_COMMENT_END_SLASH elif state == STATE_COMMENT_END_SLASH: # Need to re-introduce the star from the previous state, since # it may have been clipped by the state check below. c = '*' + c state = STATE_COMMENT_CONTENTS elif state == STATE_COMMENT_SLASH: # Need to re-introduce the slash from the previous state, since # it may have been clipped by the state check below. c = '/' + c state = STATE_NORMAL if state != check_state: c = '' if not c.isspace(): only_whitespace = False sanitized_line += c return sanitized_line NOT_COMMENT = 0 C_COMMENT = 1 PY_COMMENT = 2 def check_spelling(line, comment_type): if not spell_check_dict or not spellcheck: return False is_name_tag = re.compile(r'^\s*([a-z-]+-by): (.*@.*)$', re.I | re.M | re.S) if line.startswith('Fixes: ') or is_name_tag.match(line): return False if comment_type == NOT_COMMENT: words = line elif comment_type == C_COMMENT: words = filter_comments(line, True) elif comment_type == PY_COMMENT: words = "" matched = re.search(r'#([^!].*)$', line) if matched: words = matched.group(1) else: words = line.replace("'''", '').replace('"""', '').strip() else: return False words = words.replace(':', ' ').split(' ') flagged_words = [] num_suggestions = 3 for word in words: skip = False strword = re.subn(r'\W+', '', word)[0].replace(',', '') if (len(strword) and not spell_check_dict.check(strword.lower()) and not spell_check_dict.check(word.lower())): if any([check_char in word for check_char in ['=', '(', '-', '_', '/', '\'']]): skip = True # special case the '.' if '.' in word and not word.endswith('.'): skip = True # skip proper nouns and references to macros if strword.isupper() or (strword[0].isupper() and strword[1:].islower()): skip = True # skip words containing numbers if any(check_char.isdigit() for check_char in strword): skip = True if not skip: flagged_words.append(strword) if len(flagged_words) > 0: for mistake in flagged_words: print_warning("Possible misspelled word: \"%s\"" % mistake) print("Did you mean: ", spell_check_dict.suggest(mistake)[:num_suggestions]) return True return False def __check_doc_is_listed(text, doctype, docdir, docfile): if doctype == 'rst': beginre = re.compile(r'\+\+\+.*{}/index.rst'.format(docdir)) docre = re.compile(r'\n\+.*{}'.format(docfile.replace('.rst', ''))) elif doctype == 'automake': beginre = re.compile(r'\+\+\+.*Documentation/automake.mk') docre = re.compile(r'\n\+\t(?:{}/)?{}'.format(docdir, docfile)) else: raise NotImplementedError("Invalid doctype: {}".format(doctype)) res = beginre.search(text) if res is None: return True hunkstart = res.span()[1] hunkre = re.compile(r'\n(---|\+\+\+) (\S+)') res = hunkre.search(text[hunkstart:]) if res is None: hunkend = len(text) else: hunkend = hunkstart + res.span()[0] hunk = text[hunkstart:hunkend] # find if the file is being added. if docre.search(hunk) is not None: return False return True def __check_new_docs(text, doctype): """Check if the documentation is listed properly. If doctype is 'rst' then the index.rst is checked. If the doctype is 'automake' then automake.mk is checked. Returns TRUE if the new file is not listed.""" failed = False new_docs = __regex_added_doc_rst.findall(text) for doc in new_docs: docpathname = doc.split(' ')[2] gitdocdir, docfile = os.path.split(docpathname.rstrip('\n')) if docfile == "index.rst": continue if gitdocdir.startswith('a/'): docdir = gitdocdir.replace('a/', '', 1) else: docdir = gitdocdir if __check_doc_is_listed(text, doctype, docdir, docfile): if doctype == 'rst': print_warning("New doc {} not listed in {}/index.rst".format( docfile, docdir)) elif doctype == 'automake': print_warning("New doc {} not listed in " "Documentation/automake.mk".format(docfile)) else: raise NotImplementedError("Invalid doctype: {}".format( doctype)) failed = True return failed def check_doc_docs_automake(text): return __check_new_docs(text, 'automake') def check_new_docs_index(text): return __check_new_docs(text, 'rst') def empty_return_with_brace(line): """Returns TRUE if a function contains a return; followed by one or more line feeds and terminates with a '}' at start of line""" def empty_return(line): """Returns TRUE if a function has a 'return;'""" return __regex_empty_return.match(line) is not None global empty_return_check_state if empty_return_check_state == RETURN_CHECK_INITIAL_STATE \ and empty_return(line): empty_return_check_state = RETURN_CHECK_STATE_WITH_RETURN elif empty_return_check_state == RETURN_CHECK_STATE_WITH_RETURN \ and (re.match(r'^}$', line) or len(line) == 0): if re.match('^}$', line): empty_return_check_state = RETURN_CHECK_AWAITING_BRACE else: empty_return_check_state = RETURN_CHECK_INITIAL_STATE if empty_return_check_state == RETURN_CHECK_AWAITING_BRACE: empty_return_check_state = RETURN_CHECK_INITIAL_STATE return True return False file_checks = [ {'regex': __regex_added_doc_rst, 'check': check_new_docs_index}, {'regex': __regex_added_doc_rst, 'check': check_doc_docs_automake} ] checks = [ {'regex': None, 'match_name': lambda x: not line_length_ignore_list.search(x), 'check': lambda x: line_length_check(x)}, {'regex': None, 'match_name': lambda x: not leading_whitespace_ignore_list.search(x), 'check': lambda x: not leading_whitespace_is_spaces(x), 'print': lambda: print_warning("Line has non-spaces leading whitespace")}, {'regex': None, 'match_name': None, 'check': lambda x: trailing_whitespace_or_crlf(x), 'print': lambda: print_warning("Line has trailing whitespace")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: not if_and_for_whitespace_checks(x), 'print': lambda: print_error("Improper whitespace around control block")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: not if_and_for_end_with_bracket_check(x), 'print': lambda: print_error("Inappropriate bracing around statement")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: pointer_whitespace_check(x), 'print': lambda: print_error("Inappropriate spacing in pointer declaration")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'check': lambda x: nonascii_character_check(x), 'print': lambda: print_error("Inappropriate non-ascii characters detected.")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: cast_whitespace_check(x), 'print': lambda: print_error("Inappropriate spacing around cast")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: trailing_operator(x), 'print': lambda: print_error("Line has '?' or ':' operator at end of line")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: has_comment(x), 'check': lambda x: has_xxx_mark(x), 'print': lambda: print_warning("Comment with 'xxx' marker")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': lambda x: has_c99_comment(x), 'print': lambda: print_error("C99 style comment")}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: has_comment(x), 'check': lambda x: check_spelling(x, comment_type=C_COMMENT)}, {'regex': r'\.py(\.in)?$', 'match_name': None, 'prereq': lambda x: "#" in x or "'''" in x or '"""' in x, 'check': lambda x: check_spelling(x, PY_COMMENT)}, {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'check': lambda x: empty_return_with_brace(x), 'interim_line': True, 'print': lambda: print_warning("Empty return followed by brace, consider omitting") }, {'regex': r'(\.at|\.sh)$', 'match_name': None, 'check': lambda x: has_efgrep(x), 'print': lambda: print_error("grep -E/-F should be used instead of egrep/fgrep")}, {'regex': 'AUTHORS.rst$', 'match_name': None, 'check': lambda x: update_missing_authors(x), 'print': None}, ] def regex_function_factory(func_name): regex = re.compile(r'\b%s\([^)]*\)' % func_name) return lambda x: regex.search(x) is not None def regex_error_factory(description): return lambda: print_error(description) def regex_warn_factory(description): return lambda: print_warning(description) std_functions = [ ('malloc', 'Use xmalloc() in place of malloc()'), ('calloc', 'Use xcalloc() in place of calloc()'), ('realloc', 'Use xrealloc() in place of realloc()'), ('strdup', 'Use xstrdup() in place of strdup()'), ('asprintf', 'Use xasprintf() in place of asprintf()'), ('vasprintf', 'Use xvasprintf() in place of vasprintf()'), ('strcpy', 'Use ovs_strlcpy() in place of strcpy()'), ('strlcpy', 'Use ovs_strlcpy() in place of strlcpy()'), ('strncpy', 'Use ovs_strzcpy() in place of strncpy()'), ('strerror', 'Use ovs_strerror() in place of strerror()'), ('sleep', 'Use xsleep() in place of sleep()'), ('abort', 'Use ovs_abort() in place of abort()'), ('assert', 'Use ovs_assert() in place of assert()'), ('error', 'Use ovs_error() in place of error()'), ] checks += [ {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': regex_function_factory(function_name), 'print': regex_error_factory(description)} for (function_name, description) in std_functions] easy_to_misuse_api = [ ('ovsrcu_barrier', ['lib/ovs-rcu.c'], 'Are you sure you need to use ovsrcu_barrier(), ' 'in most cases ovsrcu_synchronize() will be fine?'), ('netdev_features_to_bps', ['lib/netdev.c', 'lib/netdev-bsd.c', 'lib/netdev-linux.c'], 'Are you sure you need to use netdev_features_to_bps()? ' 'If you want to retrieve the current and/or maximum link speed, ' 'consider using netdev_get_speed() instead.'), ] checks += [ {'regex': r'(\.c)(\.in)?$', 'match_name': lambda x, loc=locations: x not in loc, 'prereq': lambda x: not is_comment_line(x), 'check': regex_function_factory(function_name), 'print': regex_warn_factory(description)} for (function_name, locations, description) in easy_to_misuse_api] def regex_operator_factory(operator): regex = re.compile(r'^[^#][^"\']*[^ "]%s[^ "\'][^"]*' % operator) return lambda x: regex.search(filter_comments(x)) is not None infix_operators = \ [re.escape(op) for op in ['%', '<<', '>>', '<=', '>=', '==', '!=', '^', '|', '&&', '||', '?:', '=', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '|=', '<<=', '>>=']] \ + [r'[^<" ]<[^=" ]', r'[^\->" ]>[^=" ]', r'[^ !()/"\*]\*+[^/]', r'[^ !&()"]&', r'[^" +(]\+[^"+;]', r'[^" \-(]\-[^"\->;]', r'[^" <>=!^|+\-*/%&]=[^"=]', r'[^* ]/[^* ]'] checks += [ {'regex': r'(\.c|\.h)(\.in)?$', 'match_name': None, 'prereq': lambda x: not is_comment_line(x), 'check': regex_operator_factory(operator), 'print': lambda: print_warning("Line lacks whitespace around operator")} for operator in infix_operators] def get_file_type_checks(filename): """Returns the list of checks for a file based on matching the filename against regex.""" global checks checkList = [] for check in checks: regex_check = True match_check = True if check['regex'] is None and check['match_name'] is None: checkList.append(check) continue if check['regex'] is not None and \ re.compile(check['regex']).search(filename) is None: regex_check = False if check['match_name'] is not None and \ not check['match_name'](filename): match_check = False if regex_check and match_check: checkList.append(check) return checkList def run_checks(current_file, line, lineno): """Runs the various checks for the particular line. This will take filename into account.""" global checking_file, total_line print_line = False for check in get_file_type_checks(current_file): if 'prereq' in check and not check['prereq'](line): continue if check['check'](line): if 'print' in check: check['print']() print_line = True if print_line: if checking_file: print("%s:%d:" % (current_file, lineno)) else: print("#%d FILE: %s:%d:" % (total_line, current_file, lineno)) print("%s\n" % line) def interim_line_check(current_file, line, lineno): """Runs the various checks for the particular interim line. This will take filename into account, and will check for the 'interim_line' key before running the check.""" global checking_file, total_line print_line = False for check in get_file_type_checks(current_file): if 'prereq' in check and not check['prereq'](line): continue if 'interim_line' in check and check['interim_line']: if check['check'](line): if 'print' in check: check['print']() print_line = True if print_line: if checking_file: print("%s:%d:" % (current_file, lineno)) else: print("#%d FILE: %s:%d:" % (total_line, current_file, lineno)) print("%s\n" % line) def run_file_checks(text): """Runs the various checks for the text.""" for check in file_checks: if check['regex'].search(text) is not None: check['check'](text) def run_subject_checks(subject, spellcheck=False): warnings = False if spellcheck and check_spelling(subject, comment_type=NOT_COMMENT): warnings = True summary = subject[subject.rindex(': ') + 2:] area_summary = subject[subject.index(': ') + 2:] area_summary_len = len(area_summary) if area_summary_len > 70: print_warning("The subject, ': ', is over 70 " "characters, i.e., %u." % area_summary_len) warnings = True if summary[0].isalpha() and summary[0].islower(): print_warning( "The subject summary should start with a capital.") warnings = True if subject[-1] not in [".", "?", "!"]: print_warning( "The subject summary should end with a dot.") warnings = True if warnings: print(subject) return warnings def get_top_directory(): result = subprocess.run('git rev-parse --show-toplevel', stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, shell=True) if result and result.returncode == 0: return result.stdout.decode('utf-8').strip() return os.getenv('OVS_SRC_DIR', '.') def update_missing_authors(diffed_line): global missing_authors for author in missing_authors: m = re.search(r'<(.*?)>', author) if not m: continue pattern = r'\b' + re.escape(m.group(1)) + r'\b' if re.search(pattern, diffed_line) is None: continue else: missing_authors.remove(author) return False def do_authors_exist(authors): authors = list(set(authors)) missing_authors = [] try: authors_file = get_top_directory() + "/AUTHORS.rst" with open(authors_file, "r", encoding="utf-8") as file: file_content = file.read() for author in authors: m = re.search(r'<(.*?)>', author) if not m: continue pattern = r'\b' + re.escape(m.group(1)) + r'\b' if re.search(pattern, file_content) is None: missing_authors.append(author) except FileNotFoundError: print_error("Could not open AUTHORS.rst in '%s/'!" % get_top_directory()) return missing_authors def ovs_checkpatch_parse(text, filename, author=None, committer=None): global print_file_name, total_line, checking_file, \ empty_return_check_state, missing_authors PARSE_STATE_HEADING = 0 PARSE_STATE_DIFF_HEADER = 1 PARSE_STATE_CHANGE_BODY = 2 lineno = 0 signatures = [] co_authors = [] parse = 0 current_file = filename if checking_file else '' previous_file = '' seppatch = re.compile(r'^---([\w]*| \S+)$') hunks = re.compile(r'^(---|\+\+\+) (\S+)') hunk_differences = re.compile( r'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@') is_author = re.compile(r'^(Author|From): (.*)$', re.I | re.M | re.S) is_committer = re.compile(r'^(Commit: )(.*)$', re.I | re.M | re.S) is_subject = re.compile(r'^(Subject: )(.*)$', re.I | re.M | re.S) is_signature = re.compile(r'^(Signed-off-by: )(.*)$', re.I | re.M | re.S) is_co_author = re.compile(r'^(Co-authored-by: )(.*)$', re.I | re.M | re.S) is_gerrit_change_id = re.compile(r'(\s*(change-id: )(.*))$', re.I | re.M | re.S) is_fixes = re.compile(r'(\s*(Fixes:)(.*))$', re.I | re.M | re.S) is_fixes_exact = re.compile(r'^Fixes: [0-9a-f]{12} \(".*"\)$') tags_typos = { r'^Acked by:': 'Acked-by:', r'^Reported at:': 'Reported-at:', r'^Reported by:': 'Reported-by:', r'^Requested by:': 'Requested-by:', r'^Reviewed by:': 'Reviewed-by:', r'^Submitted at:': 'Submitted-at:', r'^Suggested by:': 'Suggested-by:', } reset_counters() for line in text.split("\n"): if current_file != previous_file: previous_file = current_file lineno = lineno + 1 total_line = total_line + 1 if line == "\f": # Form feed continue if len(line) <= 0: continue if checking_file: parse = PARSE_STATE_CHANGE_BODY if parse == PARSE_STATE_DIFF_HEADER: match = hunks.match(line) if match: parse = PARSE_STATE_CHANGE_BODY current_file = match.group(2)[2:] print_file_name = current_file continue elif parse == PARSE_STATE_HEADING: if seppatch.match(line): parse = PARSE_STATE_DIFF_HEADER if not skip_signoff_check: # Check that the patch has an author, that the # author is not among the co-authors, and that the # co-authors are unique. if not author: print_error("Patch lacks author.") continue if " via " in author or "@openvswitch.org" in author: print_error("Author should not be mailing list.") continue if author in co_authors: print_error("Author should not be also be co-author.") continue if len(set(co_authors)) != len(co_authors): print_error("Duplicate co-author.") # Check that the author, all co-authors, and the # committer (if any) signed off. if author not in signatures: print_error("Author %s needs to sign off." % author) for ca in co_authors: if ca not in signatures: print_error("Co-author %s needs to sign off." % ca) break if (committer and author != committer and committer not in signatures and not skip_committer_signoff_check): print_error("Committer %s needs to sign off." % committer) # Check for signatures that we do not expect. # This is only a warning because there can be, # rarely, a signature chain. # # If we don't have a known committer, and there is # a single extra sign-off, then do not warn # because that extra sign-off is probably the # committer. extra_sigs = [x for x in signatures if x not in co_authors and x != author and x != committer] if len(extra_sigs) > 1 or (committer and extra_sigs): print_warning("Unexpected sign-offs from developers " "who are not authors or co-authors or " "committers: %s" % ", ".join(extra_sigs)) if check_authors_file: missing_authors = do_authors_exist(missing_authors + co_authors + [author]) elif is_committer.match(line): committer = is_committer.match(line).group(2) elif is_author.match(line): author = is_author.match(line).group(2) elif is_subject.match(line): run_subject_checks(line, spellcheck) elif is_signature.match(line): m = is_signature.match(line) signatures.append(m.group(2)) elif is_co_author.match(line): m = is_co_author.match(line) co_authors.append(m.group(2)) elif (is_gerrit_change_id.match(line) and not skip_gerrit_change_id_check): print_error( "Remove Gerrit Change-Id's before submitting upstream.") print("%d: %s\n" % (lineno, line)) elif is_fixes.match(line) and not is_fixes_exact.match(line): print_error('"Fixes" tag is malformed.\n' 'Use the following format:\n' ' git log -1 ' '--pretty=format:"Fixes: %h (\\\"%s\\\")" ' '--abbrev=12 COMMIT_REF\n') print("%d: %s\n" % (lineno, line)) elif spellcheck: check_spelling(line, comment_type=NOT_COMMENT) for typo, correct in tags_typos.items(): m = re.match(typo, line, re.I) if m: print_error("%s tag is malformed." % (correct[:-1])) print("%d: %s\n" % (lineno, line)) elif parse == PARSE_STATE_CHANGE_BODY: newfile = hunks.match(line) if newfile: current_file = newfile.group(2)[2:] print_file_name = current_file continue reset_line_number = hunk_differences.match(line) if reset_line_number: empty_return_check_state = RETURN_CHECK_INITIAL_STATE lineno = int(reset_line_number.group(3)) if lineno < 0: lineno = -1 * lineno lineno -= 1 if is_subtracted_line(line): lineno -= 1 continue cmp_line = added_line(line) if not is_added_line(line): interim_line_check(current_file, cmp_line, lineno) continue # Skip files which have /datapath in them, since they are # linux or windows coding standards if current_file.startswith('datapath'): continue if current_file.startswith('include/linux'): continue # "sparse" includes could be copy-pasted from different sources # like DPDK or Linux and could contain workarounds not suitable # for a common style. if current_file.startswith('include/sparse'): continue if current_file.startswith('utilities/bugtool'): continue run_checks(current_file, cmp_line, lineno) run_file_checks(text) if __errors or __warnings: return EXIT_FAILURE return 0 def usage(): print("""\ Open vSwitch checkpatch.py Checks a patch for trivial mistakes. usage: %s [options] [PATCH1 [PATCH2 ...] | -f SOURCE1 [SOURCE2 ...] | -1 | -2 | ...] Input options: -f|--check-file Arguments are source files, not patches. -1, -2, ... Check recent commits in this repo. Check options: -h|--help This help message -a|--check-authors-file Check AUTHORS file for existence of the authors. Should be used by commiters only! -b|--skip-block-whitespace Skips the if/while/for whitespace tests -D|--dictionaries DICTIONARY Specify codespell dictionaries. -l|--skip-leading-whitespace Skips the leading whitespace test -q|--quiet Only print error and warning information -s|--skip-signoff-lines Tolerate missing Signed-off-by line -S|--spellcheck Check C comments and commit-message for possible spelling mistakes -t|--skip-trailing-whitespace Skips the trailing whitespace test --skip-gerrit-change-id Skips the gerrit change id test --skip-committer-signoff Skips the committer sign-off test""" % sys.argv[0]) def ovs_checkpatch_print_result(): global quiet, __warnings, __errors, total_line if __errors or __warnings: print("Lines checked: %d, Warnings: %d, Errors: %d\n" % (total_line, __warnings, __errors)) elif not quiet: print("Lines checked: %d, no obvious problems found\n" % (total_line)) def ovs_checkpatch_print_missing_authors(): if missing_authors: if len(missing_authors) == 1: print_warning("Author '%s' is not in the AUTHORS.rst file!" % missing_authors[0]) else: print_warning("Authors '%s' are not in the AUTHORS.rst file!" % ', '.join(missing_authors)) return True return False def ovs_checkpatch_file(filename): try: mail = email.message_from_file(open(filename, 'r', encoding='utf8')) except: print_error("Unable to parse file '%s'. Is it a patch?" % filename) return -1 for part in mail.walk(): if part.get_content_maintype() == 'multipart': continue result = ovs_checkpatch_parse(part.get_payload(decode=False), filename, mail.get('Author', mail['From']), mail['Commit']) if not mail['Subject'] or not mail['Subject'].strip(): if mail['Subject']: mail.replace_header('Subject', sys.argv[-1]) else: mail.add_header('Subject', sys.argv[-1]) if not checking_file: print("Subject missing! Your provisional subject is", mail['Subject']) if not checking_file and run_subject_checks('Subject: ' + mail['Subject'], spellcheck): result = True ovs_checkpatch_print_result() if ovs_checkpatch_print_missing_authors(): result = True return result def partition(pred, iterable): """Returns [[trues], [falses]], where [trues] is the items in 'iterable' that satisfy 'pred' and [falses] is all the rest.""" trues = [] falses = [] for item in iterable: if pred(item): trues.append(item) else: falses.append(item) return trues, falses if __name__ == '__main__': try: numeric_options, args = partition(lambda s: re.match('-[0-9]+$', s), sys.argv[1:]) n_patches = int(numeric_options[-1][1:]) if numeric_options else 0 optlist, args = getopt.getopt(args, 'abD:hlstfSq', ["check-file", "help", "check-authors-file", "dictionaries=", "skip-block-whitespace", "skip-leading-whitespace", "skip-signoff-lines", "skip-trailing-whitespace", "skip-gerrit-change-id", "skip-committer-signoff", "spellcheck", "quiet"]) except: print("Unknown option encountered. Please rerun with -h for help.") sys.exit(EXIT_FAILURE) for o, a in optlist: if o in ("-h", "--help"): usage() sys.exit(0) elif o in ("-a", "--check-authors-file"): check_authors_file = True elif o in ("-D", "--dictionaries"): if __codespell_dict_reset_once: __codespell_dict_reset_once = False codespell_dictionaries = [] codespell_dictionaries.append(a) elif o in ("-b", "--skip-block-whitespace"): skip_block_whitespace_check = True elif o in ("-l", "--skip-leading-whitespace"): skip_leading_whitespace_check = True elif o in ("-s", "--skip-signoff-lines"): skip_signoff_check = True elif o in ("-t", "--skip-trailing-whitespace"): skip_trailing_whitespace_check = True elif o in ("--skip-gerrit-change-id"): skip_gerrit_change_id_check = True elif o in ("--skip-committer-signoff"): skip_committer_signoff_check = True elif o in ("-f", "--check-file"): checking_file = True elif o in ("-S", "--spellcheck"): spellcheck = True elif o in ("-q", "--quiet"): quiet = True else: print("Unknown option '%s'" % o) sys.exit(EXIT_FAILURE) if sys.stdout.isatty(): colors = True if spellcheck and not open_spell_check_dict(): print("WARNING: The enchant library isn't available.") print(" Please install python enchant.") spellcheck = False if n_patches: status = 0 git_log = 'git log --no-color --no-merges --pretty=format:"%H %s" ' with os.popen(git_log + '-%d' % n_patches, 'r') as f: commits = f.read().split("\n") for i in reversed(range(0, n_patches)): revision, name = commits[i].split(" ", 1) f = os.popen('''git format-patch -1 --stdout --pretty=format:"\ Author: %an <%ae> Commit: %cn <%ce> Subject: %s %b" ''' + revision, 'r') patch = f.read() f.close() if not quiet: print('== Checking %s ("%s") ==' % (revision[0:12], name)) result = ovs_checkpatch_parse(patch, revision) if result: status = EXIT_FAILURE if ovs_checkpatch_print_missing_authors(): status = EXIT_FAILURE sys.exit(status) if not args: if sys.stdin.isatty(): usage() sys.exit(EXIT_FAILURE) result = ovs_checkpatch_parse(sys.stdin.read(), '-') ovs_checkpatch_print_result() if ovs_checkpatch_print_missing_authors(): result = EXIT_FAILURE sys.exit(result) status = 0 for filename in args: if not quiet: print('== Checking "%s" ==' % filename) result = ovs_checkpatch_file(filename) if result: status = EXIT_FAILURE sys.exit(status) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/checkpatch_dict.txt000066400000000000000000000042411514270232600246350ustar00rootroot00000000000000acl addresssanitizer afxdp alg alloc amd64 api apis appctl appveyor arg arp asan attr backport backtrace bfd bitmask bool cacheline cgtime char checkpatch checksum chmod ci cirrus cksum cmap comparator conn conntrack const cpu cpus cstime csum cutime cvlan datapath datapaths dbg debian decap decapsulation defrag defragment deref dereference dest dhcp dhcpv4 dhcpv6 dnat dns dpcls dpctl dpdk dpif dst eip enablement encap enum enums eol epasv erspan ether ethernet ethertype ethtool eventmask faq fastpath fd fdb free freebsd FreeBSD gbps gcc gcloud geneve gid gigabits gigabytes github glibc goto gre gtime gw hashmap hashmaps hmap hotplug http https hugepage hugepages hw hwol icmp icmp4 icmpv6 idl ifdef ifindex initializer inlined int ip ipf ipfix ips ipsec ipv4 ipv6 ixgbe json jsonrpc kbps kilobits kilobytes l2 l3 l4 lacp libbpf libcrypto libgcc libopenvswitch libreswan libssl libxdp linux liveness lldp llvm localnet lockless loopback malloc mbps mbuf mbufs mbundle mbundles mcast megabits megabytes megaflow megaflows memcmp mempool mempools memset metadata mfex miniflow misconfiguration misconfigured ms msec msg msgs mtu multicast mutex namespace nat nated natting ncat netdev netdevs netflow netlink networkmanager nic nicira nics ns nsec num numa odp ofctl ofpbuf ofpbufs ofport ofproto openflow openssl openvswitch opts ovs ovs-ctl ovsdb ovs-vswitchd pasv pcap pedit perf pgid pid pidfile pkts pmd pmds policer ppid pps pre prio promisc qdisc qos qsort rculist rebalance rebased recirc recirculation recirculations revalidate revalidation revalidator revalidators rhel rip rss rsslim rst rx rxq sbrec sed selinux sendmmsg sendmsg sflow shrinked sid sizeof skb skiplist slowpath smap snat src stats stderr stdout stime strcasecmp strcmp struct subnet subtable syscall tc tcp tcp4 tcpv4 testpmd tftp timeval tlv tlvs tnl tpid travis trie tso tunctl tuple tx txq ubsan udp udp4 udpv4 ufid uid ukey umask unassociated unclosed unicast unixctl upcall upcalls us usec userspace util utime uuid valgrind vconn vconns veth vhost vhostuser virtio virtqueue vlan vlans vms vnet vport vports vsize vswitch vswitchd vtep vxlan vxlans wc wget whcan wildcard x86_64 xbundle xbundles xenserver xlate xlated openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/000077500000000000000000000000001514270232600222425ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/Makefile000066400000000000000000000011241514270232600237000ustar00rootroot00000000000000#export OVS_BRANCH=branch-2.11 #export OVS_VERSION=2.11 #export KERNEL_VERSION=4.15.0-54-generic #export DISTRO=debian #export GITHUB_SRC=https://github.com/openvswitch/ovs.git #export DOCKER_REPO=openvswitch/ovs # Example: # make build # make push REPO = ${DOCKER_REPO} tag = ${OVS_VERSION}_${DISTRO}_${KERNEL_VERSION} build: ;docker build -t ${REPO}:${tag} --build-arg DISTRO=${DISTRO} \ --build-arg OVS_BRANCH=${OVS_BRANCH} \ --build-arg KERNEL_VERSION=${KERNEL_VERSION} \ --build-arg GITHUB_SRC=${GITHUB_SRC} -f ${DISTRO}/Dockerfile . .PHONY: build push: ;docker push ${REPO}:${tag} openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/create_ovs_db.sh000077500000000000000000000012031514270232600253740ustar00rootroot00000000000000#!/bin/sh # # 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. ovsdb-tool create /etc/openvswitch/conf.db \ /usr/share/openvswitch/vswitch.ovsschemaopenvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/debian/000077500000000000000000000000001514270232600234645ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/debian/Dockerfile000066400000000000000000000010711514270232600254550ustar00rootroot00000000000000FROM ubuntu:16.04 MAINTAINER "Aliasgar Ginwala" ARG OVS_BRANCH ARG KERNEL_VERSION ARG GITHUB_SRC ARG DISTRO copy $DISTRO/build-kernel-modules.sh /build-kernel-modules.sh RUN /build-kernel-modules.sh $KERNEL_VERSION $OVS_BRANCH $GITHUB_SRC COPY create_ovs_db.sh /etc/openvswitch/create_ovs_db.sh RUN /etc/openvswitch/create_ovs_db.sh COPY ovs-override.conf /etc/depmod.d/openvswitch.conf COPY start-ovs /bin/start-ovs VOLUME ["/var/log/openvswitch", "/var/lib/openvswitch",\ "/var/run/openvswitch", "/etc/openvswitch"] ENTRYPOINT ["start-ovs"] openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/debian/build-kernel-modules.sh000077500000000000000000000026531514270232600300540ustar00rootroot00000000000000#!/bin/sh # # 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. KERNEL_VERSION=host OVS_BRANCH=$2 GITHUB_SRC=$3 # Install deps build_deps="apt-utils libelf-dev build-essential libssl-dev python3 \ wget gdb autoconf libtool git automake bzip2 debhelper dh-autoreconf openssl" apt-get update if [ $KERNEL_VERSION != "host" ]; then linux="linux-image-$KERNEL_VERSION linux-headers-$KERNEL_VERSION" apt-get install -y ${linux} fi apt-get install -y ${build_deps} # get the source mkdir /build; cd /build git clone --depth 1 -b $OVS_BRANCH $GITHUB_SRC cd ovs # build and install ./boot.sh config="./configure --localstatedir="/var" --sysconfdir="/etc" --prefix="/usr" --enable-ssl" eval $config make -j8; make install # remove deps to make the container light weight. apt-get remove --purge -y ${build_deps} apt-get autoremove -y --purge cd ..; rm -rf ovs basic_utils="vim kmod net-tools uuid-runtime iproute2" apt-get install -y ${basic_utils} openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/ovs-override.conf000066400000000000000000000001241514270232600255320ustar00rootroot00000000000000override openvswitch * extra override vport-geneve * extra override vport-* * extra openvswitch-3.7.0~git20260211.8c6ebf8/utilities/docker/start-ovs000077500000000000000000000046531514270232600241420ustar00rootroot00000000000000#!/bin/bash # # 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. case $1 in "ovsdb-server") /usr/share/openvswitch/scripts/ovs-ctl start \ --system-id=random --no-ovs-vswitchd /usr/share/openvswitch/scripts/ovs-ctl stop ovsdb-server --pidfile /etc/openvswitch/conf.db \ -vconsole:emer -vsyslog:err -vfile:info \ --remote=punix:/var/run/openvswitch/db.sock \ --private-key=db:Open_vSwitch,SSL,private_key \ --certificate=db:Open_vSwitch,SSL,certificate \ --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ --log-file=/var/log/openvswitch/ovsdb-server.log \ --no-chdir ;; "ovs-vswitchd") depmod -a modprobe openvswitch modprobe vport_geneve /usr/share/openvswitch/scripts/ovs-ctl \ --no-ovsdb-server start /usr/share/openvswitch/scripts/ovs-ctl \ --no-ovsdb-server force-reload-kmod /usr/share/openvswitch/scripts/ovs-ctl stop ovs-vswitchd --pidfile -vconsole:emer -vsyslog:err \ -vfile:info --mlockall --no-chdir \ --log-file=/var/log/openvswitch/ovs-vswitchd.log ;; "ovs-vswitchd-host") /usr/share/openvswitch/scripts/ovs-ctl \ --no-ovsdb-server start /usr/share/openvswitch/scripts/ovs-ctl stop ovs-vswitchd --pidfile -vconsole:emer \ -vsyslog:err -vfile:info --mlockall --no-chdir \ --log-file=/var/log/openvswitch/ovs-vswitchd.log ;; *) echo "$0 [ovsdb-server|ovs-vswitchd|ovs-vswitchd-host]" esacopenvswitch-3.7.0~git20260211.8c6ebf8/utilities/gdb/000077500000000000000000000000001514270232600215275ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/gdb/ovs_gdb.py000066400000000000000000001575321514270232600235410ustar00rootroot00000000000000# # Copyright (c) 2018 Eelco Chaudron # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version # 2 of the License, or (at your option) any later version. # # Files name: # ovs_gdb.py # # Description: # GDB commands and functions for Open vSwitch debugging # # Author: # Eelco Chaudron # # Initial Created: # 23 April 2018 # # Notes: # It implements the following GDB commands: # - ovs_dump_bridge {ports|wanted} # - ovs_dump_bridge_ports # - ovs_dump_dp_netdev [ports] # - ovs_dump_dp_netdev_poll_threads # - ovs_dump_dp_netdev_ports # - ovs_dump_dp_provider # - ovs_dump_netdev # - ovs_dump_netdev_provider # - ovs_dump_nla {dump} {enum type} # - ovs_dump_ovs_list {[] [] {dump}]} # - ovs_dump_packets [tcpdump options] # - ovs_dump_cmap {[] [] {dump}]} # - ovs_dump_hmap {dump} # - ovs_dump_simap # - ovs_dump_smap # - ovs_dump_udpif_keys {|} {short} # - ovs_show_fdb {[] {dbg} {hash}} # - ovs_show_upcall {dbg} # - ovs_dump_conntrack_conns {short} # # Example: # $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd) # (gdb) source ./utilities/gdb/ovs_gdb.py # # (gdb) ovs_dump_ # ovs_dump_bridge ovs_dump_bridge_ports ovs_dump_dp_netdev # ovs_dump_dp_netdev_ports ovs_dump_netdev # # (gdb) ovs_dump_bridge # (struct bridge *) 0x5615471ed2e0: name = br2, type = system # (struct bridge *) 0x561547166350: name = br0, type = system # (struct bridge *) 0x561547216de0: name = ovs_pvp_br0, type = netdev # (struct bridge *) 0x5615471d0420: name = br1, type = system # # (gdb) p *(struct bridge *) 0x5615471d0420 # $1 = {node = {hash = 24776443, next = 0x0}, name = 0x5615471cca90 "br1", # type = 0x561547163bb0 "system", # ... # ... # import gdb import struct import sys import uuid try: from scapy.layers.l2 import Ether from scapy.utils import tcpdump except ModuleNotFoundError: Ether = None tcpdump = None # # Global #define's from OVS which might need updating based on a version. # N_UMAPS = 512 # # The container_of code below is a copied from the Linux kernel project file, # scripts/gdb/linux/utils.py. It has the following copyright header: # # # gdb helper commands and functions for Linux kernel debugging # # # # common utilities # # # # Copyright (c) Siemens AG, 2011-2013 # # # # Authors: # # Jan Kiszka # # # # This work is licensed under the terms of the GNU GPL version 2. # class CachedType(object): def __init__(self, name): self._type = None self._name = name def _new_objfile_handler(self, event): self._type = None gdb.events.new_objfile.disconnect(self._new_objfile_handler) def get_type(self): if self._type is None: self._type = gdb.lookup_type(self._name) if self._type is None: raise gdb.GdbError( "cannot resolve type '{0}'".format(self._name)) if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'): gdb.events.new_objfile.connect(self._new_objfile_handler) return self._type long_type = CachedType("long") def get_long_type(): global long_type return long_type.get_type() def offset_of(typeobj, field): element = gdb.Value(0).cast(typeobj) return int(str(element[field].address).split()[0], 16) def container_of(ptr, typeobj, member): return (ptr.cast(get_long_type()) - offset_of(typeobj, member)).cast(typeobj) def get_global_variable(name): var = gdb.lookup_symbol(name)[0] if var is None or not var.is_variable: print("Can't find {} global variable, are you sure " "you are debugging OVS?".format(name)) return None return gdb.parse_and_eval(name) def get_time_msec(): # There is no variable that stores the current time each iteration, # to get a decent time time_now() value. For now we take the global # "coverage_run_time" value, which is the current time + max 5 seconds # (COVERAGE_RUN_INTERVAL) return int(get_global_variable("coverage_run_time")), -5000 def get_time_now(): # See get_time_msec() above return int(get_global_variable("coverage_run_time")) / 1000, -5 def eth_addr_to_string(eth_addr): return "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format( int(eth_addr['ea'][0]), int(eth_addr['ea'][1]), int(eth_addr['ea'][2]), int(eth_addr['ea'][3]), int(eth_addr['ea'][4]), int(eth_addr['ea'][5])) # # Simple class to print a spinner on the console # class ProgressIndicator(object): def __init__(self, message=None): self.spinner = "/-\\|" self.spinner_index = 0 self.message = message if self.message is not None: print(self.message, end='') def update(self): print("{}\b".format(self.spinner[self.spinner_index]), end='') sys.stdout.flush() self.spinner_index += 1 if self.spinner_index >= len(self.spinner): self.spinner_index = 0 def pause(self): print("\r\033[K", end='') def resume(self): if self.message is not None: print(self.message, end='') self.update() def done(self): self.pause() # # Class that will provide an iterator over an OVS cmap. # class ForEachCMAP(object): def __init__(self, cmap, typeobj=None, member='node'): self.cmap = cmap self.first = True self.typeobj = typeobj self.member = member # Cursor values self.node = 0 self.bucket_idx = 0 self.entry_idx = 0 def __iter__(self): return self def __get_CMAP_K(self): ptr_type = gdb.lookup_type("void").pointer() return (64 - 4) / (4 + ptr_type.sizeof) def __next(self): ipml = self.cmap['impl']['p'] if self.node != 0: self.node = self.node['next']['p'] if self.node != 0: return while self.bucket_idx <= ipml['mask']: buckets = ipml['buckets'][self.bucket_idx] while self.entry_idx < self.__get_CMAP_K(): self.node = buckets['nodes'][self.entry_idx]['next']['p'] self.entry_idx += 1 if self.node != 0: return self.bucket_idx += 1 self.entry_idx = 0 raise StopIteration def __next__(self): ipml = self.cmap['impl']['p'] if ipml['n'] == 0: raise StopIteration self.__next() if self.typeobj is None: return self.node return container_of(self.node, gdb.lookup_type(self.typeobj).pointer(), self.member) def next(self): return self.__next__() # # Class that will provide an iterator over an OVS hmap. # class ForEachHMAP(object): def __init__(self, hmap, typeobj=None, member='node'): self.hmap = hmap self.node = None self.first = True self.typeobj = typeobj self.member = member def __iter__(self): return self def __next(self, start): for i in range(start, (self.hmap['mask'] + 1)): self.node = self.hmap['buckets'][i] if self.node != 0: return raise StopIteration def __next__(self): # # In the real implementation the n values is never checked, # however when debugging we do, as we might try to access # a hmap that has been cleared/hmap_destroy(). # if self.hmap['n'] <= 0: raise StopIteration if self.first: self.first = False self.__next(0) elif self.node['next'] != 0: self.node = self.node['next'] else: self.__next((self.node['hash'] & self.hmap['mask']) + 1) if self.typeobj is None: return self.node return container_of(self.node, gdb.lookup_type(self.typeobj).pointer(), self.member) def next(self): return self.__next__() # # Class that will provide an iterator over an Netlink attributes # class ForEachNL(object): def __init__(self, nlattrs, nlattrs_len): self.attr = nlattrs.cast(gdb.lookup_type('struct nlattr').pointer()) self.attr_len = int(nlattrs_len) def __iter__(self): return self def round_up(self, val, round_to): return int(val) + (round_to - int(val)) % round_to def __next__(self): if self.attr is None or \ self.attr_len < 4 or self.attr['nla_len'] < 4 or \ self.attr['nla_len'] > self.attr_len: # # Invalid attr set, maybe we should raise an exception? # raise StopIteration attr = self.attr self.attr_len -= self.round_up(attr['nla_len'], 4) self.attr = self.attr.cast(gdb.lookup_type('void').pointer()) \ + self.round_up(attr['nla_len'], 4) self.attr = self.attr.cast(gdb.lookup_type('struct nlattr').pointer()) return attr def next(self): return self.__next__() # # Class that will provide an iterator over an OVS shash. # class ForEachSHASH(ForEachHMAP): def __init__(self, shash, typeobj=None): self.data_typeobj = typeobj super(ForEachSHASH, self).__init__(shash['map'], "struct shash_node", "node") def __next__(self): node = super(ForEachSHASH, self).__next__() if self.data_typeobj is None: return node return node['data'].cast(gdb.lookup_type(self.data_typeobj).pointer()) def next(self): return self.__next__() # # Class that will provide an iterator over an OVS simap. # class ForEachSIMAP(ForEachHMAP): def __init__(self, shash): super(ForEachSIMAP, self).__init__(shash['map'], "struct simap_node", "node") def __next__(self): node = super(ForEachSIMAP, self).__next__() return node['name'], node['data'] def next(self): return self.__next__() # # Class that will provide an iterator over an OVS smap. # class ForEachSMAP(ForEachHMAP): def __init__(self, shash): super(ForEachSMAP, self).__init__(shash['map'], "struct smap_node", "node") def __next__(self): node = super(ForEachSMAP, self).__next__() return node['key'], node['value'] def next(self): return self.__next__() # # Class that will provide an iterator over an OVS list. # class ForEachLIST(object): def __init__(self, list, typeobj=None, member='node'): self.list = list self.node = list self.typeobj = typeobj self.member = member def __iter__(self): return self def __next__(self): if self.list.address == self.node['next']: raise StopIteration self.node = self.node['next'] if self.typeobj is None: return self.node return container_of(self.node, gdb.lookup_type(self.typeobj).pointer(), self.member) def next(self): return self.__next__() # # Class that will provide an iterator over an OFPACTS. # class ForEachOFPACTS(object): def __init__(self, ofpacts, ofpacts_len): self.ofpact = ofpacts.cast(gdb.lookup_type('struct ofpact').pointer()) self.length = int(ofpacts_len) def __round_up(self, val, round_to): return int(val) + (round_to - int(val)) % round_to def __iter__(self): return self def __next__(self): if self.ofpact is None or self.length <= 0: raise StopIteration ofpact = self.ofpact length = self.__round_up(ofpact['len'], 8) self.length -= length self.ofpact = self.ofpact.cast( gdb.lookup_type('void').pointer()) + length self.ofpact = self.ofpact.cast( gdb.lookup_type('struct ofpact').pointer()) return ofpact def next(self): return self.__next__() # # Implements the GDB "ovs_dump_bridges" command # class CmdDumpBridge(gdb.Command): """Dump all configured bridges. Usage: ovs_dump_bridge {ports|wanted} """ def __init__(self): super(CmdDumpBridge, self).__init__("ovs_dump_bridge", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): ports = False wanted = False arg_list = gdb.string_to_argv(arg) if len(arg_list) > 1 or \ (len(arg_list) == 1 and arg_list[0] != "ports" and arg_list[0] != "wanted"): print("usage: ovs_dump_bridge {ports|wanted}") return elif len(arg_list) == 1: if arg_list[0] == "ports": ports = True else: wanted = True all_bridges = get_global_variable('all_bridges') if all_bridges is None: return for node in ForEachHMAP(all_bridges, "struct bridge", "node"): print("(struct bridge *) {}: name = {}, type = {}". format(node, node['name'].string(), node['type'].string())) if ports: for port in ForEachHMAP(node['ports'], "struct port", "hmap_node"): CmdDumpBridgePorts.display_single_port(port, 4) if wanted: for port in ForEachSHASH(node['wanted_ports'], typeobj="struct ovsrec_port"): print(" (struct ovsrec_port *) {}: name = {}". format(port, port['name'].string())) # print port.dereference() # # Implements the GDB "ovs_dump_bridge_ports" command # class CmdDumpBridgePorts(gdb.Command): """Dump all ports added to a specific struct bridge*. Usage: ovs_dump_bridge_ports """ def __init__(self): super(CmdDumpBridgePorts, self).__init__("ovs_dump_bridge_ports", gdb.COMMAND_DATA) @staticmethod def display_single_port(port, indent=0): indent = " " * indent port = port.cast(gdb.lookup_type('struct port').pointer()) print("{}(struct port *) {}: name = {}, bridge = (struct bridge *) {}". format(indent, port, port['name'].string(), port['bridge'])) indent += " " * 4 for iface in ForEachLIST(port['ifaces'], "struct iface", "port_elem"): print("{}(struct iface *) {}: name = {}, ofp_port = {}, " "netdev = (struct netdev *) {}". format(indent, iface, iface['name'], iface['ofp_port'], iface['netdev'])) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 1: print("usage: ovs_dump_bridge_ports ") return bridge = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct bridge').pointer()) for node in ForEachHMAP(bridge['ports'], "struct port", "hmap_node"): self.display_single_port(node) # # Implements the GDB "ovs_dump_dp_netdev" command # class CmdDumpDpNetdev(gdb.Command): """Dump all registered dp_netdev structures. Usage: ovs_dump_dp_netdev [ports] """ def __init__(self): super(CmdDumpDpNetdev, self).__init__("ovs_dump_dp_netdev", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): ports = False arg_list = gdb.string_to_argv(arg) if len(arg_list) > 1 or \ (len(arg_list) == 1 and arg_list[0] != "ports"): print("usage: ovs_dump_dp_netdev [ports]") return elif len(arg_list) == 1: ports = True dp_netdevs = get_global_variable('dp_netdevs') if dp_netdevs is None: return for dp in ForEachSHASH(dp_netdevs, typeobj=('struct dp_netdev')): print("(struct dp_netdev *) {}: name = {}, class = " "(struct dpif_class *) {}". format(dp, dp['name'].string(), dp['class'])) if ports: for node in ForEachHMAP(dp['ports'], "struct dp_netdev_port", "node"): CmdDumpDpNetdevPorts.display_single_port(node, 4) # # Implements the GDB "ovs_dump_dp_netdev_poll_threads" command # class CmdDumpDpNetdevPollThreads(gdb.Command): """Dump all poll_thread info added to a specific struct dp_netdev*. Usage: ovs_dump_dp_netdev_poll_threads """ def __init__(self): super(CmdDumpDpNetdevPollThreads, self).__init__( "ovs_dump_dp_netdev_poll_threads", gdb.COMMAND_DATA) @staticmethod def display_single_poll_thread(pmd_thread, indent=0): indent = " " * indent print("{}(struct dp_netdev_pmd_thread *) {}: core_id = {}, " "numa_id {}".format(indent, pmd_thread, pmd_thread['core_id'], pmd_thread['numa_id'])) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 1: print("usage: ovs_dump_dp_netdev_poll_threads " "") return dp_netdev = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct dp_netdev').pointer()) for node in ForEachCMAP(dp_netdev['poll_threads'], "struct dp_netdev_pmd_thread", "node"): self.display_single_poll_thread(node) # # Implements the GDB "ovs_dump_dp_netdev_ports" command # class CmdDumpDpNetdevPorts(gdb.Command): """Dump all ports added to a specific struct dp_netdev*. Usage: ovs_dump_dp_netdev_ports """ def __init__(self): super(CmdDumpDpNetdevPorts, self).__init__("ovs_dump_dp_netdev_ports", gdb.COMMAND_DATA) @staticmethod def display_single_port(port, indent=0): indent = " " * indent print("{}(struct dp_netdev_port *) {}:".format(indent, port)) print("{} port_no = {}, n_rxq = {}, type = {}". format(indent, port['port_no'], port['n_rxq'], port['type'].string())) print("{} netdev = (struct netdev *) {}: name = {}, " "n_txq/rxq = {}/{}". format(indent, port['netdev'], port['netdev']['name'].string(), port['netdev']['n_txq'], port['netdev']['n_rxq'])) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 1: print("usage: ovs_dump_dp_netdev_ports ") return dp_netdev = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct dp_netdev').pointer()) for node in ForEachHMAP(dp_netdev['ports'], "struct dp_netdev_port", "node"): # print node.dereference() self.display_single_port(node) # # Implements the GDB "ovs_dump_dp_provider" command # class CmdDumpDpProvider(gdb.Command): """Dump all registered registered_dpif_class structures. Usage: ovs_dump_dp_provider """ def __init__(self): super(CmdDumpDpProvider, self).__init__("ovs_dump_dp_provider", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): dp_providers = get_global_variable('dpif_classes') if dp_providers is None: return for dp_class in ForEachSHASH(dp_providers, typeobj="struct registered_dpif_class"): print("(struct registered_dpif_class *) {}: " "(struct dpif_class *) 0x{:x} = {{type = {}, ...}}, " "refcount = {}". format(dp_class, int(dp_class['dpif_class']), dp_class['dpif_class']['type'].string(), dp_class['refcount'])) # # Implements the GDB "ovs_dump_netdev" command # class CmdDumpNetdev(gdb.Command): """Dump all registered netdev structures. Usage: ovs_dump_netdev """ def __init__(self): super(CmdDumpNetdev, self).__init__("ovs_dump_netdev", gdb.COMMAND_DATA) @staticmethod def display_single_netdev(netdev, indent=0): indent = " " * indent print("{}(struct netdev *) {}: name = {:15}, auto_classified = {}, " "netdev_class = {}". format(indent, netdev, netdev['name'].string(), netdev['auto_classified'], netdev['netdev_class'])) def invoke(self, arg, from_tty): netdev_shash = get_global_variable('netdev_shash') if netdev_shash is None: return for netdev in ForEachSHASH(netdev_shash, "struct netdev"): self.display_single_netdev(netdev) # # Implements the GDB "ovs_dump_netdev_provider" command # class CmdDumpNetdevProvider(gdb.Command): """Dump all registered netdev providers. Usage: ovs_dump_netdev_provider """ def __init__(self): super(CmdDumpNetdevProvider, self).__init__("ovs_dump_netdev_provider", gdb.COMMAND_DATA) @staticmethod def is_class_vport_class(netdev_class): netdev_class = netdev_class.cast( gdb.lookup_type('struct netdev_class').pointer()) vport_construct = gdb.lookup_symbol('netdev_vport_construct')[0] if netdev_class['construct'] == vport_construct.value(): return True return False @staticmethod def display_single_netdev_provider(reg_class, indent=0): indent = " " * indent print("{}(struct netdev_registered_class *) {}: refcnt = {},". format(indent, reg_class, reg_class['refcnt'])) print("{} (struct netdev_class *) 0x{:x} = {{type = {}, " "is_pmd = {}, ...}}, ". format(indent, int(reg_class['class']), reg_class['class']['type'].string(), reg_class['class']['is_pmd'])) if CmdDumpNetdevProvider.is_class_vport_class(reg_class['class']): vport = container_of( reg_class['class'], gdb.lookup_type('struct vport_class').pointer(), 'netdev_class') if vport['dpif_port'] != 0: dpif_port = vport['dpif_port'].string() else: dpif_port = "\"\"" print("{} (struct vport_class *) 0x{:x} = " "{{ dpif_port = {}, ... }}". format(indent, int(vport), dpif_port)) def invoke(self, arg, from_tty): netdev_classes = get_global_variable('netdev_classes') if netdev_classes is None: return for reg_class in ForEachCMAP(netdev_classes, "struct netdev_registered_class", "cmap_node"): self.display_single_netdev_provider(reg_class) # # Implements the GDB "ovs_dump_ovs_list" command # class CmdDumpOvsList(gdb.Command): """Dump all nodes of an ovs_list give Usage: ovs_dump_ovs_list {[] [] {dump}]} For example dump all the none quiescent OvS RCU threads: (gdb) ovs_dump_ovs_list &ovsrcu_threads (struct ovs_list *) 0x1400 (struct ovs_list *) 0xcc00 (struct ovs_list *) 0x6806 This is not very useful, so please use this with the container_of mode: (gdb) set $threads = &ovsrcu_threads (gdb) ovs_dump_ovs_list $threads 'struct ovsrcu_perthread' list_node (struct ovsrcu_perthread *) 0x1400 (struct ovsrcu_perthread *) 0xcc00 (struct ovsrcu_perthread *) 0x6806 Now you can manually use the print command to show the content, or use the dump option to dump the structure for all nodes: (gdb) ovs_dump_ovs_list $threads 'struct ovsrcu_perthread' list_node dump (struct ovsrcu_perthread *) 0x1400 = {list_node = {prev = 0x48e80 , next = 0xcc00}, mutex... (struct ovsrcu_perthread *) 0xcc00 = {list_node = {prev = 0x1400, next = 0x6806}, mutex ... (struct ovsrcu_perthread *) 0x6806 = {list_node = {prev = 0xcc00, next = 0x48e80 }, ... """ def __init__(self): super(CmdDumpOvsList, self).__init__("ovs_dump_ovs_list", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) typeobj = None member = None dump = False if len(arg_list) != 1 and len(arg_list) != 3 and len(arg_list) != 4: print("usage: ovs_dump_ovs_list " "{[] [] {dump}]}") return header = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct ovs_list').pointer()) if len(arg_list) >= 3: typeobj = arg_list[1] member = arg_list[2] if len(arg_list) == 4 and arg_list[3] == "dump": dump = True for node in ForEachLIST(header.dereference()): if typeobj is None or member is None: print("(struct ovs_list *) {}".format(node)) else: print("({} *) {} {}".format( typeobj, container_of(node, gdb.lookup_type(typeobj).pointer(), member), "=" if dump else "")) if dump: print(" {}\n".format(container_of( node, gdb.lookup_type(typeobj).pointer(), member).dereference())) # # Implements the GDB "ovs_dump_cmap" command # class CmdDumpCmap(gdb.Command): """Dump all nodes of a given cmap Usage: ovs_dump_cmap {[] [] {dump}]} For example dump all the rules in a dpcls_subtable: (gdb) ovs_dump_cmap &subtable->rules (struct cmap *) 0x3e02758 This is not very useful, so please use this with the container_of mode: (gdb) ovs_dump_cmap &subtable->rules "struct dpcls_rule" cmap_node (struct dpcls_rule *) 0x3e02758 Now you can manually use the print command to show the content, or use the dump option to dump the structure for all nodes: (gdb) ovs_dump_cmap &subtable->rules "struct dpcls_rule" cmap_node dump (struct dpcls_rule *) 0x3e02758 = {cmap_node = {next = {p = 0x0}}, mask = 0x3dfe100, flow = {hash = ... """ def __init__(self): super(CmdDumpCmap, self).__init__("ovs_dump_cmap", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) typeobj = None member = None dump = False if len(arg_list) != 1 and len(arg_list) != 3 and len(arg_list) != 4: print("usage: ovs_dump_cmap " "{[] [] {dump}]}") return cmap = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct cmap').pointer()) if len(arg_list) >= 3: typeobj = arg_list[1] member = arg_list[2] if len(arg_list) == 4 and arg_list[3] == "dump": dump = True for node in ForEachCMAP(cmap.dereference()): if typeobj is None or member is None: print("(struct cmap *) {}".format(node)) else: print("({} *) {} {}".format( typeobj, container_of(node, gdb.lookup_type(typeobj).pointer(), member), "=" if dump else "")) if dump: print(" {}\n".format(container_of( node, gdb.lookup_type(typeobj).pointer(), member).dereference())) # # Implements the GDB "ovs_dump_hmap" command # class CmdDumpHmap(gdb.Command): """Dump all nodes of a given hmap Usage: ovs_dump_hmap {dump} For example dump all the bridges when the all_bridges variable is optimized out due to LTO: (gdb) ovs_dump_hmap "&'all_bridges.lto_priv.0'" "struct bridge" "node" (struct bridge *) 0x55ec43069c70 (struct bridge *) 0x55ec430428a0 (struct bridge *) 0x55ec430a55f0 The 'dump' option will also include the full structure content in the output. """ def __init__(self): super(CmdDumpHmap, self).__init__("ovs_dump_hmap", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) typeobj = None member = None dump = False if len(arg_list) != 3 and len(arg_list) != 4: print("usage: ovs_dump_hmap " " {dump}") return hmap = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct hmap').pointer()) typeobj = arg_list[1] member = arg_list[2] if len(arg_list) == 4 and arg_list[3] == "dump": dump = True for node in ForEachHMAP(hmap.dereference(), typeobj, member): print("({} *) {} {}".format(typeobj, node, "=" if dump else "")) if dump: print(" {}\n".format(node.dereference())) # # Implements the GDB "ovs_dump_simap" command # class CmdDumpSimap(gdb.Command): """Dump all key, value entries of a simap Usage: ovs_dump_simap """ def __init__(self): super(CmdDumpSimap, self).__init__("ovs_dump_simap", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 1: print("ERROR: Missing argument!\n") print(self.__doc__) return simap = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct simap').pointer()) values = dict() max_name_len = 0 for name, value in ForEachSIMAP(simap.dereference()): values[name.string()] = int(value) if len(name.string()) > max_name_len: max_name_len = len(name.string()) for name in sorted(values.keys()): print("{}: {} / 0x{:x}".format(name.ljust(max_name_len), values[name], values[name])) # # Implements the GDB "ovs_dump_smap" command # class CmdDumpSmap(gdb.Command): """Dump all key, value pairs of a smap Usage: ovs_dump_smap """ def __init__(self): super(CmdDumpSmap, self).__init__("ovs_dump_smap", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 1: print("ERROR: Missing argument!\n") print(self.__doc__) return smap = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct smap').pointer()) values = dict() max_key_len = 0 for key, value in ForEachSMAP(smap.dereference()): values[key.string()] = value.string() if len(key.string()) > max_key_len: max_key_len = len(key.string()) for key in sorted(values.keys()): print("{}: {}".format(key.ljust(max_key_len), values[key])) # # Implements the GDB "ovs_dump_udpif_keys" command # class CmdDumpUdpifKeys(gdb.Command): """Dump all nodes of an ovs_list give Usage: ovs_dump_udpif_keys {|} {short} : Full name of the udpif's dpif to dump : Address of the udpif structure to dump. If both the and are omitted the available udpif structures are displayed. short : Only dump ukey structure addresses, no content details no_count : Do not count the number of ukeys, as it might be slow """ def __init__(self): super(CmdDumpUdpifKeys, self).__init__("ovs_dump_udpif_keys", gdb.COMMAND_DATA) def count_all_ukeys(self, udpif): count = 0 spinner = ProgressIndicator("Counting all ukeys: ") for j in range(0, N_UMAPS): spinner.update() count += udpif['ukeys'][j]['cmap']['impl']['p']['n'] spinner.done() return count def dump_all_ukeys(self, udpif, indent=0, short=False): indent = " " * indent spinner = ProgressIndicator("Walking ukeys: ") for j in range(0, N_UMAPS): spinner.update() if udpif['ukeys'][j]['cmap']['impl']['p']['n'] != 0: spinner.pause() print("{}(struct umap *) {}:". format(indent, udpif['ukeys'][j].address)) for ukey in ForEachCMAP(udpif['ukeys'][j]['cmap'], "struct udpif_key", "cmap_node"): base_str = "{} (struct udpif_key *) {}: ". \ format(indent, ukey) if short: print(base_str) continue print("{}key_len = {}, mask_len = {}". format(base_str, ukey['key_len'], ukey['mask_len'])) indent_b = " " * len(base_str) if ukey['ufid_present']: print("{}ufid = {}". format( indent_b, str(uuid.UUID( "{:08x}{:08x}{:08x}{:08x}". format(int(ukey['ufid']['u32'][3]), int(ukey['ufid']['u32'][2]), int(ukey['ufid']['u32'][1]), int(ukey['ufid']['u32'][0])))))) print("{}hash = 0x{:8x}, pmd_id = {}". format(indent_b, int(ukey['hash']), ukey['pmd_id'])) print("{}state = {}".format(indent_b, ukey['state'])) print("{}n_packets = {}, n_bytes = {}". format(indent_b, ukey['stats']['n_packets'], ukey['stats']['n_bytes'])) print("{}used = {}, tcp_flags = 0x{:04x}". format(indent_b, ukey['stats']['used'], int(ukey['stats']['tcp_flags']))) # # TODO: Would like to add support for dumping key, mask # actions, and xlate_cache # # key = "" # for nlattr in ForEachNL(ukey['key'], ukey['key_len']): # key += "{}{}".format( # "" if len(key) == 0 else ", ", # nlattr['nla_type'].cast( # gdb.lookup_type('enum ovs_key_attr'))) # print("{}key attributes = {}".format(indent_b, key)) spinner.resume() spinner.done() def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) all_udpifs = get_global_variable('all_udpifs') no_count = "no_count" in arg_list if all_udpifs is None: return udpifs = dict() for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"): udpifs[udpif['dpif']['full_name'].string()] = udpif if len(arg_list) == 0 or ( len(arg_list) == 1 and arg_list[0] == "no_count"): print("(struct udpif *) {}: name = {}, total keys = {}". format(udpif, udpif['dpif']['full_name'].string(), self.count_all_ukeys(udpif) if not no_count else "")) if len(arg_list) == 0 or ( len(arg_list) == 1 and arg_list[0] == "no_count"): return if arg_list[0] in udpifs: udpif = udpifs[arg_list[0]] else: try: udpif = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct udpif').pointer()) except Exception: udpif = None if udpif is None: print("Can't find provided udpif address!") return self.dump_all_ukeys(udpif, 0, "short" in arg_list[1:]) # # Implements the GDB "ovs_show_fdb" command # class CmdShowFDB(gdb.Command): """Show FDB information Usage: ovs_show_fdb { {dbg} {hash}} : Optional bridge name, if not supplied FDB summary information is displayed for all bridges. dbg : Will show structure address information hash : Will display the forwarding table using the hash table, rather than the rlu list. """ def __init__(self): super(CmdShowFDB, self).__init__("ovs_show_fdb", gdb.COMMAND_DATA) @staticmethod def __get_port_name_num(mac_entry): if mac_entry['mlport'] is not None: port = mac_entry['mlport']['port'].cast( gdb.lookup_type('struct ofbundle').pointer()) port_name = port['name'].string() port_no = int(container_of( port['ports']['next'], gdb.lookup_type('struct ofport_dpif').pointer(), 'bundle_node')['up']['ofp_port']) if port_no == 0xfff7: port_no = "UNSET" elif port_no == 0xfff8: port_no = "IN_PORT" elif port_no == 0xfff9: port_no = "TABLE" elif port_no == 0xfffa: port_no = "NORMAL" elif port_no == 0xfffb: port_no = "FLOOD" elif port_no == 0xfffc: port_no = "ALL" elif port_no == 0xfffd: port_no = "CONTROLLER" elif port_no == 0xfffe: port_no = "LOCAL" elif port_no == 0xffff: port_no = "NONE" else: port_no = str(port_no) else: port_name = "-" port_no = "?" return port_name, port_no @staticmethod def display_ml_summary(ml, indent=0, dbg=False): indent = " " * indent if ml is None: return if dbg: print("[(struct mac_learning *) {}]".format(ml)) print("{}table.n : {}".format(indent, ml['table']['n'])) print("{}secret : 0x{:x}".format(indent, int(ml['secret']))) print("{}idle_time : {}".format(indent, ml['idle_time'])) print("{}max_entries : {}".format(indent, ml['max_entries'])) print("{}ref_count : {}".format(indent, ml['ref_cnt']['count'])) print("{}need_revalidate : {}".format(indent, ml['need_revalidate'])) print("{}ports_by_ptr.n : {}".format(indent, ml['ports_by_ptr']['n'])) print("{}ports_by_usage.n: {}".format(indent, ml['ports_by_usage']['n'])) print("{}total_learned : {}".format(indent, ml['total_learned'])) print("{}total_expired : {}".format(indent, ml['total_expired'])) print("{}total_evicted : {}".format(indent, ml['total_evicted'])) print("{}total_moved : {}".format(indent, ml['total_moved'])) @staticmethod def display_mac_entry(mac_entry, indent=0, dbg=False): port_name, port_no = CmdShowFDB.__get_port_name_num(mac_entry) line = "{}{:16.16} {:-4} {} {:-9}".format( indent, "{}[{}]".format(port_no, port_name), int(mac_entry['vlan']), eth_addr_to_string(mac_entry['mac']), int(mac_entry['expires'])) if dbg: line += " [(struct mac_entry *) {}]".format(mac_entry) print(line) @staticmethod def display_ml_entries(ml, indent=0, hash=False, dbg=False): indent = " " * indent if ml is None: return print("\n{}FDB \"{}\" table:".format(indent, "lrus" if not hash else "hash")) print("{}port VLAN MAC Age out @". format(indent)) print("{}----------------- ---- ----------------- ---------". format(indent)) mac_entries = 0 if hash: for mac_entry in ForEachHMAP(ml['table'], "struct mac_entry", "hmap_node"): CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg) mac_entries += 1 else: for mac_entry in ForEachLIST(ml['lrus'], "struct mac_entry", "lru_node"): CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg) mac_entries += 1 print("\nTotal MAC entries: {}".format(mac_entries)) time_now = list(get_time_now()) time_now[1] = time_now[0] + time_now[1] print("\n{}Current time is between {} and {} seconds.\n". format(indent, min(time_now[0], time_now[1]), max(time_now[0], time_now[1]))) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) all_ofproto_dpifs_by_name = get_global_variable( 'all_ofproto_dpifs_by_name') if all_ofproto_dpifs_by_name is None: return all_name = dict() max_name_len = 0 for node in ForEachHMAP(all_ofproto_dpifs_by_name, "struct ofproto_dpif", "all_ofproto_dpifs_by_name_node"): all_name[node['up']['name'].string()] = node if len(node['up']['name'].string()) > max_name_len: max_name_len = len(node['up']['name'].string()) if len(arg_list) == 0: for name in sorted(all_name.keys()): print("{}: (struct mac_learning *) {}". format(name.ljust(max_name_len), all_name[name]['ml'])) self.display_ml_summary(all_name[name]['ml'], 4) else: if not arg_list[0] in all_name: print("ERROR: Given bridge name is not known!") return ml = all_name[arg_list[0]]['ml'] self.display_ml_summary(ml, 0, "dbg" in arg_list[1:]) self.display_ml_entries(ml, 0, "hash" in arg_list[1:], "dbg" in arg_list[1:]) # # Implements the GDB "ovs_show_fdb" command # class CmdShowUpcall(gdb.Command): """Show upcall information Usage: ovs_show_upcall {dbg} dbg : Will show structure address information """ def __init__(self): super(CmdShowUpcall, self).__init__("ovs_show_upcall", gdb.COMMAND_DATA) @staticmethod def display_udpif_upcall(udpif, indent=0, dbg=False): indent = " " * indent enable_ufid = get_global_variable('enable_ufid') if enable_ufid is None: return dbg_str = "" if dbg: dbg_str = ", ((struct udpif *) {})".format(udpif) print("{}{}{}:".format( indent, udpif['dpif']['full_name'].string(), dbg_str)) print("{} flows : (current {}) (avg {}) (max {}) (limit {})". format(indent, udpif['n_flows'], udpif['avg_n_flows'], udpif['max_n_flows'], udpif['flow_limit'])) print("{} dump duration : {}ms". format(indent, udpif['dump_duration'])) print("{} ufid enabled : {}\n". format(indent, enable_ufid & udpif['backer']['rt_support']['ufid'])) for i in range(0, int(udpif['n_revalidators'])): revalidator = udpif['revalidators'][i] dbg_str = "" if dbg: dbg_str = ", ((struct revalidator *) {})".\ format(revalidator.address) count = 0 j = i spinner = ProgressIndicator("Counting all ukeys: ") while j < N_UMAPS: spinner.update() count += udpif['ukeys'][j]['cmap']['impl']['p']['n'] j += int(udpif['n_revalidators']) spinner.done() print("{} {}: (keys {}){}". format(indent, revalidator['id'], count, dbg_str)) print("") def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) all_udpifs = get_global_variable('all_udpifs') if all_udpifs is None: return for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"): self.display_udpif_upcall(udpif, 0, "dbg" in arg_list) # # Implements the GDB "ovs_dump_ofpacts" command # class CmdDumpOfpacts(gdb.Command): """Dump all actions in an ofpacts set Usage: ovs_dump_ofpacts : Pointer to set of ofpact structures. : Total length of the set. Example dumping all actions when in the clone_xlate_actions() function: (gdb) ovs_dump_ofpacts actions actions_len (struct ofpact *) 0x87c8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24} (struct ofpact *) 0x87e0: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24} (struct ofpact *) 0x87f8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24} (struct ofpact *) 0x8810: {type = OFPACT_SET_FIELD, raw = 255 '', len = 32} (struct ofpact *) 0x8830: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24} (struct ofpact *) 0x8848: {type = OFPACT_RESUBMIT, raw = 38 '&', len = 16} """ def __init__(self): super(CmdDumpOfpacts, self).__init__("ovs_dump_ofpacts", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) != 2: print("usage: ovs_dump_ofpacts ") return ofpacts = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct ofpact').pointer()) length = gdb.parse_and_eval(arg_list[1]) for node in ForEachOFPACTS(ofpacts, length): print("(struct ofpact *) {}: {}".format(node, node.dereference())) # # Implements the GDB "ovs_dump_packets" command # class CmdDumpPackets(gdb.Command): """Dump metadata about dp_packets Usage: ovs_dump_packets [tcpdump options] This command can take either a dp_packet_batch struct and print out metadata about all packets in this batch, or a single dp_packet struct and print out metadata about a single packet. Everything after the struct reference is passed into tcpdump. If nothing is passed in as a tcpdump option, the default is "-n". (gdb) ovs_dump_packets packets_ 12:01:05.981214 ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.1.1 is-at a6:0f:c3:f0:5f:bd (oui Unknown), length 28 """ def __init__(self): super().__init__("ovs_dump_packets", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): if Ether is None: print("ERROR: This command requires scapy to be installed.") return arg_list = gdb.string_to_argv(arg) if len(arg_list) == 0: print("Usage: ovs_dump_packets [tcpdump options]") return symb_name = arg_list[0] tcpdump_args = arg_list[1:] if not tcpdump_args: # Add a sane default tcpdump_args = ["-n"] val = gdb.parse_and_eval(symb_name) while val.type.code == gdb.TYPE_CODE_PTR: val = val.dereference() pkt_list = [] if str(val.type).startswith("struct dp_packet_batch"): for idx in range(val['count']): pkt_struct = val['packets'][idx].dereference() pkt = self.extract_pkt(pkt_struct) if pkt is None: continue pkt_list.append(pkt) elif str(val.type) == "struct dp_packet": pkt = self.extract_pkt(val) if pkt is None: return pkt_list.append(pkt) else: print("Error, unsupported argument type: {}".format(str(val.type))) return stdout = tcpdump(pkt_list, args=tcpdump_args, getfd=True, quiet=True) gdb.write(stdout.read().decode("utf8", "replace")) def extract_pkt(self, pkt): pkt_fields = pkt.type.keys() if pkt['packet_type'] != 0: return if pkt['l3_ofs'] == 0xFFFF: return if "mbuf" in pkt_fields: if pkt['mbuf']['data_off'] == 0xFFFF: return eth_ptr = pkt['mbuf']['buf_addr'] eth_off = int(pkt['mbuf']['data_off']) eth_len = int(pkt['mbuf']['pkt_len']) else: if pkt['data_ofs'] == 0xFFFF: return eth_ptr = pkt['base_'] eth_off = int(pkt['data_ofs']) eth_len = int(pkt['size_']) if eth_ptr == 0 or eth_len < 1: return # Extract packet pkt_ptr = eth_ptr.cast( gdb.lookup_type('uint8_t').pointer() ) pkt_ptr += eth_off pkt_data = [] for idx in range(eth_len): pkt_data.append(int(pkt_ptr[idx])) pkt_data = struct.pack("{}B".format(eth_len), *pkt_data) packet = Ether(pkt_data) packet.len = int(eth_len) return packet # # Implements the GDB "ovs_dump_conntrack_conns" command # class CmdDumpDpConntrackConn(gdb.Command): """Dump all connections in a conntrack set Usage: ovs_dump_conntrack_conns {short} : Pointer to conntrack short : Only dump conn structure addresses, no content details Example dumping all connections: (gdb) ovs_dump_conntrack_conns 0x5606339c25e0 (struct conn *) 0x7f32c000a8c0: expiration = ... nw_proto = 1 (struct conn *) 0x7f32c00489d0: expiration = ... nw_proto = 6 (struct conn *) 0x7f32c0153bb0: expiration = ... nw_proto = 17 (gdb) ovs_dump_conntrack_conns 0x5606339c25e0 short (struct conn *) 0x7f32c000a8c0 (struct conn *) 0x7f32c00489d0 (struct conn *) 0x7f32c0153bb0 """ def __init__(self): super(CmdDumpDpConntrackConn, self).__init__( "ovs_dump_conntrack_conns", gdb.COMMAND_DATA) @staticmethod def display_single_conn(conn, dir_, indent=0, short=False): indent = " " * indent if short: print("{}(struct conn *) {}".format(indent, conn)) else: print("{}(struct conn *) {}: expiration = {}, mark = {}, " "dl_type = {}, zone = {}, nw_proto = {}".format( indent, conn, conn['expiration'], conn['mark'], conn['key_node'][dir_]['key']['dl_type'], conn['key_node'][dir_]['key']['zone'], conn['key_node'][dir_]['key']['nw_proto'])) def invoke(self, arg, from_tty): arg_list = gdb.string_to_argv(arg) if len(arg_list) not in (1, 2) or \ (len(arg_list) == 2 and arg_list[1] != "short"): print("usage: ovs_dump_conntrack_conns " "{short}") return ct = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct conntrack').pointer()) for key_node in ForEachCMAP(ct["conns"], "struct conn_key_node", "cm_node"): node = container_of( key_node, gdb.lookup_type('struct conn').pointer(), "key_node") self.display_single_conn(node, key_node['dir'], short="short" in arg_list[1:]) # # Implements the GDB "ovs_dump_nla" command # class CmdDumpNla(gdb.Command): """Dump all Netlink attributes. Usage: ovs_dump_nla {dump} {enum type} This is an example dumping some actions: (gdb) ovs_dump_nla 0x7f10e35d88b4 80 ovs_action_attr (struct nlattr *) 0x7f10e35d88b4:[OVS_ACTION_ATTR_METER] {nla_len = 8, ... (struct nlattr *) 0x7f10e35d88bc:[OVS_ACTION_ATTR_SET] {nla_len = 20, ... (struct nlattr *) 0x7f10e35d88d0:[OVS_ACTION_ATTR_SET] {nla_len = 32, ... (struct nlattr *) 0x7f10e35d88f0:[OVS_ACTION_ATTR_PUSH_VLAN] {nla_len ... (struct nlattr *) 0x7f10e35d88f8:[OVS_ACTION_ATTR_OUTPUT] {nla_len = 8, ... """ def __init__(self): super(CmdDumpNla, self).__init__("ovs_dump_nla", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): attr_size = gdb.lookup_type("struct nlattr").sizeof arg_list = gdb.string_to_argv(arg) dump = False enum = None if len(arg_list) not in (2, 3, 4): print("ERROR: Invalid arguments!\n") print(self.__doc__) return if len(arg_list) >= 3: for i in range(2, len(arg_list)): if arg_list[i] == "dump": dump = True else: enum = arg_list[i] nla = gdb.parse_and_eval(arg_list[0]).cast( gdb.lookup_type('struct nlattr').pointer()) length = gdb.parse_and_eval(arg_list[1]) for attr in ForEachNL(nla, length): if enum is not None: hdr = "[{}] {}, nl_attr_get() = {}". \ format(attr['nla_type'].cast( gdb.lookup_type('enum ' + arg_list[2])), attr.dereference(), attr + 1) else: hdr = " {}, nl_attr_get() = {}".format(attr.dereference(), attr + 1) if dump: mem = gdb.selected_inferior().read_memory(attr + 1, attr['nla_len'] - attr_size) dump = ": " + " ".join('{:02x}'.format(b) for b in bytes(mem)) else: dump = "" print("(struct nlattr *) {}:{}{}".format(attr, hdr, dump)) # # Initialize all GDB commands # CmdDumpBridge() CmdDumpBridgePorts() CmdDumpDpNetdev() CmdDumpDpNetdevPollThreads() CmdDumpDpNetdevPorts() CmdDumpDpProvider() CmdDumpNetdev() CmdDumpNetdevProvider() CmdDumpNla() CmdDumpOfpacts() CmdDumpOvsList() CmdDumpPackets() CmdDumpCmap() CmdDumpHmap() CmdDumpSimap() CmdDumpSmap() CmdDumpUdpifKeys() CmdShowFDB() CmdShowUpcall() CmdDumpDpConntrackConn() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/nlmon.c000066400000000000000000000117241514270232600222670ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include "netlink.h" #include "netlink-socket.h" #include "netnsid.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/poll-loop.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" static const struct nl_policy rtnlgrp_link_policy[] = { [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, }; int main(int argc OVS_UNUSED, char *argv[]) { uint64_t buf_stub[4096 / 64]; struct nl_sock *sock; int nsid; struct ofpbuf buf; int error; set_program_name(argv[0]); vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG); error = nl_sock_create(NETLINK_ROUTE, &sock); if (error) { ovs_fatal(error, "could not create rtnetlink socket"); } error = nl_sock_join_mcgroup(sock, RTNLGRP_LINK); if (error) { ovs_fatal(error, "could not join RTNLGRP_LINK multicast group"); } nl_sock_listen_all_nsid(sock, true); ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); for (;;) { error = nl_sock_recv(sock, &buf, &nsid, false); if (error == EAGAIN) { /* Nothing to do. */ } else if (error == ENOBUFS) { ovs_error(0, "network monitor socket overflowed"); } else if (error) { ovs_fatal(error, "error on network monitor socket"); } else { struct iff_flag { unsigned int flag; const char *name; }; static const struct iff_flag flags[] = { { IFF_UP, "UP", }, { IFF_BROADCAST, "BROADCAST", }, { IFF_DEBUG, "DEBUG", }, { IFF_LOOPBACK, "LOOPBACK", }, { IFF_POINTOPOINT, "POINTOPOINT", }, { IFF_NOTRAILERS, "NOTRAILERS", }, { IFF_RUNNING, "RUNNING", }, { IFF_NOARP, "NOARP", }, { IFF_PROMISC, "PROMISC", }, { IFF_ALLMULTI, "ALLMULTI", }, { IFF_MASTER, "MASTER", }, { IFF_SLAVE, "SLAVE", }, { IFF_MULTICAST, "MULTICAST", }, { IFF_PORTSEL, "PORTSEL", }, { IFF_AUTOMEDIA, "AUTOMEDIA", }, { IFF_DYNAMIC, "DYNAMIC", }, }; struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)]; struct nlmsghdr *nlh; struct ifinfomsg *iim; int i; nlh = ofpbuf_at(&buf, 0, NLMSG_HDRLEN); iim = ofpbuf_at(&buf, NLMSG_HDRLEN, sizeof *iim); if (!iim) { ovs_error(0, "received bad rtnl message (no ifinfomsg)"); continue; } if (!nl_policy_parse(&buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), rtnlgrp_link_policy, attrs, ARRAY_SIZE(rtnlgrp_link_policy))) { ovs_error(0, "received bad rtnl message (policy)"); continue; } printf("netdev %s changed (%s):\n", nl_attr_get_string(attrs[IFLA_IFNAME]), (nlh->nlmsg_type == RTM_NEWLINK ? "RTM_NEWLINK" : nlh->nlmsg_type == RTM_DELLINK ? "RTM_DELLINK" : nlh->nlmsg_type == RTM_GETLINK ? "RTM_GETLINK" : nlh->nlmsg_type == RTM_SETLINK ? "RTM_SETLINK" : "other")); printf(" flags:"); for (i = 0; i < ARRAY_SIZE(flags); i++) { if (iim->ifi_flags & flags[i].flag) { printf(" %s", flags[i].name); } } printf("\n"); if (netnsid_is_remote(nsid)) { printf(" netns id: %d\n", nsid); } else { printf(" netns id: local\n"); } if (attrs[IFLA_MASTER]) { uint32_t idx = nl_attr_get_u32(attrs[IFLA_MASTER]); char ifname[IFNAMSIZ]; if (!if_indextoname(idx, ifname)) { strcpy(ifname, "unknown"); } printf(" master=%"PRIu32" (%s)\n", idx, ifname); } } nl_sock_wait(sock, POLLIN); poll_block(); } } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-appctl-bashcomp.bash000066400000000000000000000431641514270232600255240ustar00rootroot00000000000000# # A bash command completion script for ovs-appctl. # # # Right now, the script can do the following: # # - display available completion or complete on unfinished user input # (long option, subcommand, and argument). # # - once the subcommand (e.g. ofproto/trace) has been given, the # script will print the subcommand format. # # - the script can convert between keywords like 'bridge/port/interface/dp' # and the available record in ovsdb. # # The limitation are: # # - only support small set of important keywords # (dp, datapath, bridge, switch, port, interface, iface). # # - does not support parsing of nested option # (e.g. ovsdb-tool create [db [schema]]). # # - does not support expansion on repeatitive argument # (e.g. ovs-dpctl show [dp...]). # # - only support matching on long options, and only in the format # (--option [arg], i.e. should not use --option=[arg]). # # # # Keywords # ======== # # # # Expandable keywords. _KWORDS=(bridge switch port interface iface dp_name dp) # Command name. _COMMAND= # Printf enabler. _PRINTF_ENABLE= # Bash prompt. _BASH_PROMPT= # Output to the compgen. _COMP_WORDLIST= # # For ovs-appctl command only. # # Target in the current completion, default ovs-vswitchd. _APPCTL_TARGET= # Possible targets. _POSSIBLE_TARGETS="ovs-vswitchd ovsdb-server ovs-ofctl" # Command Extraction # ================== # # # # Extracts all subcommands of 'command'. # If fails, returns nothing. extract_subcmds() { local command=$_COMMAND local target= local subcmds error if [ -n "$_APPCTL_TARGET" ]; then target="--target $_APPCTL_TARGET" fi subcmds="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \ | cut -d ' ' -f1)" || error="TRUE" if [ -z "$error" ]; then echo "$subcmds" fi } # Extracts all long options of ovs-appctl. # If fails, returns nothing. extract_options() { local command=$_COMMAND local options error options="$($command --option 2>/dev/null | sort | sed -n '/^--.*/p' | cut -d '=' -f1)" \ || error="TRUE" if [ -z "$error" ]; then echo "$options" fi } # Returns the option format, if the option asks for an argument. # If fails, returns nothing. option_require_arg() { local command=$_COMMAND local option=$1 local require_arg error require_arg="$($command --option | sort | sed -n '/^--.*/p' | grep -- "$option" | grep -- "=")" \ || error="TRUE" if [ -z "$error" ]; then echo "$require_arg" fi } # Combination Discovery # ===================== # # # # Given the subcommand formats, finds all possible completions # at current completion level. find_possible_comps() { local combs="$@" local comps= local line while read line; do local arg= for arg in $line; do # If it is an optional argument, gets all completions, # and continues. if [ -n "$(sed -n '/^\[.*\]$/p' <<< "$arg")" ]; then local opt_arg="$(sed -e 's/^\[\(.*\)\]$/\1/' <<< "$arg")" local opt_args=() IFS='|' read -a opt_args <<< "$opt_arg" comps="${opt_args[@]} $comps" # If it is in format "\[*", it is a start of nested # option, do not parse. elif [ -n "$(sed -n "/^\[.*$/p" <<< "$arg")" ]; then break; # If it is a compulsory argument, adds it to the comps # and break, since all following args are for next stage. else local args=() IFS='|' read -a args <<< "$arg" comps="${args[@]} $comps" break; fi done done <<< "$combs" echo "$comps" } # Given the subcommand format, and the current command line input, # finds keywords of all possible completions. subcmd_find_keyword_based_on_input() { local format="$1" local cmd_line=($2) local mult= local combs= local comps= local arg line # finds all combinations by searching for '{}'. # there should only be one '{}', otherwise, the # command format should be changed to multiple commands. mult="$(sed -n 's/^.*{\(.*\)}.*$/ \1/p' <<< "$format" | tr '|' '\n' | cut -c1-)" if [ -n "$mult" ]; then while read line; do local tmp= tmp="$(sed -e "s@{\(.*\)}@$line@" <<< "$format")" combs="$combs@$tmp" done <<< "$mult" combs="$(tr '@' '\n' <<< "$combs")" else combs="$format" fi # Now, starts from the first argument, narrows down the # subcommand format combinations. for arg in "${subcmd_line[@]}"; do local kword possible_comps # Finds next level possible comps. possible_comps=$(find_possible_comps "$combs") # Finds the kword. kword="$(arg_to_kwords "$arg" "$possible_comps")" # Returns if could not find 'kword' if [ -z "$kword" ]; then return fi # Trims the 'combs', keeps context only after 'kword'. if [ -n "$combs" ]; then combs="$(sed -n "s@^.*\[\{0,1\}$kword|\{0,1\}[a-z_]*\]\{0,1\} @@p" <<< "$combs")" fi done comps="$(find_possible_comps "$combs")" echo "$comps" } # Helper # ====== # # # # Prints the input to stderr. $_PRINTF_ENABLE must be filled. printf_stderr() { local stderr_out="$@" if [ -n "$_PRINTF_ENABLE" ]; then printf "\n$stderr_out" 1>&2 fi } # Extracts the bash prompt PS1, outputs it with the input argument # via 'printf_stderr'. # # Original idea inspired by: # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2 # # The code below is taken from Peter Amidon. His change makes it more # robust. extract_bash_prompt() { # On Bash 4.4+ just use the @P expansion if ((BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4))); then _BASH_PROMPT="${PS1@P}" return fi local myPS1 v myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")" v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')" v="${v##*# Begin prompt}" _BASH_PROMPT="$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')" } # Keyword Conversion # ================== # # # # All completion functions. complete_bridge () { local result error result=$(ovs-vsctl list-br 2>/dev/null | grep -- "^$1") || error="TRUE" if [ -z "$error" ]; then echo "${result}" fi } complete_port () { local ports result error local all_ports all_ports=$(ovs-vsctl --format=table \ --no-headings \ --columns=name \ list Port 2>/dev/null) || error="TRUE" ports=$(printf "$all_ports" | sort | tr -d '"' | uniq -u) result=$(grep -- "^$1" <<< "$ports") if [ -z "$error" ]; then echo "${result}" fi } complete_iface () { local bridge bridges result error bridges=$(ovs-vsctl list-br 2>/dev/null) || error="TRUE" for bridge in $bridges; do local ifaces ifaces=$(ovs-vsctl list-ifaces "${bridge}" 2>/dev/null) || error="TRUE" result="${result} ${ifaces}" done if [ -z "$error" ]; then echo "${result}" fi } complete_dp () { local dps result error dps=$(ovs-appctl dpctl/dump-dps 2>/dev/null | cut -d '@' -f2) || error="TRUE" result=$(grep -- "^$1" <<< "$dps") if [ -z "$error" ]; then echo "${result}" fi } # Converts the argument (e.g. bridge/port/interface/dp name) to # the corresponding keywords. # Returns empty string if could not map the arg to any keyword. arg_to_kwords() { local arg="$1" local possible_kwords=($2) local non_parsables=() local match= local kword for kword in ${possible_kwords[@]}; do case "$kword" in bridge|switch) match="$(complete_bridge "$arg")" ;; port) match="$(complete_port "$arg")" ;; interface|iface) match="$(complete_iface "$arg")" ;; dp_name|dp) match="$(complete_dp "$arg")" ;; *) if [ "$arg" = "$kword" ]; then match="$kword" else non_parsables+=("$kword") continue fi ;; esac if [ -n "$match" ]; then echo "$kword" return fi done # If there is only one non-parsable kword, # just assumes the user input it. if [ "${#non_parsables[@]}" -eq "1" ]; then echo "$non_parsables" return fi } # Expands the keywords to the corresponding instance names. kwords_to_args() { local possible_kwords=($@) local args=() local printf_expand_once= local kword for kword in ${possible_kwords[@]}; do local match= case "${kword}" in bridge|switch) match="$(complete_bridge "")" ;; port) match="$(complete_port "")" ;; interface|iface) match="$(complete_iface "")" ;; dp_name|dp) match="$(complete_dp "")" ;; -*) # Treats option as kword as well. match="$kword" ;; *) match= ;; esac match=$(echo "$match" | tr '\n' ' ' | tr -s ' ' | sed -e 's/^[ \t]*//') args+=( $match ) if [ -n "$_PRINTF_ENABLE" ]; then local output_stderr= if [ -z "$printf_expand_once" ]; then printf_expand_once="once" printf -v output_stderr "\nArgument expansion:\n" fi printf -v output_stderr "$output_stderr available completions \ for keyword \"%s\": %s " "$kword" "$match" printf_stderr "$output_stderr" fi done echo "${args[@]}" } # Parse and Compgen # ================= # # # # This function takes the current command line arguments as input, # finds the command format and returns the possible completions. parse_and_compgen() { local command=$_COMMAND local subcmd_line=($@) local subcmd=${subcmd_line[0]} local target= local subcmd_format= local comp_keywords= local comp_wordlist= if [ -n "$_APPCTL_TARGET" ]; then target="--target $_APPCTL_TARGET" fi # Extracts the subcommand format. subcmd_format="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \ | awk -v opt=$subcmd '$1 == opt {print $0}' | tr -s ' ' )" # Finds the possible completions based on input argument. comp_keyword="$(subcmd_find_keyword_based_on_input "$subcmd_format" \ "${subcmd_line[@]}")" # Prints subcommand format and expands the keywords if 'comp_keyword' # is not empty. if [ -n "$comp_keyword" ]; then printf_stderr "$(printf "\nCommand format:\n%s" "$subcmd_format")" comp_wordlist="$(kwords_to_args "$comp_keyword")" # If there is no expanded completions, returns "NO_EXPAN" to # distinguish from the case of no available completions. if [ -z "$comp_wordlist" ]; then echo "NO_EXPAN" else echo "$comp_wordlist" fi fi } # Compgen Helper # ============== # # # # Takes the current command line arguments and returns the possible # completions. # # At the beginning, the options are checked and completed. For ovs-appctl # completion, The function looks for the --target option which gives the # target daemon name. If it is not provided, by default, 'ovs-vswitchd' # is used. # # Then, tries to locate and complete the subcommand. If the subcommand # is provided, the following arguments are passed to the 'parse_and_compgen' # function to figure out the corresponding completion of the subcommand. # # Returns the completion arguments on success. ovs_comp_helper() { local cmd_line_so_far=($@) local comp_wordlist _subcmd options i local j=-1 # Parse the command-line args till we find the subcommand. for i in "${!cmd_line_so_far[@]}"; do # if $i is not greater than $j, it means the previous iteration # skips not-visited args. so, do nothing and catch up. if [ $i -le $j ]; then continue; fi j=$i if [[ "${cmd_line_so_far[i]}" =~ ^--* ]]; then # If --target is found, locate the target daemon. # Else, it is an option command, fill the comp_wordlist with # all options. if [ "$_COMMAND" = "ovs-appctl" ] \ && [[ "${cmd_line_so_far[i]}" =~ ^--target$ ]]; then _APPCTL_TARGET="ovs-vswitchd" if [ -n "${cmd_line_so_far[j+1]}" ]; then local daemon for daemon in $_POSSIBLE_TARGETS; do # Greps "$daemon" in argument, since the argument may # be the path to the pid file. if [ "$daemon" = "${cmd_line_so_far[j+1]}" ]; then _APPCTL_TARGET="$daemon" ((j++)) break fi done continue else comp_wordlist="$_POSSIBLE_TARGETS" break fi else options="$(extract_options $_COMMAND)" # See if we could find the exact option. if [ "${cmd_line_so_far[i]}" = "$(grep -- "${cmd_line_so_far[i]}" <<< "$options")" ]; then # If an argument is required and next argument is non-empty, # skip it. Else, return directly. if [ -n "$(option_require_arg "${cmd_line_so_far[i]}")" ]; then ((j++)) if [ -z "${cmd_line_so_far[j]}" ]; then printf_stderr "\nOption requires an arugment." return fi fi continue # Else, need to keep completing on option. else comp_wordlist="$options" break fi fi fi # Takes the first non-option argument as subcmd. _subcmd="${cmd_line_so_far[i]}" break done if [ -z "$comp_wordlist" ]; then # If the subcommand is not found, provides all subcmds and options. if [ -z "$_subcmd" ]; then comp_wordlist="$(extract_subcmds) $(extract_options)" # Else parses the current arguments and finds the possible completions. else # $j stores the index of the subcmd in cmd_line_so_far. comp_wordlist="$(parse_and_compgen "${cmd_line_so_far[@]:$j}")" fi fi echo "$comp_wordlist" } # Compgen # ======= # # # # The compgen function. _ovs_command_complete() { local cur prev _COMMAND=${COMP_WORDS} # element 0 is the command. COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} # Do not print anything at first [TAB] execution. if [ "$COMP_TYPE" -eq "9" ]; then _PRINTF_ENABLE= else _PRINTF_ENABLE="enabled" fi # Extracts bash prompt PS1. if [ "$1" != "debug" ]; then extract_bash_prompt fi # Invokes the helper function to get all available completions. # Always not input the 'COMP_WORD' at 'COMP_CWORD', since it is # the one to be completed. _COMP_WORDLIST="$(ovs_comp_helper \ ${COMP_WORDS[@]:1:COMP_CWORD-1})" # This is a hack to prevent autocompleting when there is only one # available completion and printf disabled. if [ -z "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then _COMP_WORDLIST="$_COMP_WORDLIST none void no-op" fi if [ -n "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then if [ -n "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sed -e '/NO_EXPAN/d' | grep -- "^$cur")" ]; then printf_stderr "\nAvailable completions:\n" else if [ "$1" != "debug" ]; then # If there is no match between '$cur' and the '$_COMP_WORDLIST' # prints a bash prompt since the 'complete' will not print it. printf_stderr "\n$_BASH_PROMPT${COMP_WORDS[@]}" fi fi fi if [ "$1" = "debug" ]; then printf_stderr "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sort -u | sed -e '/NO_EXPAN/d' | grep -- "$cur")\n" else if [ -n "$_COMP_WORDLIST" ]; then COMPREPLY=( $(compgen -W "$(echo $_COMP_WORDLIST | tr ' ' '\n' \ | sort -u | sed -e '/NO_EXPAN/d')" -- $cur) ) else compopt -o nospace # If there is no completions, just complete on file path. _filedir fi fi return 0 } # Debug mode. if [ "$1" = "debug" ]; then shift COMP_TYPE=0 COMP_WORDS=($@) COMP_CWORD="$(expr $# - 1)" # If the last argument is TAB, it means that the previous # argument is already complete and script should complete # next argument which is not input yet. This hack is for # compromising the fact that bash cannot take unquoted # empty argument. if [ "${COMP_WORDS[$COMP_CWORD]}" = "TAB" ]; then COMP_WORDS[$COMP_CWORD]="" fi _ovs_command_complete "debug" # Normal compgen mode. else complete -F _ovs_command_complete ovs-appctl complete -F _ovs_command_complete ovs-ofctl complete -F _ovs_command_complete ovs-dpctl complete -F _ovs_command_complete ovsdb-tool fi openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-appctl.c000066400000000000000000000243611514270232600232350ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include "command-line.h" #include "daemon.h" #include "dirs.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/json.h" #include "jsonrpc.h" #include "process.h" #include "timeval.h" #include "svec.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" static void usage(void); /* Parsed command line args. */ struct cmdl_args { enum unixctl_output_fmt format; unsigned int format_flags; char *target; }; static struct cmdl_args *cmdl_args_create(void); static struct cmdl_args *parse_command_line(int argc, char *argv[]); static struct jsonrpc *connect_to_target(const char *target); static char *reply_to_string(struct json *reply, enum unixctl_output_fmt fmt, unsigned int fmt_flags); int main(int argc, char *argv[]) { struct svec opt_argv = SVEC_EMPTY_INITIALIZER; struct json *cmd_result, *cmd_error; struct jsonrpc *client; struct cmdl_args *args; char *cmd, **cmd_argv; char *msg = NULL; int cmd_argc; int error; set_program_name(argv[0]); /* Parse command line and connect to target. */ args = parse_command_line(argc, argv); client = connect_to_target(args->target); /* Transact options request (if required) and process reply. */ if (args->format != UNIXCTL_OUTPUT_FMT_TEXT) { svec_add(&opt_argv, "--format"); svec_add(&opt_argv, unixctl_output_fmt_to_string(args->format)); } svec_terminate(&opt_argv); if (!svec_is_empty(&opt_argv)) { error = unixctl_client_transact(client, "set-options", opt_argv.n, opt_argv.names, &cmd_result, &cmd_error); if (error) { ovs_fatal(error, "%s: transaction error", args->target); } if (cmd_error) { jsonrpc_close(client); msg = reply_to_string(cmd_error, UNIXCTL_OUTPUT_FMT_TEXT, 0); fputs(msg, stderr); free(msg); ovs_error(0, "%s: server returned an error", args->target); exit(2); } json_destroy(cmd_result); json_destroy(cmd_error); } svec_destroy(&opt_argv); /* Transact command request and process reply. */ cmd = argv[optind++]; cmd_argc = argc - optind; cmd_argv = cmd_argc ? argv + optind : NULL; error = unixctl_client_transact(client, cmd, cmd_argc, cmd_argv, &cmd_result, &cmd_error); if (error) { ovs_fatal(error, "%s: transaction error", args->target); } if (cmd_error) { jsonrpc_close(client); msg = reply_to_string(cmd_error, UNIXCTL_OUTPUT_FMT_TEXT, 0); fputs(msg, stderr); free(msg); ovs_error(0, "%s: server returned an error", args->target); exit(2); } else if (cmd_result) { msg = reply_to_string(cmd_result, args->format, args->format_flags); fputs(msg, stdout); free(msg); } else { OVS_NOT_REACHED(); } jsonrpc_close(client); json_destroy(cmd_result); json_destroy(cmd_error); free(args); return 0; } static void usage(void) { printf("\ %s, for querying and controlling Open vSwitch daemon\n\ usage: %s [TARGET] COMMAND [ARG...]\n\ Targets:\n\ -t, --target=TARGET pidfile or socket to contact\n\ Common commands:\n\ list-commands List commands supported by the target\n\ version Print version of the target\n\ vlog/list List current logging levels\n\ vlog/list-pattern List logging patterns for each destination.\n\ vlog/set [SPEC]\n\ Set log levels as detailed in SPEC, which may include:\n\ A valid module name (all modules, by default)\n\ 'syslog', 'console', 'file' (all destinations, by default))\n\ 'off', 'emer', 'err', 'warn', 'info', or 'dbg' ('dbg', bydefault)\n\ vlog/reopen Make the program reopen its log file\n\ Other options:\n\ --timeout=SECS wait at most SECS seconds for a response\n\ -f, --format=FMT Output format. One of: 'json', or 'text'\n\ (default: text)\n\ --pretty Format the output in a more readable fashion.\n\ Requires: --format=json.\n\ -h, --help Print this helpful information\n\ -V, --version Display ovs-appctl version information\n", program_name, program_name); exit(EXIT_SUCCESS); } static struct cmdl_args * cmdl_args_create(void) { struct cmdl_args *args = xmalloc(sizeof *args); args->format = UNIXCTL_OUTPUT_FMT_TEXT; args->format_flags = 0; args->target = NULL; return args; } static struct cmdl_args * parse_command_line(int argc, char *argv[]) { enum { OPT_START = UCHAR_MAX + 1, OPT_PRETTY, VLOG_OPTION_ENUMS, }; static const struct option long_options[] = { {"target", required_argument, NULL, 't'}, {"execute", no_argument, NULL, 'e'}, {"format", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"pretty", no_argument, NULL, OPT_PRETTY}, {"version", no_argument, NULL, 'V'}, {"timeout", required_argument, NULL, 'T'}, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options_ = ovs_cmdl_long_options_to_short_options(long_options); char *short_options = xasprintf("+%s", short_options_); struct cmdl_args *args = cmdl_args_create(); unsigned int timeout = 0; bool pretty = false; int e_options; e_options = 0; for (;;) { int option; option = getopt_long(argc, argv, short_options, long_options, NULL); if (option == -1) { break; } switch (option) { case 't': if (args->target) { ovs_fatal(0, "-t or --target may be specified only once"); } args->target = optarg; break; case 'e': /* We ignore -e for compatibility. Older versions specified the * command as the argument to -e. Since the current version takes * the command as non-option arguments and we say that -e has no * arguments, this just works in the common case. */ if (e_options++) { ovs_fatal(0, "-e or --execute may be speciifed only once"); } break; case 'f': if (!unixctl_output_fmt_from_string(optarg, &args->format)) { ovs_fatal(0, "value %s on -f or --format is invalid", optarg); } break; case 'h': usage(); break; case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case OPT_PRETTY: pretty = true; break; case 'T': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ovs_fatal(0, "value %s on -T or --timeout is invalid", optarg); } break; case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: OVS_NOT_REACHED(); } } free(short_options_); free(short_options); ctl_timeout_setup(timeout); if (optind >= argc) { ovs_fatal(0, "at least one non-option argument is required " "(use --help for help)"); } if (pretty) { if (args->format != UNIXCTL_OUTPUT_FMT_JSON) { ovs_fatal(0, "--pretty is supported with --format json only"); } args->format_flags |= JSSF_PRETTY; } if (!args->target) { args->target = "ovs-vswitchd"; } return args; } static struct jsonrpc * connect_to_target(const char *target) { struct jsonrpc *client; char *socket_name; int error; #ifndef _WIN32 if (target[0] != '/') { char *pidfile_name; pid_t pid; pidfile_name = xasprintf("%s/%s.pid", ovs_rundir(), target); pid = read_pidfile(pidfile_name); if (pid < 0) { ovs_fatal(-pid, "cannot read pidfile \"%s\"", pidfile_name); } free(pidfile_name); socket_name = xasprintf("%s/%s.%ld.ctl", ovs_rundir(), target, (long int) pid); #else /* On windows, if the 'target' contains ':', we make an assumption that * it is an absolute path. */ if (!strchr(target, ':')) { socket_name = xasprintf("%s/%s.ctl", ovs_rundir(), target); #endif } else { socket_name = xstrdup(target); } error = unixctl_client_create(socket_name, &client); if (error) { ovs_fatal(error, "cannot connect to \"%s\"", socket_name); } free(socket_name); return client; } /* The caller is responsible for freeing the returned string, with free(), when * it is no longer needed. */ static char * reply_to_string(struct json *reply, enum unixctl_output_fmt fmt, unsigned int fmt_flags) { ovs_assert(reply); if (fmt == UNIXCTL_OUTPUT_FMT_TEXT && reply->type != JSON_STRING) { ovs_error(0, "Unexpected reply type in JSON rpc reply: %s", json_type_to_string(reply->type)); exit(2); } struct ds ds = DS_EMPTY_INITIALIZER; if (fmt == UNIXCTL_OUTPUT_FMT_TEXT) { ds_put_cstr(&ds, json_string(reply)); } else { json_to_ds(reply, JSSF_SORT | fmt_flags, &ds); } if (ds_last(&ds) != EOF && ds_last(&ds) != '\n') { ds_put_char(&ds, '\n'); } return ds_steal_cstr(&ds); } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-check-dead-ifs.in000077500000000000000000000056441514270232600246730ustar00rootroot00000000000000#! @PYTHON3@ import os import re import stat import sys if "--help" in sys.argv: sys.stdout.write("""\ ovs-check-dead-ifs: Check for packet sockets for nonexistent network devices. One side effect of the "force-reload-kmod" command that reloads the Open vSwitch kernel module is that all the network devices that the Open vSwitch kernel module implemented get destroyed and then replaced by new instances with the same names. Unfortunately, programs that are listening for packets on the original network devices will not receive packets that arrive on the new instances. This causes some services, such as DHCP, to silently fail. This program looks for such problems and, if it finds any, prints information about programs that are in such a state. The system administrator should then take some action to fix the problem, such as restarting these programs. """) sys.exit(0) elif len(sys.argv) > 1: sys.stderr.write("ovs-check-dead-ifs: no arguments or options accepted " "(use --help for help)\n") sys.exit(1) # Get the set of all valid ifindexes. # # 0 is always valid for our purposes because it means "any interface". valid_ifindexes = set([]) for ifname in os.listdir("/sys/class/net"): fn = "/sys/class/net/%s/ifindex" % ifname try: valid_ifindexes.add(int(open(fn).readline())) except IOError: pass except ValueError: print("%s: unexpected format\n" % fn) # Get inodes for all packet sockets whose ifindexes don't exist. invalid_inodes = set() f = open("/proc/net/packet") f.readline() # Skip header line. for line in f: fields = line.split() ifindex = int(fields[4]) if ifindex not in valid_ifindexes: invalid_inodes.add(int(fields[8])) f.close() if not invalid_inodes: sys.exit(0) # Now find the processes that are using those packet sockets. inode_re = re.compile(r'socket:\[([0-9]+)\]$') bad_pids = set() for pid in os.listdir("/proc"): try: pid = int(pid) except ValueError: continue try: fds = os.listdir("/proc/%d/fd" % pid) except OSError: continue for fd in fds: try: fd = int(fd) except ValueError: continue try: s = os.stat("/proc/%d/fd/%d" % (pid, fd)) except OSError: continue if not stat.S_ISSOCK(s.st_mode): continue try: linkname = os.readlink("/proc/%d/fd/%d" % (pid, fd)) except OSError: continue m = inode_re.match(linkname) if not m: continue inode = int(m.group(1)) if inode in invalid_inodes: bad_pids.add(pid) if bad_pids: print(""" The following processes are listening for packets to arrive on network devices that no longer exist. You may want to restart them.""") sys.stdout.flush() os.execvp("ps", ["ps"] + ["%s" % pspid for pspid in bad_pids]) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-ctl.in000066400000000000000000000476151514270232600227270ustar00rootroot00000000000000#! /bin/sh # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Nicira, Inc. # # 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. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do case :$PATH: in *:$dir:*) ;; *) case $dir in $sbindir | $bindir) PATH=$dir:$PATH ;; *) PATH=$PATH:$dir ;; esac esac done ## ----- ## ## start ## ## ----- ## insert_mod_if_required () { ## This takes care of inserting any required kernel modules ovs_kmod_ctl insert } set_hostname () { # 'hostname -f' needs network connectivity to work. So we should # call this only after ovs-vswitchd is running. if test X$FULL_HOSTNAME = Xyes; then hn="$(hostname -f)" || hn="$(uname -n)" else hn="$(uname -n)" fi # Set the hostname if it wasn't set before ovs_vsctl add Open_vSwitch . external-ids hostname="$hn" } set_system_ids () { set ovs_vsctl set Open_vSwitch . OVS_VERSION=`ovs-vswitchd --version | awk '/Open vSwitch/{print $NF}'` set "$@" ovs-version="$OVS_VERSION" case $SYSTEM_ID in random) id_file=$etcdir/system-id.conf uuid_file=$etcdir/install_uuid.conf if test -e "$id_file"; then SYSTEM_ID=`cat "$id_file"` elif test -e "$uuid_file"; then # Migrate from old file name. . "$uuid_file" SYSTEM_ID=$INSTALLATION_UUID run_as_ovsuser touch "$id_file" echo "$SYSTEM_ID" > "$id_file" elif SYSTEM_ID=`uuidgen`; then run_as_ovsuser touch "$id_file" echo "$SYSTEM_ID" > "$id_file" else log_failure_msg "missing uuidgen, could not generate system ID" fi ;; '') log_failure_msg "system ID not configured, please use --system-id" ;; *) ;; esac set "$@" external-ids:system-id="\"$SYSTEM_ID\"" set "$@" external-ids:rundir="\"$rundir\"" if test X"$SYSTEM_TYPE" != X; then set "$@" system-type="\"$SYSTEM_TYPE\"" else log_failure_msg "no default system type, please use --system-type" fi if test X"$SYSTEM_VERSION" != X; then set "$@" system-version="\"$SYSTEM_VERSION\"" else log_failure_msg "no default system version, please use --system-version" fi action "Configuring Open vSwitch system IDs" "$@" $extra_ids } check_core_config () { if test X"$DUMP_HUGEPAGES" = Xyes; then echo 0x7f > /proc/self/coredump_filter if test X"$FORCE_COREFILES" = Xyes; then ulimit -c unlimited fi elif test X"$FORCE_COREFILES" = Xyes; then ulimit -c $ULIMIT_CORE fi } del_transient_ports () { for port in `ovs-vsctl --bare -- --columns=name find port other_config:transient=true`; do ovs_vsctl -- del-port "$port" done } do_start_ovsdb () { check_core_config if daemon_is_running ovsdb-server; then log_success_msg "ovsdb-server is already running" else # Create initial database or upgrade database schema. upgrade_db $DB_FILE $DB_SCHEMA || return 1 # Start ovsdb-server. set ovsdb-server "$DB_FILE" for db in $EXTRA_DBS; do case $db in /*) ;; *) db=$dbdir/$db ;; esac if test ! -f "$db"; then log_warning_msg "$db (from \$EXTRA_DBS) does not exist." elif ovsdb-tool db-version "$db" >/dev/null; then set "$@" "$db" else log_warning_msg "$db (from \$EXTRA_DBS) cannot be read as a database (see error message above)" fi done if test X"$SELF_CONFINEMENT" = Xno; then set "$@" --no-self-confinement fi set "$@" -vconsole:emer -vsyslog:err -vfile:info set "$@" --remote=punix:"$DB_SOCK" set "$@" --private-key=db:Open_vSwitch,SSL,private_key set "$@" --certificate=db:Open_vSwitch,SSL,certificate set "$@" --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert [ "$OVS_USER" != "" ] && set "$@" --user "$OVS_USER" [ "$OVSDB_SERVER_OPTIONS" != "" ] && set "$@" $OVSDB_SERVER_OPTIONS start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" \ "$OVSDB_SERVER_UMASK" "$@" || return 1 # Initialize database settings. ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \ || return 1 set_system_ids || return 1 if test X"$DELETE_BRIDGES" = Xyes; then for bridge in `ovs_vsctl list-br`; do ovs_vsctl del-br $bridge done fi if test X"$DELETE_TRANSIENT_PORTS" = Xyes; then del_transient_ports fi fi } start_ovsdb() { if test X"$OVSDB_SERVER" = Xyes; then do_start_ovsdb || return 1 fi return 0 } add_managers () { # Tell ovsdb-server to connect to the remote managers. If ovs-vswitchd # is not finished configuring, it may mean that remote managers will # see more churn in the database at startup or restart. (For example, # managers may briefly see empty datapath-id or ofport columns for # records that exist at startup.). However, the alternative is a # 'bricked' system, so we allow database connectivity regardless. if test X"$OVSDB_SERVER" = Xyes || test X"$OVS_VSWITCHD" = Xyes; then if daemon_is_running ovsdb-server; then action "Enabling remote OVSDB managers" \ ovs-appctl -t ovsdb-server ovsdb-server/add-remote \ db:Open_vSwitch,Open_vSwitch,manager_options fi fi } do_start_forwarding () { check_core_config insert_mod_if_required || return 1 if daemon_is_running ovs-vswitchd; then log_success_msg "ovs-vswitchd is already running" else # Increase the limit on the number of open file descriptors. # On Linux, ovs-vswitchd needs about three file descriptors # per bridge and "n-handler-threads" file descriptors per bridge # port, so this allows a very large number of bridges and ports. MAXFD=65535 if [ $(ulimit -n) -lt $MAXFD ]; then ulimit -n $MAXFD fi # Start ovs-vswitchd. set ovs-vswitchd unix:"$DB_SOCK" set "$@" -vconsole:emer -vsyslog:err -vfile:info if test X"$MLOCKALL" != Xno; then set "$@" --mlockall fi if test X"$SELF_CONFINEMENT" = Xno; then set "$@" --no-self-confinement fi [ "$OVS_USER" != "" ] && set "$@" --user "$OVS_USER" [ "$OVS_VSWITCHD_OPTIONS" != "" ] &&set "$@" $OVS_VSWITCHD_OPTIONS start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" \ "$OVS_VSWITCHD_UMASK" "$@" || return 1 fi } start_forwarding () { if test X"$OVS_VSWITCHD" = Xyes; then do_start_forwarding || return 1 fi if test X"$RECORD_HOSTNAME" = Xyes; then set_hostname & fi return 0 } start_ovs_ipsec () { set ${datadir}/scripts/ovs-monitor-ipsec unix:"$DB_SOCK" set "$@" --log-file=${logdir}/ovs-monitor-ipsec.log set "$@" --pidfile=${rundir}/ovs-monitor-ipsec.pid set "$@" --detach test X"$MONITOR" = Xno || set "$@" --monitor set "$@" --ike-daemon=$IKE_DAEMON if test X$RESTART_IKE_DAEMON = Xno; then set "$@" --no-restart-ike-daemon fi if test X"$OVS_MONITOR_IPSEC_OPTIONS" != X; then set "$@" $OVS_MONITOR_IPSEC_OPTIONS fi action "Starting ovs-monitor-ipsec" "$@" || return 1 return 0 } ## ---- ## ## stop ## ## ---- ## stop_ovsdb () { if test X"$OVSDB_SERVER" = Xyes; then stop_daemon ovsdb-server fi } stop_forwarding () { if test X"$OVS_VSWITCHD" = Xyes; then stop_daemon ovs-vswitchd fi } stop_ovs_ipsec () { stop_daemon ovs-monitor-ipsec } ## --------------- ## ## enable-protocol ## ## --------------- ## enable_protocol () { # Translate the protocol name to a number, because "iptables -n -L" prints # some protocols by name (despite the -n) and therefore we need to look for # both forms. # # (iptables -S output is more uniform but old iptables doesn't have it.) protonum=`grep "^$PROTOCOL[ ]" /etc/protocols | awk '{print $2}'` if expr X"$protonum" : X'[0-9]\{1,\}$' > /dev/null; then :; else log_failure_msg "unknown protocol $PROTOCOL" return 1 fi name=$PROTOCOL match="(\$2 == \"$PROTOCOL\" || \$2 == $protonum)" insert="iptables -I INPUT -p $PROTOCOL" if test X"$DPORT" != X; then name="$name to port $DPORT" match="$match && /dpt:$DPORT/" insert="$insert --dport $DPORT" fi if test X"$SPORT" != X; then name="$name from port $SPORT" match="$match && /spt:$SPORT/" insert="$insert --sport $SPORT" fi insert="$insert -j ACCEPT" if (iptables -n -L INPUT) >/dev/null 2>&1; then if iptables -n -L INPUT | awk "$match { n++ } END { exit n == 0 }" then # There's already a rule for this protocol. Don't override it. log_success_msg "iptables already has a rule for $name, not explicitly enabling" else action "Enabling $name with iptables" $insert fi elif (iptables --version) >/dev/null 2>&1; then action "cannot list iptables rules, not adding a rule for $name" else action "iptables binary not installed, not adding a rule for $name" fi } ## ---- ## ## main ## ## ---- ## set_defaults () { SYSTEM_ID= FULL_HOSTNAME=yes RECORD_HOSTNAME=yes DELETE_BRIDGES=no DELETE_TRANSIENT_PORTS=no DAEMON_CWD=/ FORCE_COREFILES=yes DUMP_HUGEPAGES=no MLOCKALL=yes SELF_CONFINEMENT=yes MONITOR=yes OVS_USER= OVSDB_SERVER=yes OVS_VSWITCHD=yes OVSDB_SERVER_PRIORITY=-10 OVS_VSWITCHD_PRIORITY=-10 OVSDB_SERVER_WRAPPER= OVS_VSWITCHD_WRAPPER= OVSDB_SERVER_OPTIONS= OVS_VSWITCHD_OPTIONS= OVS_MONITOR_IPSEC_OPTIONS= OVSDB_SERVER_UMASK= OVS_VSWITCHD_UMASK= OOM_SCORE= ULIMIT_CORE=67108864 DB_FILE=$dbdir/conf.db DB_SOCK=$rundir/db.sock DB_SCHEMA=$datadir/vswitch.ovsschema EXTRA_DBS= PROTOCOL=gre DPORT= SPORT= IKE_DAEMON= RESTART_IKE_DAEMON=yes type_file=$etcdir/system-type.conf version_file=$etcdir/system-version.conf if test -e "$type_file" ; then SYSTEM_TYPE=`cat $type_file` SYSTEM_VERSION=`cat $version_file` elif test -e "@sysconfdir@/os-release"; then SYSTEM_TYPE=`. '@sysconfdir@/os-release' && echo "$ID"` SYSTEM_VERSION=`. '@sysconfdir@/os-release' && echo "$VERSION_ID"` elif (lsb_release --id) >/dev/null 2>&1; then SYSTEM_TYPE=`lsb_release --id -s` system_release=`lsb_release --release -s` system_codename=`lsb_release --codename -s` SYSTEM_VERSION="${system_release}-${system_codename}" else SYSTEM_TYPE=unknown SYSTEM_VERSION=unknown fi } usage () { set_defaults cat <&2 "$0: unknown option \"$arg\" (use --help for help)" return fi eval $var=\$value } daemons () { echo ovsdb-server ovs-vswitchd } set_defaults extra_ids= command= for arg do case $arg in -h | --help) usage ;; -V | --version) echo "$0 (Open vSwitch) $VERSION" exit 0 ;; --external-id=*) value=`expr X"$arg" : 'X[^=]*=\(.*\)'` case $value in *=*) extra_ids="$extra_ids external-ids:$value" ;; *) echo >&2 "$0: --external-id argument not in the form \"key=value\"" exit 1 ;; esac ;; --[a-z]*=*) option=`expr X"$arg" : 'X--\([^=]*\)'` value=`expr X"$arg" : 'X[^=]*=\(.*\)'` type=string set_option ;; --no-[a-z]*) option=`expr X"$arg" : 'X--no-\(.*\)'` value=no type=bool set_option ;; --[a-z]*) option=`expr X"$arg" : 'X--\(.*\)'` value=yes type=bool set_option ;; -*) echo >&2 "$0: unknown option \"$arg\" (use --help for help)" exit 1 ;; *) if test X"$command" = X; then command=$arg else echo >&2 "$0: exactly one non-option argument required (use --help for help)" exit 1 fi ;; esac done case $command in start) start_ovsdb || exit 1 start_forwarding || exit 1 add_managers ;; stop) stop_forwarding stop_ovsdb ;; restart) restart ;; status) rc=0 for daemon in `daemons`; do daemon_status $daemon || rc=$? done exit $rc ;; version) for daemon in `daemons`; do $daemon --version done ;; force-reload-kmod) force_reload_kmod ;; load-kmod) insert_mod_if_required ;; enable-protocol) enable_protocol ;; delete-transient-ports) del_transient_ports ;; start-ovs-ipsec) start_ovs_ipsec ;; stop-ovs-ipsec) stop_ovs_ipsec ;; record-hostname-if-not-set) set_hostname ;; help) usage ;; '') echo >&2 "$0: missing command name (use --help for help)" exit 1 ;; *) echo >&2 "$0: unknown command \"$command\" (use --help for help)" exit 1 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-dev.py000077500000000000000000000323121514270232600227340ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2013, 2014, 2015, 2016, 2020 Nicira, Inc. # # 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. import optparse import os import shutil import subprocess import sys import time ENV = os.environ HOME = ENV["HOME"] PWD = os.getcwd() OVS_SRC = HOME + "/ovs" if os.path.exists(PWD + "/README.rst"): OVS_SRC = PWD # Use current directory as OVS source tree RUNDIR = OVS_SRC + "/_run" BUILD_GCC = OVS_SRC + "/_build-gcc" BUILD_CLANG = OVS_SRC + "/_build-clang" options = None parser = None commands = [] def set_path(build): PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": build} ENV["PATH"] = PATH + ":" + ENV["PATH"] def _sh(*args, **kwargs): print("------> " + " ".join(args)) shell = len(args) == 1 if kwargs.get("capture", False): proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell) return proc.stdout.readlines() elif kwargs.get("check", True): subprocess.check_call(args, shell=shell) else: subprocess.call(args, shell=shell) def uname(): return _sh("uname", "-r", capture=True)[0].decode().strip() def sudo(): if os.geteuid() != 0: _sh(" ".join(["sudo"] + sys.argv), check=True) sys.exit(0) def conf(): tag() try: os.remove(OVS_SRC + "/Makefile") except OSError: pass configure = ["../configure", "--prefix=" + RUNDIR, "--localstatedir=" + RUNDIR, "--with-logdir=%s/log" % RUNDIR, "--with-rundir=%s/run" % RUNDIR, "--enable-silent-rules", "--with-dbdir=" + RUNDIR, "--silent"] cflags = "-g -fno-omit-frame-pointer" if options.werror: configure.append("--enable-Werror") if options.cache_time: configure.append("--enable-cache-time") if options.mandir: configure.append("--mandir=" + options.mandir) if options.with_dpdk: configure.append("--with-dpdk=" + options.with_dpdk) if options.optimize is None: options.optimize = 0 cflags += " -O%s" % str(options.optimize) ENV["CFLAGS"] = cflags _sh("./boot.sh") try: os.mkdir(BUILD_GCC) except OSError: pass # Directory exists. os.chdir(BUILD_GCC) _sh(*(configure)) try: _sh("clang --version", check=True) clang = True except subprocess.CalledProcessError: clang = False try: _sh("sparse --version", check=True) sparse = True except subprocess.CalledProcessError: sparse = False if clang: try: os.mkdir(BUILD_CLANG) except OSError: pass # Directory exists. ENV["CC"] = "clang" os.chdir(BUILD_CLANG) _sh(*configure) if sparse: c1 = "C=1" else: c1 = "" os.chdir(OVS_SRC) make_str = "\t$(MAKE) -C %s $@\n" mf = open(OVS_SRC + "/Makefile", "w") mf.write("all:\n%:\n") if clang: mf.write(make_str % BUILD_CLANG) mf.write("\t$(MAKE) -C %s %s $@\n" % (BUILD_GCC, c1)) mf.write("\ncheck-valgrind:\n") mf.write("\ncheck:\n") mf.write(make_str % BUILD_GCC) mf.close() commands.append(conf) def make(args=""): make = "make -s -j 8 " + args _sh(make) commands.append(make) def check(): flags = "" if options.jobs: flags += "-j%d " % options.jobs else: flags += "-j8 " if options.tests: for arg in str.split(options.tests): if arg[0].isdigit(): flags += "%s " % arg else: flags += "-k %s " % arg ENV["TESTSUITEFLAGS"] = flags make("check") commands.append(check) def tag(): ctags = ['ctags', '-R', '-f', '.tags'] try: _sh(*ctags) except: pass try: _sh('cscope', '-R', '-b') except: pass commands.append(tag) def kill(): sudo() for proc in ["ovs-vswitchd", "ovsdb-server"]: if os.path.exists("%s/run/openvswitch/%s.pid" % (RUNDIR, proc)): _sh("ovs-appctl", "-t", proc, "exit", check=False) time.sleep(.1) _sh("killall", "-q", "-2", proc, check=False) commands.append(kill) def reset(): sudo() kill() if os.path.exists(RUNDIR): shutil.rmtree(RUNDIR) for dp in _sh("ovs-dpctl dump-dps", capture=True): _sh("ovs-dpctl", "del-dp", dp.decode().strip()) commands.append(reset) def run(): sudo() kill() for d in ["log", "run"]: d = "%s/%s" % (RUNDIR, d) shutil.rmtree(d, ignore_errors=True) os.makedirs(d) pki_dir = RUNDIR + "/pki" if not os.path.exists(pki_dir): os.mkdir(pki_dir) os.chdir(pki_dir) _sh("ovs-pki init") _sh("ovs-pki req+sign ovsclient") os.chdir(OVS_SRC) if not os.path.exists(RUNDIR + "/conf.db"): _sh("ovsdb-tool", "create", RUNDIR + "/conf.db", OVS_SRC + "/vswitchd/vswitch.ovsschema") opts = ["--pidfile", "--log-file"] if (options.user == "") or (options.user == "root:root"): _sh("chown", "root:root", "-R", RUNDIR) if '--user' in sys.argv: sys.argv.remove("--user") else: _sh("chown", options.user, "-R", RUNDIR) opts = ["--user", options.user] + opts if (options.monitor): opts = ["--monitor"] + opts _sh(*(["ovsdb-server", "--remote=punix:%s/run/db.sock" % RUNDIR, "--remote=db:Open_vSwitch,Open_vSwitch,manager_options", "--private-key=db:Open_vSwitch,SSL,private_key", "--certificate=db:Open_vSwitch,SSL,certificate", "--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert", "--detach", "-vconsole:off"] + opts)) _sh("ovs-vsctl --no-wait --bootstrap set-ssl %s/ovsclient-privkey.pem" " %s/ovsclient-cert.pem %s/vswitchd.cacert" % (pki_dir, pki_dir, pki_dir)) version = _sh("ovs-vsctl --no-wait --version", capture=True) version = version[0].decode().strip().split()[3] root_uuid = _sh("ovs-vsctl --no-wait --bare list Open_vSwitch", capture=True)[0].decode().strip() _sh("ovs-vsctl --no-wait set Open_vSwitch %s ovs_version=%s" % (root_uuid, version)) build = BUILD_CLANG if options.clang else BUILD_GCC cmd = [build + "/vswitchd/ovs-vswitchd"] if options.dpdk: _sh("ovs-vsctl --no-wait set Open_vSwitch %s " "other_config:dpdk-init=true" % root_uuid) _sh("ovs-vsctl --no-wait set Open_vSwitch %s other_config:" "dpdk-extra=\"%s\"" % (root_uuid, ' '.join(options.dpdk))) else: _sh("ovs-vsctl --no-wait set Open_vSwitch %s " "other_config:dpdk-init=false" % root_uuid) if options.gdb: cmd = ["gdb", "--args"] + cmd elif options.valgrind: cmd = ["valgrind", "--track-origins=yes", "--leak-check=full", "--suppressions=%s/tests/glibc.supp" % OVS_SRC, "--suppressions=%s/tests/openssl.supp" % OVS_SRC] + cmd else: opts = opts + ["-vconsole:off", "--detach", "--enable-dummy"] _sh(*(cmd + opts)) commands.append(run) def modinst(): if not os.path.exists("/lib/modules"): print("Missing modules directory. Is this a Linux system?") sys.exit(1) sudo() try: _sh("rmmod", "openvswitch") except subprocess.CalledProcessError: pass # Module isn't loaded try: _sh("rm -f /lib/modules/%s/extra/openvswitch.ko" % uname()) _sh("rm -f /lib/modules/%s/extra/vport-*.ko" % uname()) except subprocess.CalledProcessError: pass # Module isn't installed conf() make() make("modules_install") _sh("modprobe", "openvswitch") _sh("dmesg | grep openvswitch | tail -1") _sh("find /lib/modules/%s/ -iname vport-*.ko -exec insmod '{}' \\;" % uname()) commands.append(modinst) def env(): print("export PATH=" + ENV["PATH"]) commands.append(env) def doc(): parser.print_help() print(""" This program is designed to help developers build and run Open vSwitch without necessarily needing to know the gory details. Given some basic requirements (described below), it can be used to build and run Open vSwitch, keeping runtime files in the user's home directory. Basic Configuration: # This section can be run as a script on ubuntu systems. # First install the basic requirements needed to build Open vSwitch. sudo apt-get install git build-essential libtool autoconf pkg-config \\ libssl-dev gdb libcap-ng-dev # Next clone the Open vSwitch source. git clone https://github.com/openvswitch/ovs.git %(ovs)s # Setup environment variables. `%(v)s env` # Build the switch. %(v)s conf make # Run the switch. %(v)s run Commands: conf - Configure the ovs source. make - Build the source (must have been configured). check - Run the unit tests. tag - Run ctags and cscope over the source. kill - Kill all running instances of ovs. reset - Reset any runtime configuration in %(run)s. run - Run ovs. modinst - Build ovs and install the kernel module. env - Print the required path environment variable. doc - Print this message. Note: If running as non-root user, "kill", "reset", "run" and "modinst" will always run as the root user, by rerun the commands with "sudo". """ % {"ovs": OVS_SRC, "v": sys.argv[0], "run": RUNDIR}) sys.exit(0) commands.append(doc) def parse_subargs(option, opt_str, value, parser): subopts = [] while parser.rargs: dpdkarg = parser.rargs.pop(0) if dpdkarg == "--": break subopts.append(dpdkarg) setattr(parser.values, option.dest, subopts) def main(): global options global parser description = "Open vSwitch developer configuration. Try `%prog doc`." cmd_names = [c.__name__ for c in commands] usage = "usage: %prog" + " [options] [%s] ..." % "|".join(cmd_names) parser = optparse.OptionParser(usage=usage, description=description) group = optparse.OptionGroup(parser, "conf") group.add_option("--disable-Werror", dest="werror", action="store_false", default=True, help="compile without the Werror flag") group.add_option("--cache-time", dest="cache_time", action="store_true", help="configure with cached timing") group.add_option("--mandir", dest="mandir", metavar="MANDIR", help="configure the man documentation install directory") group.add_option("--with-dpdk", dest="with_dpdk", metavar="DPDK_BUILD", help="built with dpdk libraries located at DPDK_BUILD") parser.add_option_group(group) group = optparse.OptionGroup(parser, "Optimization Flags") for i in ["s", "g"] + list(range(4)) + ["fast"]: group.add_option("--O%s" % str(i), dest="optimize", action="store_const", const=i, help="compile with -O%s" % str(i)) parser.add_option_group(group) group = optparse.OptionGroup(parser, "check") group.add_option("-j", "--jobs", dest="jobs", metavar="N", type="int", help="Run N tests in parallel") group.add_option("--tests", dest="tests", metavar="FILTER", help="""run specific tests and/or a test category eg, --tests=\"1-10 megaflow\"""") parser.add_option_group(group) group = optparse.OptionGroup(parser, "run") group.add_option("-g", "--gdb", dest="gdb", action="store_true", help="run ovs-vswitchd under gdb") group.add_option("--valgrind", dest="valgrind", action="store_true", help="run ovs-vswitchd under valgrind") group.add_option("--dpdk", dest="dpdk", action="callback", callback=parse_subargs, help="run ovs-vswitchd with dpdk subopts (ended by --)") group.add_option("--clang", dest="clang", action="store_true", help="Use binaries built by clang") group.add_option("--user", dest="user", action="store", default="", help="run all daemons as a non root user") group.add_option("--monitor", dest="monitor", action="store_true", help="run daemons with --monitor option") parser.add_option_group(group) options, args = parser.parse_args() for arg in args: if arg not in cmd_names: print("Unknown argument " + arg) doc() if options.clang: set_path(BUILD_CLANG) else: set_path(BUILD_GCC) try: os.chdir(OVS_SRC) except OSError: print("Missing %s." % OVS_SRC) doc() for arg in args: for cmd in commands: if arg == cmd.__name__: cmd() if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-docker000077500000000000000000000176001514270232600230010ustar00rootroot00000000000000#!/bin/bash # Copyright (C) 2014 Nicira, Inc. # # 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. # Check for programs we'll need. search_path () { save_IFS=$IFS IFS=: for dir in $PATH; do IFS=$save_IFS if test -x "$dir/$1"; then return 0 fi done IFS=$save_IFS echo >&2 "$0: $1 not found in \$PATH, please install and try again" exit 1 } ovs_vsctl () { ovs-vsctl --timeout=60 "$@" } create_netns_link () { mkdir -p /var/run/netns if [ ! -e /var/run/netns/"$PID" ]; then ln -s /proc/"$PID"/ns/net /var/run/netns/"$PID" trap 'delete_netns_link' 0 for signal in 1 2 3 13 14 15; do trap 'delete_netns_link; trap - $signal; kill -$signal $$' $signal done fi } delete_netns_link () { rm -f /var/run/netns/"$PID" } get_port_for_container_interface () { CONTAINER="$1" INTERFACE="$2" PORT=`ovs_vsctl --data=bare --no-heading --columns=name find interface \ external_ids:container_id="$CONTAINER" \ external_ids:container_iface="$INTERFACE"` if [ -z "$PORT" ]; then echo >&2 "$UTIL: Failed to find any attached port" \ "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE" fi echo "$PORT" } add_port () { BRIDGE="$1" INTERFACE="$2" CONTAINER="$3" if [ -z "$BRIDGE" ] || [ -z "$INTERFACE" ] || [ -z "$CONTAINER" ]; then echo >&2 "$UTIL add-port: not enough arguments (use --help for help)" exit 1 fi shift 3 while [ $# -ne 0 ]; do case $1 in --ipaddress=*) ADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --macaddress=*) MACADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --gateway=*) GATEWAY=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --mtu=*) MTU=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; *) echo >&2 "$UTIL add-port: unknown option \"$1\"" exit 1 ;; esac done # Check if a port is already attached for the given container and interface PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE" \ 2>/dev/null` if [ -n "$PORT" ]; then echo >&2 "$UTIL: Port already attached" \ "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE" exit 1 fi if ovs_vsctl br-exists "$BRIDGE" || \ ovs_vsctl add-br "$BRIDGE"; then :; else echo >&2 "$UTIL: Failed to create bridge $BRIDGE" exit 1 fi if PID=`docker inspect -f '{{.State.Pid}}' "$CONTAINER"`; then :; else echo >&2 "$UTIL: Failed to get the PID of the container" exit 1 fi create_netns_link # Create a veth pair. ID=`uuidgen | sed 's/-//g'` PORTNAME="${ID:0:13}" ip link add "${PORTNAME}_l" type veth peer name "${PORTNAME}_c" # Add one end of veth to OVS bridge. if ovs_vsctl --may-exist add-port "$BRIDGE" "${PORTNAME}_l" \ -- set interface "${PORTNAME}_l" \ external_ids:container_id="$CONTAINER" \ external_ids:container_iface="$INTERFACE"; then :; else echo >&2 "$UTIL: Failed to add "${PORTNAME}_l" port to bridge $BRIDGE" ip link delete "${PORTNAME}_l" exit 1 fi ip link set "${PORTNAME}_l" up # Move "${PORTNAME}_c" inside the container and changes its name. ip link set "${PORTNAME}_c" netns "$PID" ip netns exec "$PID" ip link set dev "${PORTNAME}_c" name "$INTERFACE" ip netns exec "$PID" ip link set "$INTERFACE" up if [ -n "$MTU" ]; then ip netns exec "$PID" ip link set dev "$INTERFACE" mtu "$MTU" fi if [ -n "$ADDRESS" ]; then ip netns exec "$PID" ip addr add "$ADDRESS" dev "$INTERFACE" fi if [ -n "$MACADDRESS" ]; then ip netns exec "$PID" ip link set dev "$INTERFACE" address "$MACADDRESS" fi if [ -n "$GATEWAY" ]; then ip netns exec "$PID" ip route add default via "$GATEWAY" fi } del_port () { BRIDGE="$1" INTERFACE="$2" CONTAINER="$3" if [ "$#" -lt 3 ]; then usage exit 1 fi PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE"` if [ -z "$PORT" ]; then exit 1 fi ovs_vsctl --if-exists del-port "$PORT" ip link delete "$PORT" } del_ports () { BRIDGE="$1" CONTAINER="$2" if [ "$#" -lt 2 ]; then usage exit 1 fi PORTS=`ovs_vsctl --data=bare --no-heading --columns=name find interface \ external_ids:container_id="$CONTAINER"` if [ -z "$PORTS" ]; then exit 0 fi for PORT in $PORTS; do ovs_vsctl --if-exists del-port "$PORT" ip link delete "$PORT" done } set_vlan () { BRIDGE="$1" INTERFACE="$2" CONTAINER_ID="$3" VLAN="$4" if [ "$#" -lt 4 ]; then usage exit 1 fi PORT=`get_port_for_container_interface "$CONTAINER_ID" "$INTERFACE"` if [ -z "$PORT" ]; then exit 1 fi ovs_vsctl set port "$PORT" tag="$VLAN" } usage() { cat << EOF ${UTIL}: Performs integration of Open vSwitch with Docker. usage: ${UTIL} COMMAND Commands: add-port BRIDGE INTERFACE CONTAINER [--ipaddress="ADDRESS"] [--gateway=GATEWAY] [--macaddress="MACADDRESS"] [--mtu=MTU] Adds INTERFACE inside CONTAINER and connects it as a port in Open vSwitch BRIDGE. Optionally, sets ADDRESS on INTERFACE. ADDRESS can include a '/' to represent network prefix length. Optionally, sets a GATEWAY, MACADDRESS and MTU. e.g.: ${UTIL} add-port br-int eth1 c474a0e2830e --ipaddress=192.168.1.2/24 --gateway=192.168.1.1 --macaddress="a2:c3:0d:49:7f:f8" --mtu=1450 del-port BRIDGE INTERFACE CONTAINER Deletes INTERFACE inside CONTAINER and removes its connection to Open vSwitch BRIDGE. e.g.: ${UTIL} del-port br-int eth1 c474a0e2830e del-ports BRIDGE CONTAINER Removes all Open vSwitch interfaces from CONTAINER. e.g.: ${UTIL} del-ports br-int c474a0e2830e set-vlan BRIDGE INTERFACE CONTAINER VLAN Configures the INTERFACE of CONTAINER attached to BRIDGE to become an access port of VLAN. e.g.: ${UTIL} set-vlan br-int eth1 c474a0e2830e 5 Options: -h, --help display this help message. EOF } UTIL=$(basename $0) search_path ovs-vsctl search_path docker search_path uuidgen if (ip netns) > /dev/null 2>&1; then :; else echo >&2 "$UTIL: ip utility not found (or it does not support netns),"\ "cannot proceed" exit 1 fi if [ $# -eq 0 ]; then usage exit 0 fi case $1 in "add-port") shift add_port "$@" exit 0 ;; "del-port") shift del_port "$@" exit 0 ;; "del-ports") shift del_ports "$@" exit 0 ;; "set-vlan") shift set_vlan "$@" exit 0 ;; -h | --help) usage exit 0 ;; *) echo >&2 "$UTIL: unknown command \"$1\" (use --help for help)" exit 1 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-dpctl-top.8.in000066400000000000000000000102201514270232600241770ustar00rootroot00000000000000.so lib/ovs.tmac .TH ovs\-dpctl\-top "8" "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME \fBovs\-dpctl\-top\fR \- Top like behavior for ovs\-dpctl dump\-flows . .SH SYNOPSIS \fBovs\-dpctl\-top\fR [\-h] [\-v] [\-f FLOWFILES] [\-V] [\-s] [\-\-host HOST] [\-a | \-\-accumulate] [\-\-accumulate\-decay ACCUMULATEDECAY] [\-d DELAY] . .SH DESCRIPTION .PP This program summarizes \fBovs\-dpctl\fR flow content by aggregating the number of packets, total bytes and occurrence of the following fields: .IP \- Datapath in_port .IP \- Ethernet type .IP \- Source and destination MAC addresses .IP \- IP protocol .IP \- Source and destination IPv4 addresses .IP \- Source and destination IPv6 addresses .IP \- UDP and TCP destination port .IP \- Tunnel source and destination addresses . .SS "Output shows four values:" .IP \- FIELDS: the flow fields for example in_port(1). .IP \- COUNT: the number of lines in the dump\-flow output contain the flow field. .IP \- PACKETS: the total number of packets containing the flow field. .IP \- BYTES: the total number of bytes containing the flow field. If units are not present then values are in bytes. .IP \- AVERAGE: the average packets size (BYTES/PACKET). .PP .SS "Top Behavior" .PP While in top mode, the default behavior, the following single character commands are supported: .IP a \- toggles top in accumulate and live mode. Accumulate mode is described below. .IP s \- toggles which column is used to sort content in decreasing order. A DESC title is placed over the column. .IP _ \- a space indicating to collect dump\-flow content again .IP h \- halt output. Any character will restart sampling .IP f \- cycle through flow fields .IP q \- q for quit. .PP .SS "Accumulate Mode" .PP There are two supported modes: live and accumulate. The default is live. The parameter \fB\-\-accumulate\fR or the 'a' character in top mode enables the latter. In live mode, recent dump\-flow content is presented. Where as accumulate mode keeps track of the prior historical information until the flow is reset not when the flow is purged. Reset flows are determined when the packet count for a flow has decreased from its previous sample. There is one caveat, eventually the system will run out of memory if, after the accumulate\-decay period any flows that have not been refreshed are purged. The goal here is to free memory of flows that are not active. Statistics are not decremented. Their purpose is to reflect the overall history of the flow fields. .PP .SS "Debugging Errors" .PP Parsing errors are counted and displayed in the status line at the beginning of the output. Use the \fB\-\-verbose\fR option with \fB\-\-script to see what output was not parsed, like this: .PP $ ovs\-dpctl dump\-flows | ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-verbose\fR .PP Error messages will identify content that failed to parse. .PP .SS "Access Remote Hosts" .PP The \fB\-\-host\fR must follow the format user@hostname. This script simply calls \&'ssh user@Hostname' without checking for login credentials therefore public keys should be installed on the system identified by hostname, such as: .PP $ ssh\-copy\-id user@hostname .PP Consult ssh\-copy\-id man pages for more details. .PP .SS "Expected usage" .PP $ ovs\-dpctl\-top .PP or to run as a script: .PP $ ovs\-dpctl dump\-flows > dump\-flows.log .PP $ ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-flow\-file\fR dump\-flows.log .SS "OPTIONS" .TP \fB\-h\fR, \fB\-\-help\fR show this help message and exit. .TP \fB\-v\fR, \fB\-\-version\fR show program's version number and exit. .TP \fB\-f\fR FLOWFILES, \fB\-\-flow\-file\fR FLOWFILES file containing flows from ovs\-dpctl dump\-flow. .TP \fB\-V\fR, \fB\-\-verbose\fR enable debug level verbosity. .TP \fB\-s\fR, \fB\-\-script\fR Run from a script (no user interface). .TP \fB\-\-host\fR HOST Specify a user@host for retrieving flows see Accessing Remote Hosts for more information. .TP \fB\-a\fR, \fB\-\-accumulate\fR Accumulate dump\-flow content. .TP \fB\-\-accumulate\-decay\fR ACCUMULATEDECAY Decay old accumulated flows. The default is 5 minutes. A value of 0 disables decay. .TP \fB\-d\fR DELAY, \fB\-\-delay\fR DELAY Delay in milliseconds to collect dump\-flow content (sample rate). openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-dpctl-top.in000077500000000000000000001703141514270232600240470ustar00rootroot00000000000000#! @PYTHON3@ # # Copyright (c) 2013 Nicira, Inc. # # 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. # # # The approximate_size code was copied from # http://getpython3.com/diveintopython3/your-first-python-program.html#divingin # which is licensed under # "Dive Into Python 3," Copyright 2011 Mark Pilgrim, # used under a Creative Commons Attribution-Share-Alike license: # http://creativecommons.org/licenses/by-sa/3.0/ # # """Top like behavior for ovs-dpctl dump-flows output. This program summarizes ovs-dpctl flow content by aggregating the number of packets, total bytes and occurrence of the following fields: - Datapath in_port - Ethernet type - Source and destination MAC addresses - IP protocol - Source and destination IPv4 addresses - Source and destination IPv6 addresses - UDP and TCP destination port - Tunnel source and destination addresses Output shows four values: - FIELDS: the flow fields for example in_port(1). - PACKETS: the total number of packets containing the flow field. - BYTES: the total number of bytes containing the flow field. If units are not present then values are in bytes. - AVERAGE: the average packets size (BYTES/PACKET). - COUNT: the number of lines in the dump-flow output contain the flow field. Top Behavior While in top mode, the default behavior, the following single character commands are supported: a - toggles top in accumulate and live mode. Accumulate mode is described below. s - toggles which column is used to sort content in decreasing order. A DESC title is placed over the column. _ - a space indicating to collect dump-flow content again h - halt output. Any character will restart sampling f - cycle through flow fields. The initial field is in_port q - q for quit. Accumulate Mode There are two supported modes: live and accumulate. The default is live. The parameter --accumulate or the 'a' character in top mode enables the latter. In live mode, recent dump-flow content is presented. Where as accumulate mode keeps track of the prior historical information until the flow is reset not when the flow is purged. Reset flows are determined when the packet count for a flow has decreased from its previous sample. There is one caveat, eventually the system will run out of memory if, after the accumulate-decay period any flows that have not been refreshed are purged. The goal here is to free memory of flows that are not active. Statistics are not decremented. Their purpose is to reflect the overall history of the flow fields. Debugging Errors Parsing errors are counted and displayed in the status line at the beginning of the output. Use the --verbose option with --script to see what output was not parsed, like this: $ ovs-dpctl dump-flows | ovs-dpctl-top --script --verbose Error messages will identify content that failed to parse. Access Remote Hosts The --host must follow the format user@hostname. This script simply calls 'ssh user@Hostname' without checking for login credentials therefore public keys should be installed on the system identified by hostname, such as: $ ssh-copy-id user@hostname Consult ssh-copy-id man pages for more details. Expected usage $ ovs-dpctl-top or to run as a script: $ ovs-dpctl dump-flows > dump-flows.log $ ovs-dpctl-top --script --flow-file dump-flows.log """ # pylint: disable-msg=C0103 # pylint: disable-msg=C0302 # pylint: disable-msg=R0902 # pylint: disable-msg=R0903 # pylint: disable-msg=R0904 # pylint: disable-msg=R0912 # pylint: disable-msg=R0913 # pylint: disable-msg=R0914 import sys import os try: ## # Arg parse is not installed on older Python distributions. # ovs ships with a version in the directory mentioned below. import argparse except ImportError: sys.path.append(os.path.join("@pkgdatadir@", "python")) import argparse import logging import re import unittest import copy import curses import operator import subprocess import fcntl import struct import termios import datetime import threading import time import socket ## # The following two definitions provide the necessary netaddr functionality. # Python netaddr module is not part of the core installation. Packaging # netaddr was involved and seems inappropriate given that only two # methods where used. def ipv4_to_network(ip_str): """ Calculate the network given a ipv4/mask value. If a mask is not present simply return ip_str. """ pack_length = '!HH' try: (ip, mask) = ip_str.split("/") except ValueError: # just an ip address no mask. return ip_str ip_p = socket.inet_pton(socket.AF_INET, ip) ip_t = struct.unpack(pack_length, ip_p) mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET, mask)) network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] return socket.inet_ntop(socket.AF_INET, struct.pack('!HH', network_n[0], network_n[1])) def ipv6_to_network(ip_str): """ Calculate the network given a ipv6/mask value. If a mask is not present simply return ip_str. """ pack_length = '!HHHHHHHH' try: (ip, mask) = ip_str.split("/") except ValueError: # just an ip address no mask. return ip_str ip_p = socket.inet_pton(socket.AF_INET6, ip) ip_t = struct.unpack(pack_length, ip_p) mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET6, mask)) network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] return socket.inet_ntop(socket.AF_INET6, struct.pack(pack_length, network_n[0], network_n[1], network_n[2], network_n[3], network_n[4], network_n[5], network_n[6], network_n[7])) ## # columns displayed ## class Columns: """ Holds column specific content. Titles needs to be less than 8 characters. """ VALUE_WIDTH = 9 FIELDS = "fields" PACKETS = "packets" COUNT = "count" BYTES = "bytes" AVERAGE = "average" def __init__(self): pass @staticmethod def assoc_list(obj): """ Return a associated list. """ return [(Columns.FIELDS, repr(obj)), (Columns.PACKETS, obj.packets), (Columns.BYTES, obj.bytes), (Columns.COUNT, obj.count), (Columns.AVERAGE, obj.average), ] def element_eth_get(field_type, element, stats_dict): """ Extract eth frame src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element = fmt % (field_type, element["src"], element["dst"]) return SumData(field_type, element, stats_dict["packets"], stats_dict["bytes"], element) def element_ipv4_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element_show = fmt % (field_type, element["src"], element["dst"]) element_key = fmt % (field_type, ipv4_to_network(element["src"]), ipv4_to_network(element["dst"])) return SumData(field_type, element_show, stats_dict["packets"], stats_dict["bytes"], element_key) def element_tunnel_get(field_type, element, stats_dict): """ Extract src and dst from a tunnel.""" return element_ipv4_get(field_type, element, stats_dict) def element_ipv6_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element_show = fmt % (field_type, element["src"], element["dst"]) element_key = fmt % (field_type, ipv6_to_network(element["src"]), ipv6_to_network(element["dst"])) return SumData(field_type, element_show, stats_dict["packets"], stats_dict["bytes"], element_key) def element_dst_port_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" element_key = "%s(dst=%s)" % (field_type, element["dst"]) return SumData(field_type, element_key, stats_dict["packets"], stats_dict["bytes"], element_key) def element_passthrough_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" element_key = "%s(%s)" % (field_type, element) return SumData(field_type, element_key, stats_dict["packets"], stats_dict["bytes"], element_key) # pylint: disable-msg=R0903 class OutputFormat: """ Holds field_type and function to extract element value. """ def __init__(self, field_type, elements, generator): self.field_type = field_type self.elements = elements self.generator = generator ## # The order below is important. The initial flow field depends on whether # --script or top mode is used. In top mode, the expected behavior, in_port # flow fields are shown first. A future feature will allow users to # filter output by selecting a row. Filtering by in_port is a natural # filtering starting point. # # In script mode, all fields are shown. The expectation is that users could # filter output by piping through grep. # # In top mode, the default flow field is in_port. In --script mode, # the default flow field is all. # # All is added to the end of the OUTPUT_FORMAT list. ## OUTPUT_FORMAT = [ OutputFormat("in_port", (), element_passthrough_get), OutputFormat("eth", ("src","dst"), element_eth_get), OutputFormat("eth_type", (), element_passthrough_get), OutputFormat("ipv4", ("src","dst"), element_ipv4_get), OutputFormat("ipv6", ("src","dst"), element_ipv6_get), OutputFormat("udp", ("src","dst"), element_dst_port_get), OutputFormat("tcp", ("src","dst"), element_dst_port_get), OutputFormat("tunnel", ("src","dst"), element_tunnel_get), ] ## ELEMENT_KEY = { "udp": "udp.dst", "tcp": "tcp.dst" } def top_input_get(args): """ Return subprocess stdout.""" cmd = [] if (args.host): cmd += ["ssh", args.host] cmd += ["ovs-dpctl", "dump-flows"] return subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout def args_get(): """ read program parameters handle any necessary validation of input. """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__) ## # None is a special value indicating to read flows from stdin. # This handles the case # ovs-dpctl dump-flows | ovs-dpctl-flows.py parser.add_argument("-v", "--version", version="@VERSION@@VERSION_SUFFIX@", action="version", help="show version") parser.add_argument("-f", "--flow-file", dest="flowFiles", default=None, action="append", help="file containing flows from ovs-dpctl dump-flow") parser.add_argument("-V", "--verbose", dest="verbose", default=logging.CRITICAL, action="store_const", const=logging.DEBUG, help="enable debug level verbosity") parser.add_argument("-s", "--script", dest="top", action="store_false", help="Run from a script (no user interface)") parser.add_argument("--host", dest="host", help="Specify a user@host for retrieving flows see" "Accessing Remote Hosts for more information") parser.add_argument("-a", "--accumulate", dest="accumulate", action="store_true", default=False, help="Accumulate dump-flow content") parser.add_argument("--accumulate-decay", dest="accumulateDecay", default=5.0 * 60, type=float, help="Decay old accumulated flows. " "The default is 5 minutes. " "A value of 0 disables decay.") parser.add_argument("-d", "--delay", dest="delay", type=int, default=1000, help="Delay in milliseconds to collect dump-flow " "content (sample rate).") args = parser.parse_args() logging.basicConfig(level=args.verbose) return args ### # Code to parse a single line in dump-flow ### # key(values) FIELDS_CMPND = re.compile(r"([\w]+)\((.+)\)") # key:value FIELDS_CMPND_ELEMENT = re.compile(r"([\w:]+)=([/\.\w:]+)") FIELDS_ELEMENT = re.compile(r"([\w]+):([-\.\w]+)") def flow_line_iter(line): """ iterate over flow dump elements. return tuples of (true, element) or (false, remaining element) """ # splits by , except for when in a (). Actions element was not # split properly but we don't need it. rc = [] element = "" paren_count = 0 for ch in line: if (ch == '('): paren_count += 1 elif (ch == ')'): paren_count -= 1 if (ch == ' '): # ignore white space. continue elif ((ch == ',') and (paren_count == 0)): rc.append(element) element = "" else: element += ch if (paren_count): raise ValueError(line) else: if (len(element) > 0): rc.append(element) return rc def flow_line_compound_parse(compound): """ Parse compound element for example src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03 which is in eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03) """ result = {} for element in flow_line_iter(compound): match = FIELDS_CMPND_ELEMENT.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = value match = FIELDS_CMPND.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = flow_line_compound_parse(value) continue if (len(result.keys()) == 0): return compound return result def flow_line_split(line): """ Convert a flow dump line into ([fields], [stats], actions) tuple. Where fields and stats are lists. This function relies on a the following ovs-dpctl dump-flow output characteristics: 1. The dumpe flow line consists of a list of frame fields, list of stats and action. 2. list of frame fields, each stat and action field are delimited by ', '. 3. That all other non stat field are not delimited by ', '. """ results = re.split(', ', line) (field, stats, action) = (results[0], results[1:-1], results[-1]) fields = flow_line_iter(field) return (fields, stats, action) def elements_to_dict(elements): """ Convert line to a hierarchy of dictionaries. """ result = {} for element in elements: if (element == "eth()"): continue match = FIELDS_CMPND.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = flow_line_compound_parse(value) continue match = FIELDS_ELEMENT.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = value else: raise ValueError("can't parse >%s<" % element) return result # pylint: disable-msg=R0903 class SumData(object): """ Interface that all data going into SumDb must implement. Holds the flow field and its corresponding count, total packets, total bytes and calculates average. __repr__ is used as key into SumData singleton. __str__ is used as human readable output. """ def __init__(self, field_type, field, packets, flow_bytes, key): # Count is the number of lines in the dump-flow log. self.field_type = field_type self.field = field self.count = 1 self.packets = int(packets) self.bytes = int(flow_bytes) self.key = key def decrement(self, decr_packets, decr_bytes, decr_count): """ Decrement content to calculate delta from previous flow sample.""" self.packets -= decr_packets self.bytes -= decr_bytes self.count -= decr_count def __iadd__(self, other): """ Add two objects. """ if (self.key != other.key): raise ValueError("adding two unrelated types") self.count += other.count self.packets += other.packets self.bytes += other.bytes return self def __isub__(self, other): """ Decrement two objects. """ if (self.key != other.key): raise ValueError("adding two unrelated types") self.count -= other.count self.packets -= other.packets self.bytes -= other.bytes return self def __getattr__(self, name): """ Handle average. """ if (name == "average"): if (self.packets == 0): return float(0.0) else: return float(self.bytes) / float(self.packets) raise AttributeError(name) def __str__(self): """ Used for debugging. """ return "%s %s %s %s" % (self.field, self.count, self.packets, self.bytes) def __repr__(self): """ Used as key in the FlowDB table. """ return self.key def flow_aggregate(fields_dict, stats_dict): """ Search for content in a line. Passed the flow port of the dump-flows plus the current stats consisting of packets, bytes, etc """ result = [] for output_format in OUTPUT_FORMAT: field = fields_dict.get(output_format.field_type, None) if (field) and all (k in field for k in output_format.elements): obj = output_format.generator(output_format.field_type, field, stats_dict) result.append(obj) return result def flows_read(ihdl, flow_db): """ read flow content from ihdl and insert into flow_db. """ done = False while (not done): line = ihdl.readline() if (len(line) == 0): # end of input break try: flow_db.flow_line_add(line) except ValueError as arg: logging.error(arg) return flow_db def get_terminal_size(): """ return column width and height of the terminal """ for fd_io in [0, 1, 2]: try: result = struct.unpack('hh', fcntl.ioctl(fd_io, termios.TIOCGWINSZ, '1234')) except IOError: result = None continue if (result is None or result == (0, 0)): # Maybe we can't get the width. In that case assume (25, 80) result = (25, 80) return result ## # Content derived from: # http://getpython3.com/diveintopython3/your-first-python-program.html#divingin ## SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']} def approximate_size(size, a_kilobyte_is_1024_bytes=True): """Convert a file size to human-readable form. Keyword arguments: size -- file size in bytes a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 if False, use multiples of 1000 Returns: string """ size = float(size) if size < 0: raise ValueError('number must be non-negative') if (a_kilobyte_is_1024_bytes): multiple = 1024 else: multiple = 1000 for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: return "%.1f %s" % (size, suffix) raise ValueError('number too large') ## # End copied content ## class ColMeta: """ Concepts about columns. """ def __init__(self, sortable, width): self.sortable = sortable self.width = width class RowMeta: """ How to render rows. """ def __init__(self, label, fmt): self.label = label self.fmt = fmt def fmt_packet(obj, width): """ Provide a string for packets that is appropriate for output.""" return str(obj.packets).rjust(width) def fmt_count(obj, width): """ Provide a string for average that is appropriate for output.""" return str(obj.count).rjust(width) def fmt_avg(obj, width): """ Provide a string for average that is appropriate for output.""" return str(int(obj.average)).rjust(width) def fmt_field(obj, width): """ truncate really long flow and insert ellipses to help make it clear. """ ellipses = " ... " value = obj.field if (len(obj.field) > width): value = value[:(width - len(ellipses))] + ellipses return value.ljust(width) def fmt_bytes(obj, width): """ Provide a string for average that is appropriate for output.""" if (len(str(obj.bytes)) <= width): value = str(obj.bytes) else: value = approximate_size(obj.bytes) return value.rjust(width) def title_center(value, width): """ Center a column title.""" return value.upper().center(width) def title_rjust(value, width): """ Right justify a column title. """ return value.upper().rjust(width) def column_picker(order, obj): """ return the column as specified by order. """ if (order == 1): return obj.count elif (order == 2): return obj.packets elif (order == 3): return obj.bytes elif (order == 4): return obj.average else: raise ValueError("order outside of range %s" % order) class Render: """ Renders flow data. The two FIELD_SELECT variables should be set to the actual field minus 1. During construction, an internal method increments and initializes this object. """ FLOW_FIELDS = [_field.field_type for _field in OUTPUT_FORMAT] + ["all"] FIELD_SELECT_SCRIPT = 7 FIELD_SELECT_TOP = -1 def __init__(self, console_width, field_select): """ Calculate column widths taking into account changes in format.""" self._start_time = datetime.datetime.now() self._cols = [ColMeta(False, 0), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH)] self._console_width = console_width self.console_width_set(console_width) # Order in this array dictate the order of the columns. # The 0 width for the first entry is a place holder. This is # dynamically calculated. The first column is special. We need a # way to indicate which field are presented. self._descs = [RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust)] self._column_sort_select = 0 self.column_select_event() self._titles = [ RowMeta(Columns.FIELDS, title_center), RowMeta(Columns.COUNT, title_rjust), RowMeta(Columns.PACKETS, title_rjust), RowMeta(Columns.BYTES, title_rjust), RowMeta(Columns.AVERAGE, title_rjust) ] self._datas = [ RowMeta(None, fmt_field), RowMeta(None, fmt_count), RowMeta(None, fmt_packet), RowMeta(None, fmt_bytes), RowMeta(None, fmt_avg) ] ## # _field_types hold which fields are displayed in the field # column, with the keyword all implying all fields. ## self._field_types = Render.FLOW_FIELDS ## # The default is to show all field types. ## self._field_type_select = field_select self.field_type_toggle() def _field_type_select_get(self): """ Return which field type to display. """ return self._field_types[self._field_type_select] def field_type_toggle(self): """ toggle which field types to show. """ self._field_type_select += 1 if (self._field_type_select >= len(self._field_types)): self._field_type_select = 0 value = Columns.FIELDS + " (%s)" % self._field_type_select_get() self._titles[0].label = value def column_select_event(self): """ Handles column select toggle. """ self._descs[self._column_sort_select].label = "" for _ in range(len(self._cols)): self._column_sort_select += 1 if (self._column_sort_select >= len(self._cols)): self._column_sort_select = 0 # Now look for the next sortable column if (self._cols[self._column_sort_select].sortable): break self._descs[self._column_sort_select].label = "DESC" def console_width_set(self, console_width): """ Adjust the output given the new console_width. """ self._console_width = console_width spaces = len(self._cols) - 1 ## # Calculating column width can be tedious but important. The # flow field value can be long. The goal here is to dedicate # fixed column space for packets, bytes, average and counts. Give the # remaining space to the flow column. When numbers get large # transition output to output generated by approximate_size which # limits output to ###.# XiB in other words 9 characters. ## # At this point, we know the maximum length values. We may # truncate the flow column to get everything to fit. self._cols[0].width = 0 values_max_length = sum([ii.width for ii in self._cols]) + spaces flow_max_length = console_width - values_max_length self._cols[0].width = flow_max_length def format(self, flow_db): """ shows flows based on --script parameter.""" rc = [] ## # Top output consists of # Title # Column title (2 rows) # data # statistics and status ## # Title ## rc.append("Flow Summary".center(self._console_width)) stats = " Total: %(flow_total)s errors: %(flow_errors)s " % \ flow_db.flow_stats_get() accumulate = flow_db.accumulate_get() if (accumulate): stats += "Accumulate: on " else: stats += "Accumulate: off " duration = datetime.datetime.now() - self._start_time stats += "Duration: %s " % str(duration) rc.append(stats.ljust(self._console_width)) ## # 2 rows for columns. ## # Indicate which column is in descending order. rc.append(" ".join([ii.fmt(ii.label, col.width) for (ii, col) in zip(self._descs, self._cols)])) rc.append(" ".join([ii.fmt(ii.label, col.width) for (ii, col) in zip(self._titles, self._cols)])) ## # Data. ## for dd in flow_db.field_values_in_order(self._field_type_select_get(), self._column_sort_select): rc.append(" ".join([ii.fmt(dd, col.width) for (ii, col) in zip(self._datas, self._cols)])) return rc def curses_screen_begin(): """ begin curses screen control. """ stdscr = curses.initscr() curses.cbreak() curses.noecho() stdscr.keypad(1) return stdscr def curses_screen_end(stdscr): """ end curses screen control. """ curses.nocbreak() stdscr.keypad(0) curses.echo() curses.endwin() class FlowDB: """ Implements live vs accumulate mode. Flows are stored as key value pairs. The key consists of the content prior to stat fields. The value portion consists of stats in a dictionary form. @ \todo future add filtering here. """ def __init__(self, accumulate): self._accumulate = accumulate self._error_count = 0 # Values are (stats, last update time.) # The last update time is used for aging. self._flow_lock = threading.Lock() # This dictionary holds individual flows. self._flows = {} # This dictionary holds aggregate of flow fields. self._fields = {} def accumulate_get(self): """ Return the current accumulate state. """ return self._accumulate def accumulate_toggle(self): """ toggle accumulate flow behavior. """ self._accumulate = not self._accumulate def begin(self): """ Indicate the beginning of processing flow content. if accumulate is false clear current set of flows. """ if (not self._accumulate): self._flow_lock.acquire() try: self._flows.clear() finally: self._flow_lock.release() self._fields.clear() def flow_line_add(self, line): """ Split a line from a ovs-dpctl dump-flow into key and stats. The order of the content in the flow should be: - flow content - stats for the flow - actions This method also assumes that the dump flow output does not change order of fields of the same flow. """ if not isinstance(line, str): line = str(line) line = line.rstrip("\n") (fields, stats, _) = flow_line_split(line) try: fields_dict = elements_to_dict(fields) if (len(fields_dict) == 0): raise ValueError("flow fields are missing %s", line) stats_dict = elements_to_dict(stats) if not all (k in stats_dict for k in ("packets","bytes")): raise ValueError("statistics are missing %s.", line) ## # In accumulate mode, the Flow database can reach 10,000's of # persistent flows. The interaction of the script with this many # flows is too slow. Instead, delta are sent to the flow_db # database allow incremental changes to be done in O(m) time # where m is the current flow list, instead of iterating over # all flows in O(n) time where n is the entire history of flows. key = ",".join(fields) self._flow_lock.acquire() try: (stats_old_dict, _) = self._flows.get(key, (None, None)) finally: self._flow_lock.release() self.flow_event(fields_dict, stats_old_dict, stats_dict) except ValueError as arg: logging.error(arg) self._error_count += 1 raise self._flow_lock.acquire() try: self._flows[key] = (stats_dict, datetime.datetime.now()) finally: self._flow_lock.release() def decay(self, decayTimeInSeconds): """ Decay content. """ now = datetime.datetime.now() for (key, value) in list(self._flows.items()): (stats_dict, updateTime) = value delta = now - updateTime if (delta.seconds > decayTimeInSeconds): self._flow_lock.acquire() try: del self._flows[key] fields_dict = elements_to_dict(flow_line_iter(key)) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: self.field_dec(match) finally: self._flow_lock.release() def flow_stats_get(self): """ Return statistics in a form of a dictionary. """ rc = None self._flow_lock.acquire() try: rc = {"flow_total": len(self._flows), "flow_errors": self._error_count} finally: self._flow_lock.release() return rc def field_types_get(self): """ Return the set of types stored in the singleton. """ types = set((ii.field_type for ii in self._fields.values())) return types def field_add(self, data): """ Collect dump-flow data to sum number of times item appears. """ current = self._fields.get(repr(data), None) if (current is None): current = copy.copy(data) else: current += data self._fields[repr(current)] = current def field_dec(self, data): """ Collect dump-flow data to sum number of times item appears. """ current = self._fields.get(repr(data), None) if (current is None): raise ValueError("decrementing field missing %s" % repr(data)) current -= data self._fields[repr(current)] = current if (current.count == 0): del self._fields[repr(current)] def field_values_in_order(self, field_type_select, column_order): """ Return a list of items in order maximum first. """ values = self._fields.values() if (field_type_select != "all"): # If a field type other than "all" then reduce the list. values = [ii for ii in values if (ii.field_type == field_type_select)] values = [(column_picker(column_order, ii), ii) for ii in values] values.sort(key=operator.itemgetter(0)) values.reverse() values = [ii[1] for ii in values] return values def flow_event(self, fields_dict, stats_old_dict, stats_new_dict): """ Receives new flow information. """ # In order to avoid processing every flow at every sample # period, changes in flow packet count is used to determine the # delta in the flow statistics. This delta is used in the call # to self.decrement prior to self.field_add if (stats_old_dict is None): # This is a new flow matches = flow_aggregate(fields_dict, stats_new_dict) for match in matches: self.field_add(match) else: old_packets = int(stats_old_dict.get("packets", 0)) new_packets = int(stats_new_dict.get("packets", 0)) if (old_packets == new_packets): # ignore. same data. pass else: old_bytes = stats_old_dict.get("bytes", 0) # old_packets != new_packets # if old_packets > new_packets then we end up decrementing # packets and bytes. matches = flow_aggregate(fields_dict, stats_new_dict) for match in matches: match.decrement(int(old_packets), int(old_bytes), 1) self.field_add(match) class DecayThread(threading.Thread): """ Periodically call flow database to see if any flows are old. """ def __init__(self, flow_db, interval): """ Start decay thread. """ threading.Thread.__init__(self) self._interval = max(1, interval) self._min_interval = min(1, interval / 10) self._flow_db = flow_db self._event = threading.Event() self._running = True self.daemon = True def run(self): """ Worker thread which handles decaying accumulated flows. """ while(self._running): self._event.wait(self._min_interval) if (self._running): self._flow_db.decay(self._interval) def stop(self): """ Stop thread. """ self._running = False self._event.set() ## # Give the calling thread time to terminate but not too long. # this thread is a daemon so the application will terminate if # we timeout during the join. This is just a cleaner way to # release resources. self.join(2.0) def flow_top_command(stdscr, render, flow_db): """ Handle input while in top mode. """ ch = stdscr.getch() ## # Any character will restart sampling. if (ch == ord('h')): # halt output. ch = stdscr.getch() while (ch == -1): ch = stdscr.getch() if (ch == ord('s')): # toggle which column sorts data in descending order. render.column_select_event() elif (ch == ord('a')): flow_db.accumulate_toggle() elif (ch == ord('f')): render.field_type_toggle() elif (ch == ord(' ')): # resample pass return ch def decay_timer_start(flow_db, accumulateDecay): """ If accumulateDecay greater than zero then start timer. """ if (accumulateDecay > 0): decay_timer = DecayThread(flow_db, accumulateDecay) decay_timer.start() return decay_timer else: return None def flows_top(args): """ handles top like behavior when --script is not specified. """ flow_db = FlowDB(args.accumulate) render = Render(0, Render.FIELD_SELECT_TOP) decay_timer = decay_timer_start(flow_db, args.accumulateDecay) lines = [] try: stdscr = curses_screen_begin() try: ch = 'X' #stdscr.nodelay(1) stdscr.timeout(args.delay) while (ch != ord('q')): flow_db.begin() try: ihdl = top_input_get(args) try: flows_read(ihdl, flow_db) finally: ihdl.close() except OSError as arg: logging.critical(arg) break (console_height, console_width) = stdscr.getmaxyx() render.console_width_set(console_width) output_height = console_height - 1 line_count = range(output_height) line_output = render.format(flow_db) lines = zip(line_count, line_output[:output_height]) stdscr.erase() for (count, line) in lines: stdscr.addstr(count, 0, line[:console_width]) stdscr.refresh() ch = flow_top_command(stdscr, render, flow_db) finally: curses_screen_end(stdscr) except KeyboardInterrupt: pass if (decay_timer): decay_timer.stop() # repeat output for (count, line) in lines: print(line) def flows_script(args): """ handles --script option. """ flow_db = FlowDB(args.accumulate) flow_db.begin() if (args.flowFiles is None): logging.info("reading flows from stdin") flow_db = flows_read(sys.stdin, flow_db) else: for flowFile in args.flowFiles: logging.info("reading flows from %s", flowFile) ihdl = open(flowFile, "r") try: flow_db = flows_read(ihdl, flow_db) finally: ihdl.close() (_, console_width) = get_terminal_size() render = Render(console_width, Render.FIELD_SELECT_SCRIPT) for line in render.format(flow_db): print(line) def main(): """ Return 0 on success or 1 on failure. Algorithm There are four stages to the process ovs-dpctl dump-flow content. 1. Retrieve current input 2. store in FlowDB and maintain history 3. Iterate over FlowDB and aggregating stats for each flow field 4. present data. Retrieving current input is currently trivial, the ovs-dpctl dump-flow is called. Future version will have more elaborate means for collecting dump-flow content. FlowDB returns all data as in the form of a hierarchical dictionary. Input will vary. In the case of accumulate mode, flows are not purged from the FlowDB manager. Instead at the very least, merely the latest statistics are kept. In the case, of live output the FlowDB is purged prior to sampling data. Aggregating results requires identify flow fields to aggregate out of the flow and summing stats. """ args = args_get() try: if (args.top): flows_top(args) else: flows_script(args) except KeyboardInterrupt: return 1 return 0 if __name__ == '__main__': sys.exit(main()) elif __name__ == 'ovs-dpctl-top': # pylint: disable-msg=R0915 ## # Test case beyond this point. # pylint: disable-msg=R0904 class TestsuiteFlowParse(unittest.TestCase): """ parse flow into hierarchy of dictionaries. """ def test_flow_parse(self): """ test_flow_parse. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:1, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) flow_dict = elements_to_dict(fields + stats) self.assertEqual(flow_dict["eth"]["src"], "00:50:56:b4:4e:f8") self.assertEqual(flow_dict["eth"]["dst"], "33:33:00:01:00:03") self.assertEqual(flow_dict["ipv6"]["src"], "fe80::55bf:fe42:bc96:2812") self.assertEqual(flow_dict["ipv6"]["dst"], "ff02::1:3") self.assertEqual(flow_dict["packets"], "1") self.assertEqual(flow_dict["bytes"], "92") line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:1, bytes:92, "\ "used:-0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) flow_dict = elements_to_dict(fields + stats) self.assertEqual(flow_dict["used"], "-0.703s") self.assertEqual(flow_dict["packets"], "1") self.assertEqual(flow_dict["bytes"], "92") def test_flow_sum(self): """ test_flow_sum. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) stats_dict = elements_to_dict(stats) fields_dict = elements_to_dict(fields) ## # Test simple case of one line. flow_db = FlowDB(False) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) flow_types = flow_db.field_types_get() expected_flow_types = ["eth", "eth_type", "udp", "in_port", "ipv6"] self.assert_(len(flow_types) == len(expected_flow_types)) for flow_type in flow_types: self.assertTrue(flow_type in expected_flow_types) for flow_type in flow_types: sum_value = flow_db.field_values_in_order("all", 1) self.assert_(len(sum_value) == 5) self.assert_(sum_value[0].packets == 2) self.assert_(sum_value[0].count == 1) self.assert_(sum_value[0].bytes == 92) ## # Add line again just to see counts go up. matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) flow_types = flow_db.field_types_get() self.assert_(len(flow_types) == len(expected_flow_types)) for flow_type in flow_types: self.assertTrue(flow_type in expected_flow_types) for flow_type in flow_types: sum_value = flow_db.field_values_in_order("all", 1) self.assert_(len(sum_value) == 5) self.assert_(sum_value[0].packets == 4) self.assert_(sum_value[0].count == 2) self.assert_(sum_value[0].bytes == 2 * 92) def test_assoc_list(self): """ test_assoc_list. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" valid_flows = [ 'eth_type(0x86dd)', 'udp(dst=5355)', 'in_port(4)', 'ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3)', 'eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)' ] (fields, stats, _) = flow_line_split(line) stats_dict = elements_to_dict(stats) fields_dict = elements_to_dict(fields) ## # Test simple case of one line. flow_db = FlowDB(False) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) for sum_value in flow_db.field_values_in_order("all", 1): assoc_list = Columns.assoc_list(sum_value) for item in assoc_list: if (item[0] == "fields"): self.assertTrue(item[1] in valid_flows) elif (item[0] == "packets"): self.assertTrue(item[1] == 2) elif (item[0] == "count"): self.assertTrue(item[1] == 1) elif (item[0] == "average"): self.assertTrue(item[1] == 46.0) elif (item[0] == "bytes"): self.assertTrue(item[1] == 92) else: raise ValueError("unknown %s", item[0]) def test_human_format(self): """ test_assoc_list. """ self.assertEqual(approximate_size(0.0), "0.0 KiB") self.assertEqual(approximate_size(1024), "1.0 KiB") self.assertEqual(approximate_size(1024 * 1024), "1.0 MiB") self.assertEqual(approximate_size((1024 * 1024) + 100000), "1.1 MiB") value = (1024 * 1024 * 1024) + 100000000 self.assertEqual(approximate_size(value), "1.1 GiB") def test_flow_line_split(self): """ Splitting a flow line is not trivial. There is no clear delimiter. Comma is used liberally.""" expected_fields = ["in_port(4)", "eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)", "eth_type(0x86dd)", "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3," "label=0,proto=17,tclass=0,hlimit=1,frag=no)", "udp(src=61252,dst=5355)"] expected_stats = ["packets:2", "bytes:92", "used:0.703s"] expected_actions = "actions:3,8,11,14,17,20,23,26,29,32,35," \ "38,41,44,47,50,53,56,59,62,65" line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, actions) = flow_line_split(line) self.assertEqual(fields, expected_fields) self.assertEqual(stats, expected_stats) self.assertEqual(actions, expected_actions) def test_accumulate_decay(self): """ test_accumulate_decay: test accumulated decay. """ lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), " "packets:1, bytes:120, used:0.004s, actions:1"] flow_db = FlowDB(True) flow_db.begin() flow_db.flow_line_add(lines[0]) # Make sure we decay time.sleep(4) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.decay(1) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) flow_db.flow_line_add(lines[0]) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.decay(30) # Should not be deleted. self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.flow_line_add(lines[0]) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) timer = decay_timer_start(flow_db, 2) time.sleep(10) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) timer.stop() def test_accumulate(self): """ test_accumulate test that FlowDB supports accumulate. """ lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), " "packets:1, bytes:120, used:0.004s, actions:1", "in_port(2)," "eth(src=68:ef:bd:25:ef:c0,dst=33:33:00:00:00:66)," "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029), " "packets:2, bytes:5026, used:0.348s, actions:1", "in_port(1),eth(src=ee:ee:ee:ee:ee:ee," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), packets:2, " "bytes:240, used:0.004s, actions:1"] lines = [ "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", "in_port(2),eth_type(0x0806), packets:2, bytes:126, actions:1", "in_port(1),eth_type(0x0806), packets:2, bytes:240, actions:1", "in_port(1),eth_type(0x0800), packets:1, bytes:120, actions:1", "in_port(1),eth_type(0x0800), packets:2, bytes:240, actions:1", "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", ] # Turn on accumulate. flow_db = FlowDB(True) flow_db.begin() flow_db.flow_line_add(lines[0]) # Test one flow exist. sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 1) self.assertEqual(in_ports[0].bytes, 120) self.assertEqual(in_ports[0].count, 1) # simulate another sample # Test two different flows exist. flow_db.begin() flow_db.flow_line_add(lines[1]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 1) self.assertEqual(in_ports[0].bytes, 120) self.assertEqual(in_ports[0].count, 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Test first flow increments packets. flow_db.begin() flow_db.flow_line_add(lines[2]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 240) self.assertEqual(in_ports[0].count, 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Test third flow but with the same in_port(1) as the first flow. flow_db.begin() flow_db.flow_line_add(lines[3]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 3) self.assertEqual(in_ports[0].bytes, 360) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Third flow has changes. flow_db.begin() flow_db.flow_line_add(lines[4]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 4) self.assertEqual(in_ports[0].bytes, 480) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # First flow reset. flow_db.begin() flow_db.flow_line_add(lines[5]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 3) self.assertEqual(in_ports[0].bytes, 360) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) def test_parse_character_errors(self): """ test_parsing errors. The flow parses is purposely loose. Its not designed to validate input. Merely pull out what it can but there are situations that a parse error can be detected. """ lines = ["complete garbage", "in_port(2),eth(src=68:ef:bd:25:ef:c0," "dst=33:33:00:00:00:66)," "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029)," "packets:2,bytes:5026,actions:1"] flow_db = FlowDB(False) flow_db.begin() for line in lines: try: flow_db.flow_line_add(line) except ValueError: # We want an exception. That is how we know we have # correctly found a simple parsing error. We are not # looking to validate flow output just catch simple issues. continue self.assertTrue(False) def test_tunnel_parsing(self): """ test_tunnel_parsing test parse flows with tunnel. """ lines = [ "tunnel(tun_id=0x0,src=192.168.1.1,dst=192.168.1.10," "tos=0x0,ttl=64,flags(key)),in_port(1)," "eth(src=9e:40:f5:ef:ec:ee,dst=01:23:20:00:00:30)," "eth_type(0x8902), packets:6, bytes:534, used:0.128s, " "actions:userspace(pid=4294962691,slow_path(cfm))" ] flow_db = FlowDB(False) flow_db.begin() flow_db.flow_line_add(lines[0]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 6) self.assertEqual(in_ports[0].bytes, 534) self.assertEqual(in_ports[0].count, 1) def test_flow_multiple_paren(self): """ test_flow_multiple_paren. """ line = "tunnel(tun_id=0x0,src=192.168.1.1,flags(key)),in_port(2)" valid = ["tunnel(tun_id=0x0,src=192.168.1.1,flags(key))", "in_port(2)"] rc = flow_line_iter(line) self.assertEqual(valid, rc) def test_to_network(self): """ test_to_network test ipv4_to_network and ipv6_to_network. """ ipv4s = [ ("192.168.0.1", "192.168.0.1"), ("192.168.0.1/255.255.255.255", "192.168.0.1"), ("192.168.0.1/255.255.255.0", "192.168.0.0"), ("192.168.0.1/255.255.0.0", "192.168.0.0"), ("192.168.0.1/255.0.0.0", "192.0.0.0"), ("192.168.0.1/0.0.0.0", "0.0.0.0"), ("10.24.106.230/255.255.255.255", "10.24.106.230"), ("10.24.106.230/255.255.255.0", "10.24.106.0"), ("10.24.106.0/255.255.255.0", "10.24.106.0"), ("10.24.106.0/255.255.252.0", "10.24.104.0") ] ipv6s = [ ("1::192:168:0:1", "1::192:168:0:1"), ("1::192:168:0:1/1::ffff:ffff:ffff:ffff", "1::192:168:0:1"), ("1::192:168:0:1/1::ffff:ffff:ffff:0", "1::192:168:0:0"), ("1::192:168:0:1/1::ffff:ffff:0:0", "1::192:168:0:0"), ("1::192:168:0:1/1::ffff:0:0:0", "1::192:0:0:0"), ("1::192:168:0:1/1::0:0:0:0", "1::"), ("1::192:168:0:1/::", "::") ] for (ipv4_test, ipv4_check) in ipv4s: self.assertEqual(ipv4_to_network(ipv4_test), ipv4_check) for (ipv6_test, ipv6_check) in ipv6s: self.assertEqual(ipv6_to_network(ipv6_test), ipv6_check) def test_ui(self): """ test_ui: test expected ui behavior. """ #pylint: disable=W0212 top_render = Render(80, Render.FIELD_SELECT_TOP) script_render = Render(80, Render.FIELD_SELECT_SCRIPT) self.assertEqual(top_render._field_type_select_get(), "in_port") self.assertEqual(script_render._field_type_select_get(), "all") #pylint: enable=W0212 openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-dpctl.8.in000066400000000000000000000040331514270232600234040ustar00rootroot00000000000000.so lib/ovs.tmac .TH ovs\-dpctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-dpctl . .SH NAME ovs\-dpctl \- administer Open vSwitch datapaths . .SH SYNOPSIS .B ovs\-dpctl [\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...] . .SH DESCRIPTION .PP The \fBovs\-dpctl\fR program can create, modify, and delete Open vSwitch datapaths. A single machine may host any number of datapaths. .PP This program works only with datapaths that are implemented outside of \fBovs\-vswitchd\fR itself, such as the Linux and Windows kernel-based datapaths. To manage datapaths that are integrated into \fBovs\-vswitchd\fR, such as the userspace (\fBnetdev\fR) datapath, use \fBovs\-appctl\fR(8) to invoke the \fBdpctl/*\fR commands, which are documented in \fBovs\-vswitchd\fR(8). .PP A newly created datapath is associated with only one network device, a virtual network device sometimes called the datapath's ``local port''. A newly created datapath is not, however, associated with any of the host's other network devices. To intercept and process traffic on a given network device, use the \fBadd\-if\fR command to explicitly add that network device to the datapath. .PP If \fBovs\-vswitchd\fR(8) is in use, use \fBovs\-vsctl\fR(8) instead of \fBovs\-dpctl\fR. .PP Most \fBovs\-dpctl\fR commands that work with datapaths take an argument that specifies the name of the datapath. Datapath names take the form [\fItype\fB@\fR]\fIname\fR, where \fIname\fR is the network device associated with the datapath's local port. If \fItype\fR is given, it specifies the datapath provider of \fIname\fR, otherwise the default provider \fBsystem\fR is assumed. .PP The following commands manage datapaths. . .ds DX .de DO \\$1 \\$2 \\$3 .. .so lib/dpctl.man . .SH OPTIONS .IP "\fB\-t\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" Limits \fBovs\-dpctl\fR runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBovs\-dpctl\fR will exit with a \fBSIGALRM\fR signal. . .so lib/vlog.man .so lib/common.man . .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-vswitchd (8) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-dpctl.c000066400000000000000000000170301514270232600230530ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "dpctl.h" #include "fatal-signal.h" #include "odp-util.h" #include "packets.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" static struct dpctl_params dpctl_p; OVS_NO_RETURN static void usage(void *userdata OVS_UNUSED); static void parse_options(int argc, char *argv[]); static void dpctl_print(void *userdata OVS_UNUSED, bool error, const char *msg) { FILE *outfile = error ? stderr : stdout; fputs(msg, outfile); } int main(int argc, char *argv[]) { int error; set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); dpctl_p.is_appctl = false; dpctl_p.output = dpctl_print; dpctl_p.usage = usage; error = dpctl_run_command(argc - optind, (const char **) argv + optind, &dpctl_p); return error ? EXIT_FAILURE : EXIT_SUCCESS; } static void parse_options(int argc, char *argv[]) { enum { OPT_CLEAR = UCHAR_MAX + 1, OPT_MAY_CREATE, OPT_READ_ONLY, OPT_NAMES, OPT_NO_NAMES, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"statistics", no_argument, NULL, 's'}, {"clear", no_argument, NULL, OPT_CLEAR}, {"may-create", no_argument, NULL, OPT_MAY_CREATE}, {"read-only", no_argument, NULL, OPT_READ_ONLY}, {"more", no_argument, NULL, 'm'}, {"names", no_argument, NULL, OPT_NAMES}, {"no-names", no_argument, NULL, OPT_NO_NAMES}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); bool set_names = false; unsigned int timeout = 0; for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 's': dpctl_p.print_statistics = true; break; case OPT_CLEAR: dpctl_p.zero_statistics = true; break; case OPT_MAY_CREATE: dpctl_p.may_create = true; break; case OPT_READ_ONLY: dpctl_p.read_only = true; break; case 'm': dpctl_p.verbosity++; break; case OPT_NAMES: dpctl_p.names = true; set_names = true; break; case OPT_NO_NAMES: dpctl_p.names = false; set_names = true; break; case 't': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ovs_fatal(0, "value %s on -t or --timeout is invalid", optarg); } break; case 'h': usage(NULL); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); ctl_timeout_setup(timeout); if (!set_names) { dpctl_p.names = dpctl_p.verbosity > 0; } } static void usage(void *userdata OVS_UNUSED) { printf("%s: Open vSwitch datapath management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" " add-dp DP [IFACE...] add new datapath DP (with IFACEs)\n" " del-dp DP delete local datapath DP\n" " add-if DP IFACE... add each IFACE as a port on DP\n" " set-if DP IFACE... reconfigure each IFACE within DP\n" " del-if DP IFACE... delete each IFACE from DP\n" " dump-dps display names of all datapaths\n" " show show basic info on all datapaths\n" " show DP... show basic info on each DP\n" " dump-flows [DP] display flows in DP\n" " add-flow [DP] FLOW ACTIONS add FLOW with ACTIONS to DP\n" " add-flows [DP] FILE add flows from FILE\n" " mod-flow [DP] FLOW ACTIONS change FLOW actions to ACTIONS in DP\n" " mod-flows [DP] FILE change flows from FILE\n" " get-flow [DP] ufid:UFID fetch flow corresponding to UFID\n" " del-flow [DP] FLOW delete FLOW from DP\n" " del-flows [DP] [FILE] " \ "delete all or specified flows from DP\n" " cache-get-size [DP] " \ "Show the current size for all caches\n" " cache-set-size DP CACHE SIZE " \ "Set cache size for a specific cache\n" " dump-conntrack [DP] [zone=ZONE] " \ "display conntrack entries for ZONE\n" " flush-conntrack [DP] [zone=ZONE] [ct-tuple]" \ "delete matched conntrack entries in ZONE\n" " ct-stats-show [DP] [zone=ZONE] [verbose] " \ "CT connections grouped by protocol\n" " ct-bkts [DP] [gt=N] display connections per CT bucket\n" "Each IFACE on add-dp, add-if, and set-if may be followed by\n" "comma-separated options. See ovs-dpctl(8) for syntax, or the\n" "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n" "For COMMAND dump-flows, add-flow, add-flows, mod-flow,\n" "mod-flows, del-flow and del-flows, DP is optional if there is\n" "only one datapath.\n", program_name, program_name); vlog_usage(); printf("\nOptions for show and mod-flow:\n" " -s, --statistics print statistics for port or flow\n" "\nOptions for dump-flows:\n" " -m, --more increase verbosity of output\n" " --names use port names in output\n" "\nOptions for mod-flow:\n" " --may-create create flow if it doesn't exist\n" " --read-only do not run read/write commands\n" " --clear reset existing stats to zero\n" "\nOther options:\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-kmod-ctl.8000066400000000000000000000055421514270232600234110ustar00rootroot00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .TH ovs\-ctl 8 "February 2018" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-ctl . .SH NAME ovs\-kmod\-ctl \- OVS startup helper script for loading kernel modules . .SH SYNOPSIS \fBovs\-kmod\-ctl\fR \fBinsert .br \fBovs\-kmod\-ctl \fBremove .br \fBovs\-kmod\-ctl help \fR| \fB\-h \fR| \fB\-\-help .br \fBovs\-kmod\-ctl \-\-version .br \fBovs\-kmod\-ctl version . .SH DESCRIPTION . .PP The \fBovs\-kmod\-ctl\fR program is responsible for inserting and removing Open vSwitch kernel modules. It is not meant to be invoked directly by system administrators but to be called internally by system startup scripts. The script is used as part of an SELinux transition domain. . .PP Each of \fBovs\-kmod\-ctl\fR's commands is described separately below. . .SH "The ``insert'' command" . .PP The \fBinsert\fR command loads the Open vSwitch kernel modules, if needed. If this fails, and the Linux bridge module is loaded but no bridges exist, it tries to unload the bridge module and tries loading the Open vSwitch kernel module again. . .SH "The ``remove'' command" . .PP The \fBremove\fR command unloads the Open vSwitch kernel module (including the bridge compatibility module, if loaded) and any associated vport modules. . .SH "EXIT STATUS" . \fBovs\-kmod\-ctl\fR exits with status 0 on success and nonzero on failure. The \fBinsert\fR command is considered to succeed if kernel modules are already loaded; the \fBremove\fR command is considered to succeed if none of the kernel modules are loaded. . .SH "ENVIRONMENT" . The following environment variables affect \fBovs\-kmod\-ctl\fR: . .IP "\fBPATH\fR" \fBovs\-kmod\-ctl\fR does not hardcode the location of any of the programs that it runs. \fBovs\-kmod\-ctl\fR will add the \fIsbindir\fR and \fIbindir\fR that were specified at \fBconfigure\fR time to \fBPATH\fR, if they are not already present. . .IP "\fBOVS_LOGDIR\fR" .IQ "\fBOVS_RUNDIR\fR" .IQ "\fBOVS_DBDIR\fR" .IQ "\fBOVS_SYSCONFDIR\fR" .IQ "\fBOVS_PKGDATADIR\fR" .IQ "\fBOVS_BINDIR\fR" .IQ "\fBOVS_SBINDIR\fR" Setting one of these variables in the environment overrides the respective \fBconfigure\fR option, both for \fBovs\-kmod\-ctl\fR itself and for the other Open vSwitch programs that it runs. . .SH "FILES" . \fBovs\-kmod\-ctl\fR uses the following files: . .IP "\fBovs\-lib" Shell function library used internally by \fBovs\-kmod\-ctl\fR. It must be installed in the same directory as \fBovs\-kmod\-ctl\fR. . .SH "EXAMPLE" . .PP \fBovs\-kmod\-ctl\fR isn't intended to be manually executed. However, the following examples demonstrate loading the kernel modules. . .TP \fBovs\-kmod\-ctl\fR insert Attempts to insert the Open vSwitch kernel modules. . .TP \fBovs\-kmod\-ctl\fR remove Attempts to remove the Open vSwitch kernel modules. . .SH "SEE ALSO" . \fBREADME.rst\fR, \fBovs\-ctl\fR(8) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-kmod-ctl.in000066400000000000000000000136551514270232600236540ustar00rootroot00000000000000#! /bin/sh # SPDX-License-Identifier: Apache-2.0 # Copyright (C) 2018 Red Hat, Inc. # # 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. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do case :$PATH: in *:$dir:*) ;; *) PATH=$PATH:$dir ;; esac done insert_mods () { # Try loading openvswitch kernel module. action "Inserting openvswitch module" modprobe openvswitch } insert_kmods_if_required() { # If this kernel has no module support, expect we're done. if test ! -e /proc/modules then log_success_msg "Kernel has no loadable module support. Skipping modprobe" return 0 fi # If openvswitch is already loaded then we're done. test -e /sys/module/openvswitch && return 0 # Load openvswitch. If that's successful then we're done. insert_mods && return 0 # If the bridge module is loaded, then that might be blocking # openvswitch. Try to unload it, if there are no bridges. test -e /sys/module/bridge || return 1 bridges=`echo /sys/class/net/*/bridge | sed 's,/sys/class/net/,,g;s,/bridge,,g'` if test "$bridges" != "*"; then log_warning_msg "not removing bridge module because bridges exist ($bridges)" return 1 fi action "removing bridge module" rmmod bridge || return 1 # Try loading openvswitch again. insert_mods } remove_kmods() { for vport in `awk '/^vport_/ { print $1 }' /proc/modules`; do action "Removing $vport module" rmmod $vport done if test -e /sys/module/ip_gre; then action "Forcing removal of ip_gre module" rmmod ip_gre fi if test -e /sys/module/gre; then action "Forcing removal of gre module" rmmod gre fi if test -e /sys/module/openvswitch; then action "Removing openvswitch module" rmmod openvswitch fi # Older releases may be using the rtnetlink interface while a # newer release will want to use the internal compat interface # for geneve and vxlan. if test -e /sys/class/net/genev_sys_6081; then action "Removing geneve device" \ ip link del link genev_sys_6081 dev genev_sys_6081 fi if test -e /sys/class/net/vxlan_sys_4789; then action "Removing vxlan device" \ ip link del link vxlan_sys_4789 dev vxlan_sys_4789 fi if test -e /sys/module/geneve; then action "Forcing removal of geneve module" rmmod geneve fi if test -e /sys/module/vxlan; then action "Forcing removal of vxlan module" rmmod vxlan fi } usage () { cat <&2 "$0: unknown option \"$arg\" (use --help for help)" return fi eval $var=\$value } extra_ids= command= for arg do case $arg in -h | --help) usage ;; -V | --version) echo "$0 (Open vSwitch) $VERSION" exit 0 ;; --[a-z]*=*) option=`expr X"$arg" : 'X--\([^=]*\)'` value=`expr X"$arg" : 'X[^=]*=\(.*\)'` type=string set_option ;; --no-[a-z]*) option=`expr X"$arg" : 'X--no-\(.*\)'` value=no type=bool set_option ;; --[a-z]*) option=`expr X"$arg" : 'X--\(.*\)'` value=yes type=bool set_option ;; -*) echo >&2 "$0: unknown option \"$arg\" (use --help for help)" exit 1 ;; *) if test X"$command" = X; then command=$arg else echo >&2 "$0: exactly one non-option argument required (use --help for help)" exit 1 fi ;; esac done case $command in remove) remove_kmods ;; insert) insert_kmods_if_required ;; help) usage ;; '') echo >&2 "$0: missing command name (use --help for help)" exit 1 ;; *) echo >&2 "$0: unknown command \"$command\" (use --help for help)" exit 1 ;; esac openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-l3ping.in000066400000000000000000000054311514270232600233270ustar00rootroot00000000000000#! @PYTHON3@ # # 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. """ ovs L3 ping utility allows to do tests between two remote hosts without opening holes in the firewall for the XML RPC control connection. This is achieved by tunneling the control connection inside the tunnel itself. """ import socket import xmlrpc.client import ovstest.args as args import ovstest.tests as tests import ovstest.util as util def get_packet_sizes(me, he, remote_ip): """ This function retrieves MTUs from both hosts and returns a list of packet sizes, that are more likely to uncover possible configuration issues. """ mtu_node1 = 1500 mtu_node2 = 1500 server1 = util.rpc_client(me[0], me[1]) server2 = util.rpc_client(he[0], he[1]) iface1 = server2.get_interface(remote_ip) iface2 = server1.get_interface_from_routing_decision(remote_ip) if iface1: mtu_node1 = server2.get_interface_mtu(iface1) if iface2: mtu_node2 = server1.get_interface_mtu(iface2) return util.get_datagram_sizes(mtu_node1, mtu_node2) if __name__ == '__main__': local_server = None try: args = args.l3_initialize_args() tunnel_mode = args.tunnelMode if args.server is not None: # Start in server mode local_server = tests.configure_l3(args.server, tunnel_mode) local_server.wait() elif args.client is not None: # Run in client mode bandwidth = args.targetBandwidth interval = args.testInterval me = (util.ip_from_cidr(args.client[1][0]), args.client[1][1], args.client[1][0], args.client[1][2]) he = (args.client[2][0], args.client[2][1], args.client[2][0], args.client[2][2]) local_server = tests. configure_l3(args.client, tunnel_mode) ps = get_packet_sizes(me, he, args.client[0]) tests.do_direct_tests(me, he, bandwidth, interval, ps) except KeyboardInterrupt: print("Terminating") except xmlrpc.client.Fault: print("Couldn't contact peer") except socket.error: print("Couldn't contact peer") except xmlrpc.client.ProtocolError: print("XMLRPC control channel was abruptly terminated") finally: if local_server is not None: local_server.terminate() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-lib.in000066400000000000000000000540651514270232600227100ustar00rootroot00000000000000# -*- sh -*- # vi:syntax=sh # This is a shell function library sourced by some Open vSwitch scripts. # It is not intended to be invoked on its own. # Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc. # # 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. ## ----------------- ## ## configure options ## ## ----------------- ## # All of these should be substituted by the Makefile at build time. logdir=${OVS_LOGDIR-'@LOGDIR@'} # /var/log/openvswitch rundir=${OVS_RUNDIR-'@RUNDIR@'} # /var/run/openvswitch sysconfdir=${OVS_SYSCONFDIR-'@sysconfdir@'} # /etc etcdir=$sysconfdir/openvswitch # /etc/openvswitch datadir=${OVS_PKGDATADIR-'@pkgdatadir@'} # /usr/share/openvswitch bindir=${OVS_BINDIR-'@bindir@'} # /usr/bin sbindir=${OVS_SBINDIR-'@sbindir@'} # /usr/sbin # /etc/openvswitch or /var/lib/openvswitch if test X"$OVS_DBDIR" != X; then dbdir=$OVS_DBDIR elif test X"$OVS_SYSCONFDIR" != X; then dbdir=$OVS_SYSCONFDIR/openvswitch else dbdir='@DBDIR@' fi ovs_ctl_log () { echo "$@" >> "${logdir}/ovs-ctl.log" } ovs_ctl () { case "$@" in *"=strace"*) # In case of running the daemon with strace, piping the o/p causes # the script to block (strace probably does not close the inherited # pipe). So, do not log the o/p to ovs-ctl.log. "${datadir}/scripts/ovs-ctl" "$@" ;; "status") # In case of the command 'status', we should return the exit status # of ovs-ctl. It is also useful to document the o/p in ovs-ctl.log. display=`"${datadir}/scripts/ovs-ctl" "$@" 2>&1` rc=$? if test -w "${logdir}/ovs-ctl.log"; then echo "${display}" | tee -a "${logdir}/ovs-ctl.log" else echo "${display}" fi return ${rc} ;; *) echo "`date -u`:$@" >> "${logdir}/ovs-ctl.log" "${datadir}/scripts/ovs-ctl" "$@" 2>&1 | tee -a "${logdir}/ovs-ctl.log" ;; esac } VERSION='@VERSION@@VERSION_SUFFIX@' DAEMON_CWD=/ LC_ALL=C; export LC_ALL ## ------------- ## ## LSB functions ## ## ------------- ## # Use the system's own implementations if it has any. if test -e /etc/init.d/functions; then . /etc/init.d/functions elif test -e /etc/rc.d/init.d/functions; then . /etc/rc.d/init.d/functions elif test -e /lib/lsb/init-functions; then . /lib/lsb/init-functions fi # Implement missing functions (e.g. OpenSUSE lacks 'action'). if type log_success_msg >/dev/null 2>&1; then :; else log_success_msg () { printf '%s.\n' "$*" } fi if type log_failure_msg >/dev/null 2>&1; then :; else log_failure_msg () { printf '%s ... failed!\n' "$*" } fi if type log_warning_msg >/dev/null 2>&1; then :; else log_warning_msg () { printf '%s ... (warning).\n' "$*" } fi if type action >/dev/null 2>&1; then :; else action () { STRING=$1 shift "$@" rc=$? if test $rc = 0; then log_success_msg "$STRING" else log_failure_msg "$STRING" fi return $rc } fi ## ------- ## ## Daemons ## ## ------- ## pid_exists () { # This is better than "kill -0" because it doesn't require permission to # send a signal (so daemon_status in particular works as non-root). test -n "$1" && test -d /proc/"$1" } pid_comm_check () { [ "$1" = "`cat /proc/$2/comm`" ] } # version_geq version_a version_b # # Compare (dot separated) version numbers. Returns true (exit code 0) if # version_a is greater or equal than version_b, otherwise false (exit code 1). version_geq() { echo $1 $2 | awk '{ n1 = split($1, a, "."); n2 = split($2, b, "."); n = (n1 > n2) ? n1 : n2; for (i = 1; i <= n; i++) { if (a[i]+0 < b[i]+0) exit 1 if (a[i]+0 > b[i]+0) exit 0 } }' } install_dir () { DIR="$1" INSTALL_MODE="${2:-755}" INSTALL_USER="root" INSTALL_GROUP="root" [ "$OVS_USER" != "" ] && INSTALL_USER="${OVS_USER%:*}" [ "${OVS_USER##*:}" != "" ] && INSTALL_GROUP="${OVS_USER##*:}" if test ! -d "$DIR"; then install -d -m "$INSTALL_MODE" -o "$INSTALL_USER" -g "$INSTALL_GROUP" "$DIR" restorecon "$DIR" >/dev/null 2>&1 fi } start_daemon () { priority=$1 && shift wrapper=$1 && shift umask=$1 && shift daemon=$1 strace="" # drop core files in a sensible place install_dir "$DAEMON_CWD" set "$@" --no-chdir cd "$DAEMON_CWD" # log file install_dir "$logdir" "750" set "$@" --log-file="$logdir/$daemon.log" # pidfile and monitoring install_dir "$rundir" set "$@" --pidfile="$rundir/$daemon.pid" set "$@" --detach test X"$MONITOR" = Xno || set "$@" --monitor # wrapper case $wrapper in valgrind) if (valgrind --version) > /dev/null 2>&1; then set valgrind -q --leak-check=full --time-stamp=yes \ --log-file="$logdir/$daemon.valgrind.log.%p" "$@" else log_failure_msg "valgrind not installed, running $daemon without it" fi ;; strace) if (strace -V) > /dev/null 2>&1; then strace="strace -tt -T -s 256 -ff" if (strace -DV) > /dev/null 2>&1; then # Has the -D option. set $strace -D -o "$logdir/$daemon.strace.log" "$@" strace="" fi else log_failure_msg "strace not installed, running $daemon without it" fi ;; glibc) set env MALLOC_CHECK_=2 MALLOC_PERTURB_=165 "$@" ;; '') ;; *) log_failure_msg "unknown wrapper $wrapper, running $daemon without it" ;; esac # priority if test X"$priority" != X; then set nice -n "$priority" "$@" fi # Set requested umask if any and turn previous value back. if [ -n "$umask" ]; then previuos_umask_value=$(umask) umask "$umask" fi action "Starting $daemon" "$@" || return 1 # If umask was set, turn umask value to previous value. if [ -n "$umask" ]; then umask "$previuos_umask_value" fi if test X"$OOM_SCORE" != X; then echo "$OOM_SCORE" > /proc/`cat $rundir/$daemon.pid`/oom_score_adj 2>/dev/null fi if test X"$strace" != X; then # Strace doesn't have the -D option so we attach after the fact. setsid $strace -o "$logdir/$daemon.strace.log" \ -p `cat $rundir/$daemon.pid` > /dev/null 2>&1 & fi } stop_daemon () { if test -e "$rundir/$1.pid"; then if pid=`cat "$rundir/$1.pid"`; then if pid_exists "$pid" >/dev/null 2>&1; then :; else rm -f $rundir/$1.$pid.ctl $rundir/$1.$pid return 0 fi graceful="EXIT .1 .25 .65 1" actions="TERM .1 .25 .65 1 1 1 1 \ KILL 1 1 1 2 10 15 30 \ FAIL" version=`ovs-appctl -T 1 -t $rundir/$1.$pid.ctl version \ | awk 'NR==1{print $NF}'` # Use `ovs-appctl exit` only if the running daemon version # is >= 2.5.90. This script might be used during upgrade to # stop older versions of daemons which do not behave correctly # with `ovs-appctl exit` (e.g. ovs-vswitchd <= 2.5.0 deletes # internal ports). if version_geq "$version" "2.5.90"; then actions="$graceful $actions" fi actiontype="" for action in $actions; do if pid_exists "$pid" >/dev/null 2>&1; then :; else # pid does not exist. if [ -n "$actiontype" ]; then return 0 fi # But, does the file exist? We may have had a daemon # segfault with `ovs-appctl exit`. Check one more time # before deciding that the daemon is dead. [ -e "$rundir/$1.pid" ] && sleep 2 && pid=`cat "$rundir/$1.pid"` 2>/dev/null if pid_exists "$pid" >/dev/null 2>&1; then :; else return 0 fi fi case $action in EXIT) action "Exiting $1 ($pid)" \ ${bindir}/ovs-appctl -T 1 -t $rundir/$1.$pid.ctl exit # The above command could have resulted in delayed # daemon segfault. And if a monitor is running, it # would restart the daemon giving it a new pid. ;; TERM) action "Killing $1 ($pid)" kill $pid actiontype="force" ;; KILL) action "Killing $1 ($pid) with SIGKILL" kill -9 $pid actiontype="force" ;; FAIL) log_failure_msg "Killing $1 ($pid) failed" return 1 ;; *) sleep $action ;; esac done fi fi log_success_msg "$1 is not running" } daemon_status () { pidfile=$rundir/$1.pid if test -e "$pidfile"; then if pid=`cat "$pidfile"`; then if pid_exists "$pid"; then echo "$1 is running with pid $pid" return 0 else echo "Pidfile for $1 ($pidfile) is stale" fi else echo "Pidfile for $1 ($pidfile) exists but cannot be read" fi else echo "$1 is not running" fi return 1 } daemon_is_running () { pidfile=$rundir/$1.pid test -e "$pidfile" && pid=`cat "$pidfile"` && pid_exists "$pid" && pid_comm_check $1 $pid } >/dev/null 2>&1 # Prints commands needed to move the ip address from interface $1 to interface # $2 move_ip_address () { if [ -z "$1" ] || [ -z "$2" ]; then return fi dev="$1" dst="$2" # IP addresses (including IPv6). echo "ip addr flush dev $dev 2>/dev/null" # Suppresses "Nothing to flush". ip addr show dev $dev | while read addr; do set -- $addr # Check and trim family. family=$1 shift case $family in inet | inet6) ;; *) continue ;; esac # Trim device off the end--"ip" insists on having "dev" precede it. addrcmd= while test $# != 0; do case $1 in dynamic) # XXX: According to 'man ip-address', "dynamic" is only # used for ipv6 addresses. But, atleast on RHEL 7.4 # (iproute-3.10.0-87), it is being used for ipv4 # addresses assigned with dhcp. if [ "$family" = "inet" ]; then shift continue fi # Omit kernel-maintained route. continue 2 ;; scope) if test "$2" = link -a "$family" != inet6; then # Omit route derived from IP address, e.g. # 172.16.0.0/16 derived from 172.16.12.34, # but preserve IPv6 link-local address. continue 2 fi ;; "$dev"|"$dev:"*) # Address label string label=`echo $1 | sed "s/$dev/$dst/"` addrcmd="$addrcmd label $label" shift continue ;; esac addrcmd="$addrcmd $1" shift done if test "$1" != "$dev"; then addrcmd="$addrcmd $1" fi echo ip -f $family addr add $addrcmd dev $dst done } # Prints commands needed to move the ip route of interface $1 to interface $2 move_ip_routes () { if [ -z "$1" ] || [ -z "$2" ]; then return fi dev="$1" dst="$2" echo "ip route flush dev $dev proto boot 2>/dev/null" # Suppresses "Nothing to flush". ip route show dev $dev | while read route; do # "proto kernel" routes are installed by the kernel automatically. case $route in *" proto kernel "*) continue ;; esac echo "ip route add $route dev $dst" done } run_as_ovsuser() { if [ "$OVS_USER" != "" ]; then local uid=$(id -u "${OVS_USER%:*}") local gid=$(id -g "${OVS_USER%:*}") local groups=$(id -G "${OVS_USER%:*}" | tr ' ' ',') setpriv --reuid "$uid" --regid "$gid" --groups "$groups" "$@" else "$@" fi } ovsdb_tool () { run_as_ovsuser ovsdb-tool -vconsole:off "$@" } create_db () { DB_FILE="$1" DB_SCHEMA="$2" action "Creating empty database $DB_FILE" ovsdb_tool create "$DB_FILE" "$DB_SCHEMA" } backup_db () { # Back up the old version. version=`ovsdb_tool db-version "$DB_FILE"` cksum=`ovsdb_tool db-cksum "$DB_FILE" | awk '{print $1}'` backup=$DB_FILE.backup$version-$cksum action "Backing up database to $backup" cp "$DB_FILE" "$backup" || return 1 } upgrade_db () { DB_FILE="$1" DB_SCHEMA="$2" schemaver=`ovsdb_tool schema-version "$DB_SCHEMA"` if test ! -e "$DB_FILE"; then log_warning_msg "$DB_FILE does not exist" install_dir `dirname $DB_FILE` create_db "$DB_FILE" "$DB_SCHEMA" elif test X"`ovsdb_tool needs-conversion "$DB_FILE" "$DB_SCHEMA"`" = Xyes; then backup_db || return 1 # Compact database. This is important if the old schema did not enable # garbage collection (i.e. if it did not have any tables with "isRoot": # true) but the new schema does. In that situation the old database # may contain a transaction that creates a record followed by a # transaction that creates the first use of the record. Replaying that # series of transactions against the new database schema (as "convert" # does) would cause the record to be dropped by the first transaction, # then the second transaction would cause a referential integrity # failure (for a strong reference). # # Errors might occur on an Open vSwitch downgrade if ovsdb-tool doesn't # understand some feature of the schema used in the OVSDB version that # we're downgrading from, so we don't give up on error. action "Compacting database" ovsdb_tool compact "$DB_FILE" # Upgrade or downgrade schema. if action "Converting database schema" ovsdb_tool convert "$DB_FILE" "$DB_SCHEMA"; then : else log_warning_msg "Schema conversion failed, using empty database instead" rm -f "$DB_FILE" create_db "$DB_FILE" "$DB_SCHEMA" fi fi } upgrade_cluster () { local DB_SCHEMA=$1 DB_SERVER=$2 local schema_name=$(ovsdb-tool schema-name $1) || return 1 action "Waiting for $schema_name to come up" ovsdb-client -t 30 wait "$DB_SERVER" "$schema_name" connected || return $? local db_version=$(ovsdb-client -t 10 get-schema-version "$DB_SERVER" "$schema_name") || return $? local target_version=$(ovsdb-tool schema-version "$DB_SCHEMA") || return $? if ovsdb-tool compare-versions "$db_version" == "$target_version"; then : elif ovsdb-tool compare-versions "$db_version" ">" "$target_version"; then log_warning_msg "Database $schema_name has newer schema version ($db_version) than our local schema ($target_version), possibly an upgrade is partially complete?" else action "Upgrading database $schema_name from schema version $db_version to $target_version" ovsdb-client -t 30 convert "$DB_SERVER" "$DB_SCHEMA" fi } create_cluster () { DB_FILE="$1" DB_SCHEMA="$2" LOCAL_ADDR="$3" ELECTION_TIMER_MS="$4" election_timer_arg= if [ -n "$ELECTION_TIMER_MS" ]; then election_timer_arg="--election-timer=$ELECTION_TIMER_MS" fi if test ! -e "$DB_FILE"; then action "Creating cluster database $DB_FILE" ovsdb_tool $election_timer_arg create-cluster "$DB_FILE" "$DB_SCHEMA" "$LOCAL_ADDR" elif ovsdb_tool db-is-standalone "$DB_FILE"; then # Convert standalone database to clustered. backup_db || return 1 rm -f "$DB_FILE" action "Creating cluster database $DB_FILE from existing one" \ ovsdb_tool $election_timer_arg create-cluster "$DB_FILE" "$backup" "$LOCAL_ADDR" fi } join_cluster() { DB_FILE="$1" SCHEMA_NAME="$2" LOCAL_ADDR="$3" REMOTE_ADDR="$4" if test -e "$DB_FILE" && ovsdb_tool db-is-standalone "$DB_FILE"; then backup_db || return 1 rm $DB_FILE fi if test ! -e "$DB_FILE"; then action "Joining $DB_FILE to cluster" \ ovsdb_tool join-cluster "$DB_FILE" "$SCHEMA_NAME" "$LOCAL_ADDR" "$REMOTE_ADDR" fi } ovs_vsctl () { ovs-vsctl --no-wait "$@" } ## ----------------- ## ## force-reload-kmod ## ## ----------------- ## ovs_kmod_ctl () { "$dir0/ovs-kmod-ctl" "$@" } internal_interfaces () { # Outputs a list of internal interfaces: # # - There is an internal interface for every bridge, whether it # has an Interface record or not and whether the Interface # record's 'type' is properly set or not. # # - There is an internal interface for each Interface record whose # 'type' is 'internal'. # # But ignore interfaces that don't really exist. for d in `(ovs_vsctl --bare \ -- --columns=name find Interface type=internal \ -- list-br) | sort -u` do if test -e "/sys/class/net/$d"; then printf "%s " "$d" fi done } ovs_save () { bridges=`ovs_vsctl -- --real list-br` if [ -n "${bridges}" ] && \ "$datadir/scripts/ovs-save" "$1" ${bridges} > "$2"; then chmod +x "$2" return 0 fi [ -z "${bridges}" ] && return 0 } save_flows_if_required () { if test X"$DELETE_BRIDGES" != Xyes; then action "Saving flows" ovs_save save-flows "${script_flows}" fi } save_interfaces () { "$datadir/scripts/ovs-save" save-interfaces ${ifaces} \ > "${script_interfaces}" } flow_restore_wait () { if test X"${OVS_VSWITCHD:-yes}" = Xyes; then ovs_vsctl set open_vswitch . other_config:flow-restore-wait="true" fi } flow_restore_complete () { if test X"${OVS_VSWITCHD:-yes}" = Xyes; then ovs_vsctl --if-exists remove open_vswitch . other_config \ flow-restore-wait="true" fi } restore_flows () { [ -x "${script_flows}" ] && \ action "Restoring saved flows" "${script_flows}" } restore_interfaces () { [ ! -x "${script_interfaces}" ] && return 0 action "Restoring interface configuration" "${script_interfaces}" rc=$? if test $rc = 0; then level=debug else level=err fi log="logger -p daemon.$level -t ovs-save" $log "interface restore script exited with status $rc:" $log -f "$script_interfaces" } init_restore_scripts () { script_interfaces=`mktemp` script_flows=`mktemp` trap 'rm -f "${script_interfaces}" "${script_flows}"' 0 } force_reload_kmod () { if test X"${OVS_VSWITCHD:-yes}" != Xyes; then log_failure_msg "Reloading of kmod without ovs-vswitchd is an error" exit 1 fi ifaces=`internal_interfaces` action "Detected internal interfaces: $ifaces" true init_restore_scripts save_flows_if_required # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb || return 1 stop_forwarding if action "Saving interface configuration" save_interfaces; then : else log_warning_msg "Failed to save configuration, not replacing kernel module" start_forwarding add_managers exit 1 fi chmod +x "$script_interfaces" for dp in `ovs-dpctl dump-dps`; do action "Removing datapath: $dp" ovs-dpctl del-dp "$dp" done if test -e /sys/module/ip_gre; then action "Forcing removal of ip_gre module" rmmod ip_gre fi if test -e /sys/module/gre; then action "Forcing removal of gre module" rmmod gre fi ovs_kmod_ctl remove # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding || return 1 # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers restore_interfaces action "Finding processes on dead interfaces" timeout 5 \ "$datadir/scripts/ovs-check-dead-ifs" || true } ## ------- ## ## restart ## ## ------- ## restart () { if daemon_is_running ovsdb-server && daemon_is_running ovs-vswitchd; then init_restore_scripts if test X"${OVS_VSWITCHD:-yes}" = Xyes; then save_flows_if_required fi fi # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb || return 1 stop_forwarding # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding || return 1 # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-ofctl.8.in000066400000000000000000002107021514270232600234070ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-ofctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-ofctl . .SH NAME ovs\-ofctl \- administer OpenFlow switches . .SH SYNOPSIS .B ovs\-ofctl [\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...] . .SH DESCRIPTION The .B ovs\-ofctl program is a command line tool for monitoring and administering OpenFlow switches. It can also show the current state of an OpenFlow switch, including features, configuration, and table entries. It should work with any OpenFlow switch, not just Open vSwitch. . .SS "OpenFlow Switch Management Commands" .PP These commands allow \fBovs\-ofctl\fR to monitor and administer an OpenFlow switch. It is able to show the current state of a switch, including features, configuration, and table entries. .PP Most of these commands take an argument that specifies the method for connecting to an OpenFlow switch. The following connection methods are supported: . .RS .so lib/vconn-active.man . .IP "\fIfile\fR" This is short for \fBunix:\fIfile\fR, as long as \fIfile\fR does not contain a colon. . .IP \fIbridge\fR This is short for \fBunix:@RUNDIR@/\fIbridge\fB.mgmt\fR, as long as \fIbridge\fR does not contain a colon. . .IP [\fItype\fB@\fR]\fIdp\fR Attempts to look up the bridge associated with \fIdp\fR and open as above. If \fItype\fR is given, it specifies the datapath provider of \fIdp\fR, otherwise the default provider \fBsystem\fR is assumed. .RE . .TP \fBshow \fIswitch\fR Prints to the console information on \fIswitch\fR, including information on its flow tables and ports. . .TP \fBdump\-tables \fIswitch\fR Prints to the console statistics for each of the flow tables used by \fIswitch\fR. .TP \fBdump\-table\-features \fIswitch\fR Prints to the console features for each of the flow tables used by \fIswitch\fR. .TP \fBdump\-table\-desc \fIswitch\fR Prints to the console configuration for each of the flow tables used by \fIswitch\fR for OpenFlow 1.4+. .IP "\fBmod\-table \fIswitch\fR \fItable\fR \fIsetting\fR" This command configures flow table settings in \fIswitch\fR for OpenFlow table \fItable\fR, which may be expressed as a number or (unless \fB\-\-no\-names\fR is specified) a name. .IP The available settings depend on the OpenFlow version in use. In OpenFlow 1.1 and 1.2 (which must be enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures behavior when no flow is found when a packet is looked up in a flow table. The following \fIsetting\fR values are available: .RS .IP \fBdrop\fR Drop the packet. .IP \fBcontinue\fR Continue to the next table in the pipeline. (This is how an OpenFlow 1.0 switch always handles packets that do not match any flow, in tables other than the last one.) .IP \fBcontroller\fR Send to controller. (This is how an OpenFlow 1.0 switch always handles packets that do not match any flow in the last table.) .RE .IP In OpenFlow 1.3 and later (which must be enabled with the \fB\-O\fR option) and Open vSwitch 2.11 and later only, \fBmod\-table\fR can change the name of a table: .RS .IP \fBname:\fInew-name\fR Changes the name of the table to \fInew-name\fR. Use an empty \fInew-name\fR to clear the name. (This will be ineffective if the name is set via the \fBname\fR column in the \fBFlow_Table\fR table in the \fBOpen_vSwitch\fR database as described in \fBovs\-vswitchd.conf.db\fR(5).) .RE .IP In OpenFlow 1.4 and later (which must be enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures the behavior when a controller attempts to add a flow to a flow table that is full. The following \fIsetting\fR values are available: .RS .IP \fBevict\fR Delete some existing flow from the flow table, according to the algorithm described for the \fBFlow_Table\fR table in \fBovs-vswitchd.conf.db\fR(5). .IP \fBnoevict\fR Refuse to add the new flow. (Eviction might still be enabled through the \fBoverflow_policy\fR column in the \fBFlow_Table\fR table documented in \fBovs-vswitchd.conf.db\fR(5).) .IP \fBvacancy:\fIlow\fB,\fIhigh\fR Enables sending vacancy events to controllers using \fBTABLE_STATUS\fR messages, based on percentage thresholds \fIlow\fR and \fIhigh\fR. .IP \fBnovacancy\fR Disables vacancy events. .RE . .TP \fBdump\-ports \fIswitch\fR [\fInetdev\fR] Prints to the console statistics for network devices associated with \fIswitch\fR. If \fInetdev\fR is specified, only the statistics associated with that device will be printed. \fInetdev\fR can be an OpenFlow assigned port number or device name, e.g. \fBeth0\fR. . .IP "\fBdump\-ports\-desc \fIswitch\fR [\fIport\fR]" Prints to the console detailed information about network devices associated with \fIswitch\fR. To dump only a specific port, specify its number as \fIport\fR. Otherwise, if \fIport\fR is omitted, or if it is specified as \fBANY\fR, then all ports are printed. This is a subset of the information provided by the \fBshow\fR command. .IP If the connection to \fIswitch\fR negotiates OpenFlow 1.0, 1.2, or 1.2, this command uses an OpenFlow extension only implemented in Open vSwitch (version 1.7 and later). .IP Only OpenFlow 1.5 and later support dumping a specific port. Earlier versions of OpenFlow always dump all ports. . .IP "\fBmod\-port \fIswitch\fR \fIport\fR \fIaction\fR" Modify characteristics of port \fBport\fR in \fIswitch\fR. \fIport\fR may be an OpenFlow port number or name (unless \fB\-\-no\-names\fR is specified) or the keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow local port). The \fIaction\fR may be any one of the following: . .RS .IQ \fBup\fR .IQ \fBdown\fR Enable or disable the interface. This is equivalent to \fBip link set up\fR or \fBip link set down\fR on a Unix system. . .IP \fBstp\fR .IQ \fBno\-stp\fR Enable or disable 802.1D spanning tree protocol (STP) on the interface. OpenFlow implementations that don't support STP will refuse to enable it. . .IP \fBreceive\fR .IQ \fBno\-receive\fR .IQ \fBreceive\-stp\fR .IQ \fBno\-receive\-stp\fR Enable or disable OpenFlow processing of packets received on this interface. When packet processing is disabled, packets will be dropped instead of being processed through the OpenFlow table. The \fBreceive\fR or \fBno\-receive\fR setting applies to all packets except 802.1D spanning tree packets, which are separately controlled by \fBreceive\-stp\fR or \fBno\-receive\-stp\fR. . .IP \fBforward\fR .IQ \fBno\-forward\fR Allow or disallow forwarding of traffic to this interface. By default, forwarding is enabled. . .IP \fBflood\fR .IQ \fBno\-flood\fR Controls whether an OpenFlow \fBflood\fR action will send traffic out this interface. By default, flooding is enabled. Disabling flooding is primarily useful to prevent loops when a spanning tree protocol is not in use. . .IP \fBpacket\-in\fR .IQ \fBno\-packet\-in\fR Controls whether packets received on this interface that do not match a flow table entry generate a ``packet in'' message to the OpenFlow controller. By default, ``packet in'' messages are enabled. .RE .IP The \fBshow\fR command displays (among other information) the configuration that \fBmod\-port\fR changes. . .IP "\fBget\-frags \fIswitch\fR" Prints \fIswitch\fR's fragment handling mode. See \fBset\-frags\fR, below, for a description of each fragment handling mode. .IP The \fBshow\fR command also prints the fragment handling mode among its other output. . .IP "\fBset\-frags \fIswitch frag_mode\fR" Configures \fIswitch\fR's treatment of IPv4 and IPv6 fragments. The choices for \fIfrag_mode\fR are: .RS .IP "\fBnormal\fR" Fragments pass through the flow table like non-fragmented packets. The TCP ports, UDP ports, and ICMP type and code fields are always set to 0, even for fragments where that information would otherwise be available (fragments with offset 0). This is the default fragment handling mode for an OpenFlow switch. .IP "\fBdrop\fR" Fragments are dropped without passing through the flow table. .IP "\fBreassemble\fR" The switch reassembles fragments into full IP packets before passing them through the flow table. Open vSwitch does not implement this fragment handling mode. .IP "\fBnx\-match\fR" Fragments pass through the flow table like non-fragmented packets. The TCP ports, UDP ports, and ICMP type and code fields are available for matching for fragments with offset 0, and set to 0 in fragments with nonzero offset. This mode is a Nicira extension. .RE .IP See the description of \fBip_frag\fR, in \fBovs\-fields\fR(7), for a way to match on whether a packet is a fragment and on its fragment offset. . .TP \fBdump\-flows \fIswitch \fR[\fIflows\fR] Prints to the console all flow entries in \fIswitch\fR's tables that match \fIflows\fR. If \fIflows\fR is omitted, all flows in the switch are retrieved. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR. The output format is described in \fBTable Entry Output\fR. . .IP By default, \fBovs\-ofctl\fR prints flow entries in the same order that the switch sends them, which is unlikely to be intuitive or consistent. Use \fB\-\-sort\fR and \fB\-\-rsort\fR to control display order. The \fB\-\-names\fR/\fB\-\-no\-names\fR and \fB\-\-stats\fR/\fB\-\-no\-stats\fR options also affect output formatting. See the descriptions of these options, under \fBOPTIONS\fR below, for more information . .TP \fBdump\-aggregate \fIswitch \fR[\fIflows\fR] Prints to the console aggregate statistics for flows in \fIswitch\fR's tables that match \fIflows\fR. If \fIflows\fR is omitted, the statistics are aggregated across all flows in the switch's flow tables. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR. The output format is described in \fBTable Entry Output\fR. . .IP "\fBqueue\-stats \fIswitch \fR[\fIport \fR[\fIqueue\fR]]" Prints to the console statistics for the specified \fIqueue\fR on \fIport\fR within \fIswitch\fR. \fIport\fR can be an OpenFlow port number or name, the keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow local port), or the keyword \fBALL\fR. Either of \fIport\fR or \fIqueue\fR or both may be omitted (or equivalently the keyword \fBALL\fR). If both are omitted, statistics are printed for all queues on all ports. If only \fIqueue\fR is omitted, then statistics are printed for all queues on \fIport\fR; if only \fIport\fR is omitted, then statistics are printed for \fIqueue\fR on every port where it exists. . .IP "\fBqueue\-get\-config \fIswitch [\fIport \fR[\fIqueue\fR]]" Prints to the console the configuration of \fIqueue\fR on \fIport\fR in \fIswitch\fR. If \fIport\fR is omitted or \fBANY\fR, reports queues for all port. If \fIqueue\fR is omitted or \fBANY\fR, reports all queues. For OpenFlow 1.3 and earlier, the output always includes all queues, ignoring \fIqueue\fR if specified. .IP This command has limited usefulness, because ports often have no configured queues and because the OpenFlow protocol provides only very limited information about the configuration of a queue. . .IP "\fBdump\-ipfix\-bridge \fIswitch\fR" Prints to the console the statistics of bridge IPFIX for \fIswitch\fR. If bridge IPFIX is configured on the \fIswitch\fR, IPFIX statistics can be retrieved. Otherwise, error message will be printed. .IP This command uses an Open vSwitch extension that is only in Open vSwitch 2.6 and later. . .IP "\fBdump\-ipfix\-flow \fIswitch\fR" Prints to the console the statistics of flow-based IPFIX for \fIswitch\fR. If flow-based IPFIX is configured on the \fIswitch\fR, statistics of all the collector set ids on the \fIswitch\fR will be printed. Otherwise, print error message. .IP Refer to \fBovs\-vswitchd.conf.db\fR(5) for more details on configuring flow based IPFIX and collector set ids. .IP This command uses an Open vSwitch extension that is only in Open vSwitch 2.6 and later. . .IP "\fBct\-flush\-zone \fIswitch zone\fR Flushes the connection tracking entries in \fIzone\fR on \fIswitch\fR. .IP This command uses an Open vSwitch extension that is only in Open vSwitch 2.6 and later. . .IP "\fBct\-flush \fIswitch [zone=N] [mark=X[/M]] [labels=Y[/N]] [ct-orig-tuple [ct-reply-tuple]]\fR Flushes the connection entries on \fIswitch\fR based on \fIzone\fR, \fImark\fR, \fIlabels\fR and connection tracking tuples \fIct-[orig|reply]-tuple\fR. .IP If \fIct-[orig|reply]-tuple\fR is not provided, flushes all the connection entries. If \fIzone\fR is specified, only flushes the connections in \fIzone\fR. if \fImark\fR or \fIlabels\fR is provided, it will flush only entries that are matching specific \fImark/labels\fR. .IP If \fIct-[orig|reply]-tuple\fR is provided, flushes the connection entry specified by \fIct-[orig|reply]-tuple\fR in \fIzone\fR. The zone defaults to 0 if it is not provided. The \fImark\fR and \fIlabels\fR defaults to "0/0" if it is not provided. The userspace connection tracker requires flushing with the original pre-NATed tuple and a warning log will be otherwise generated. The tuple can be partial and will remove all connections that are matching on the specified fields. In order to specify only \fIct-reply-tuple\fR, provide empty string as \fIct-orig-tuple\fR. .IP Note: Currently there is limitation for matching on ICMP, in order to partially match on ICMP parameters the \fIct-[orig|reply]-tuple\fR has to include either source or destination IP. .IP An example of an IPv4 ICMP \fIct-[orig|reply]-tuple\fR: .IP "ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=1,icmp_type=8,icmp_code=0,icmp_id=10" .IP An example of an IPv6 TCP \fIct-[orig|reply]-tuple\fR: .IP "ct_ipv6_src=fc00::1,ct_ipv6_dst=fc00::2,ct_nw_proto=6,ct_tp_src=1,ct_tp_dst=2" .IP This command uses an Open vSwitch extension that is only in Open vSwitch 3.1 and later. Support for matching on \fImark\fR and \fIlabels\fR is only in Open vSwitch 3.3 and later. . .SS "OpenFlow Switch Flow Table Commands" . These commands manage the flow table in an OpenFlow switch. In each case, \fIflow\fR specifies a flow entry in the format described in \fBFlow Syntax\fR, below, \fIfile\fR is a text file that contains zero or more flows in the same syntax, one per line, and the optional \fB\-\-bundle\fR option operates the command as a single atomic transaction, see option \fB\-\-bundle\fR, below. . .IP "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch flow\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch \fB\- < \fIfile\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-flows \fIswitch file\fR" Add each flow entry to \fIswitch\fR's tables. . Each flow specification (e.g., each line in \fIfile\fR) may start with \fBadd\fR, \fBmodify\fR, \fBdelete\fR, \fBmodify_strict\fR, or \fBdelete_strict\fR keyword to specify whether a flow is to be added, modified, or deleted, and whether the modify or delete is strict or not. For backwards compatibility a flow specification without one of these keywords is treated as a flow add. All flow mods are executed in the order specified. . .IP "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch flow\fR" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch \fB\- < \fIfile\fR" Modify the actions in entries from \fIswitch\fR's tables that match the specified flows. With \fB\-\-strict\fR, wildcards are not treated as active for matching purposes. . .IP "[\fB\-\-bundle\fR] \fBdel\-flows \fIswitch\fR" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fR[\fIflow\fR]" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fB\- < \fIfile\fR" Deletes entries from \fIswitch\fR's flow table. With only a \fIswitch\fR argument, deletes all flows. Otherwise, deletes flow entries that match the specified flows. With \fB\-\-strict\fR, wildcards are not treated as active for matching purposes. . .IP "[\fB\-\-bundle\fR] [\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR" Reads flow entries from \fIfile\fR (or \fBstdin\fR if \fIfile\fR is \fB\-\fR) and queries the flow table from \fIswitch\fR. Then it fixes up any differences, adding flows from \fIflow\fR that are missing on \fIswitch\fR, deleting flows from \fIswitch\fR that are not in \fIfile\fR, and updating flows in \fIswitch\fR whose actions, cookie, or timeouts differ in \fIfile\fR. . .IP With \fB\-\-readd\fR, \fBovs\-ofctl\fR adds all the flows from \fIfile\fR, even those that exist with the same actions, cookie, and timeout in \fIswitch\fR. In OpenFlow 1.0 and 1.1, re-adding a flow always resets the flow's packet and byte counters to 0, and in OpenFlow 1.2 and later, it does so only if the \fBreset_counts\fR flag is set. . .IP "\fBdiff\-flows \fIsource1 source2\fR" Reads flow entries from \fIsource1\fR and \fIsource2\fR and prints the differences. A flow that is in \fIsource1\fR but not in \fIsource2\fR is printed preceded by a \fB\-\fR, and a flow that is in \fIsource2\fR but not in \fIsource1\fR is printed preceded by a \fB+\fR. If a flow exists in both \fIsource1\fR and \fIsource2\fR with different actions, cookie, or timeouts, then both versions are printed preceded by \fB\-\fR and \fB+\fR, respectively. .IP \fIsource1\fR and \fIsource2\fR may each name a file or a switch. If a name begins with \fB/\fR or \fB.\fR, then it is considered to be a file name. A name that contains \fB:\fR is considered to be a switch. Otherwise, it is a file if a file by that name exists, a switch if not. .IP For this command, an exit status of 0 means that no differences were found, 1 means that an error occurred, and 2 means that some differences were found. . .IP "\fBpacket\-out \fIswitch\fR \fIpacket-out\fR" Connects to \fIswitch\fR and instructs it to execute the \fIpacket-out\fR OpenFlow message, specified as defined in \fBPacket\-Out Syntax\fR section. . .SS "Group Table Commands" . These commands manage the group table in an OpenFlow switch. In each case, \fIgroup\fR specifies a group entry in the format described in \fBGroup Syntax\fR, below, and \fIfile\fR is a text file that contains zero or more groups in the same syntax, one per line, and the optional \fB\-\-bundle\fR option operates the command as a single atomic transaction, see option \fB\-\-bundle\fR, below. .PP The group commands work only with switches that support OpenFlow 1.1 or later or the Open vSwitch group extensions to OpenFlow 1.0 (added in Open vSwitch 2.9.90). For OpenFlow 1.1 or later, it is necessary to explicitly enable these protocol versions in \fBovs\-ofctl\fR (using \fB\-O\fR). For more information, see ``Q: What versions of OpenFlow does Open vSwitch support?'' in the Open vSwitch FAQ. . .IP "[\fB\-\-bundle\fR] \fBadd\-group \fIswitch group\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-group \fIswitch \fB\- < \fIfile\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-groups \fIswitch file\fR" Add each group entry to \fIswitch\fR's tables. . Each group specification (e.g., each line in \fIfile\fR) may start with \fBadd\fR, \fBmodify\fR, \fBadd_or_mod\fR, \fBdelete\fR, \fBinsert_bucket\fR, or \fBremove_bucket\fR keyword to specify whether a flow is to be added, modified, or deleted, or whether a group bucket is to be added or removed. For backwards compatibility a group specification without one of these keywords is treated as a group add. All group mods are executed in the order specified. . .IP "[\fB\-\-bundle\fR] [\fB\-\-may\-create\fR] \fBmod\-group \fIswitch group\fR" .IQ "[\fB\-\-bundle\fR] [\fB\-\-may\-create\fR] \fBmod\-group \fIswitch \fB\- < \fIfile\fR" Modify the action buckets in entries from \fIswitch\fR's tables for each group entry. If a specified group does not already exist, then without \fB\-\-may\-create\fR, this command has no effect; with \fB\-\-may\-create\fR, it creates a new group. The \fB\-\-may\-create\fR option uses an Open vSwitch extension to OpenFlow only implemented in Open vSwitch 2.6 and later. . .IP "[\fB\-\-bundle\fR] \fBdel\-groups \fIswitch\fR" .IQ "[\fB\-\-bundle\fR] \fBdel\-groups \fIswitch \fR[\fIgroup\fR]" .IQ "[\fB\-\-bundle\fR] \fBdel\-groups \fIswitch \fB\- < \fIfile\fR" Deletes entries from \fIswitch\fR's group table. With only a \fIswitch\fR argument, deletes all groups. Otherwise, deletes the group for each group entry. . .IP "[\fB\-\-bundle\fR] \fBinsert\-buckets \fIswitch group\fR" .IQ "[\fB\-\-bundle\fR] \fBinsert\-buckets \fIswitch \fB\- < \fIfile\fR" Add buckets to an existing group present in the \fIswitch\fR's group table. If no \fIcommand_bucket_id\fR is present in the group specification then all buckets of the group are removed. . .IP "[\fB\-\-bundle\fR] \fBremove\-buckets \fIswitch group\fR" .IQ "[\fB\-\-bundle\fR] \fBremove\-buckets \fIswitch \fB\- < \fIfile\fR" Remove buckets to an existing group present in the \fIswitch\fR's group table. If no \fIcommand_bucket_id\fR is present in the group specification then all buckets of the group are removed. . .IP "\fBdump\-groups \fIswitch\fR [\fIgroup\fR]" Prints group entries in \fIswitch\fR's tables to console. To dump only a specific group, specify its number as \fIgroup\fR. Otherwise, if \fIgroup\fR is omitted, or if it is specified as \fBALL\fR, then all groups are printed. .IP Only OpenFlow 1.5 and later support dumping a specific group. Earlier versions of OpenFlow always dump all groups. . .IP "\fBdump\-group\-features \fIswitch" Prints to the console the group features of the \fIswitch\fR. . .IP "\fBdump\-group\-stats \fIswitch \fR[\fIgroup\fR]" Prints to the console statistics for the specified \fIgroup\fR in \fIswitch\fR's tables. If \fIgroup\fR is omitted then statistics for all groups are printed. . .SS "OpenFlow 1.3+ Switch Meter Table Commands" . These commands manage the meter table in an OpenFlow switch. In each case, \fImeter\fR specifies a meter entry in the format described in \fBMeter Syntax\fR, below. . .PP OpenFlow 1.3 introduced support for meters, so these commands only work with switches that support OpenFlow 1.3 or later. It is necessary to explicitly enable these protocol versions in \fBovs\-ofctl\fR (using \fB\-O\fR) and in the switch itself (with the \fBprotocols\fR column in the \fBBridge\fR table). For more information, see ``Q: What versions of OpenFlow does Open vSwitch support?'' in the Open vSwitch FAQ. . .IP "\fBadd\-meter \fIswitch meter\fR" Add a meter entry to \fIswitch\fR's tables. The \fImeter\fR syntax is described in section \fBMeter Syntax\fR, below. . .IP "\fBmod\-meter \fIswitch meter\fR" Modify an existing meter. . .IP "\fBdel\-meters \fIswitch\fR [\fImeter\fR]" Delete entries from \fIswitch\fR's meter table. To delete only a specific meter, specify its number as \fImeter\fR. Otherwise, if \fImeter\fR is omitted, or if it is specified as \fBall\fR, then all meters are deleted. . .IP "\fBdump\-meters \fIswitch\fR [\fImeter\fR]" Print entries from \fIswitch\fR's meter table. To print only a specific meter, specify its number as \fImeter\fR. Otherwise, if \fImeter\fR is omitted, or if it is specified as \fBall\fR, then all meters are printed. . .IP "\fBmeter\-stats \fIswitch\fR [\fImeter\fR]" Print meter statistics. \fImeter\fR can specify a single meter with syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR. . .IP "\fBmeter\-features \fIswitch\fR" Print meter features. . .SS OpenFlow Switch Bundle Command . Transactional updates to both flow and group tables can be made with the \fBbundle\fR command. \fIfile\fR is a text file that contains zero or more flow mods, group mods, or packet-outs in \fBFlow Syntax\fR, \fBGroup Syntax\fR, or \fBPacket\-Out Syntax\fR, each line preceded by \fBflow\fR, \fBgroup\fR, or \fBpacket\-out\fR keyword, correspondingly. The \fBflow\fR keyword may be optionally followed by one of the keywords \fBadd\fR, \fBmodify\fR, \fBmodify_strict\fR, \fBdelete\fR, or \fBdelete_strict\fR, of which the \fBadd\fR is assumed if a bare \fBflow\fR is given. Similarly, the \fBgroup\fR keyword may be optionally followed by one of the keywords \fBadd\fR, \fBmodify\fR, \fBadd_or_mod\fR, \fBdelete\fR, \fBinsert_bucket\fR, or \fBremove_bucket\fR, of which the \fBadd\fR is assumed if a bare \fBgroup\fR is given. . .IP "\fBbundle \fIswitch file\fR" Execute all flow and group mods in \fIfile\fR as a single atomic transaction against \fIswitch\fR's tables. All bundled mods are executed in the order specified. . .SS "OpenFlow Switch Tunnel TLV Table Commands" . Open vSwitch maintains a mapping table between tunnel option TLVs (defined by ) and NXM fields \fBtun_metadata\fIn\fR, where \fIn\fR ranges from 0 to 63, that can be operated on for the purposes of matches, actions, etc. This TLV table can be used for Geneve option TLVs or other protocols with options in same TLV format as Geneve options. This mapping must be explicitly specified by the user through the following commands. A TLV mapping is specified with the syntax \fB{class=\fIclass\fB,type=\fItype\fB,len=\fIlength\fB}->tun_metadata\fIn\fR. When an option mapping exists for a given \fBtun_metadata\fIn\fR, matching on the defined field becomes possible, e.g.: .RS ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0" .PP ovs-ofctl add-flow br0 tun_metadata0=1234,actions=controller .RE A mapping should not be changed while it is in active use by a flow. The result of doing so is undefined. These commands are Nicira extensions to OpenFlow and require Open vSwitch 2.5 or later. .IP "\fBadd\-tlv\-map \fIswitch option\fR[\fB,\fIoption\fR]..." Add each \fIoption\fR to \fIswitch\fR's tables. Duplicate fields are rejected. . .IP "\fBdel\-tlv\-map \fIswitch \fR[\fIoption\fR[\fB,\fIoption\fR]]..." Delete each \fIoption\fR from \fIswitch\fR's table, or all option TLV mapping if no \fIoption\fR is specified. Fields that aren't mapped are ignored. . .IP "\fBdump\-tlv\-map \fIswitch\fR" Show the currently mapped fields in the switch's option table as well as switch capabilities. . .SS "OpenFlow Switch Monitoring Commands" . .IP "\fBsnoop \fIswitch\fR" Connects to \fIswitch\fR and prints to the console all OpenFlow messages received. Unlike other \fBovs\-ofctl\fR commands, if \fIswitch\fR is the name of a bridge, then the \fBsnoop\fR command connects to a Unix domain socket named \fB@RUNDIR@/\fIswitch\fB.snoop\fR. \fBovs\-vswitchd\fR listens on such a socket for each bridge and sends to it all of the OpenFlow messages sent to or received from its configured OpenFlow controller. Thus, this command can be used to view OpenFlow protocol activity between a switch and its controller. .IP When a switch has more than one controller configured, only the traffic to and from a single controller is output. If none of the controllers is configured as a primary or a secondary (using a Nicira extension to OpenFlow 1.0 or 1.1, or a standard request in OpenFlow 1.2 or later), then a controller is chosen arbitrarily among them. If there is a primary controller, it is chosen; otherwise, if there are any controllers that are not primaries or secondaries, one is chosen arbitrarily; otherwise, a secondary controller is chosen arbitrarily. This choice is made once at connection time and does not change as controllers reconfigure their roles. .IP If a switch has no controller configured, or if the configured controller is disconnected, no traffic is sent, so monitoring will not show any traffic. . .IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fBinvalid_ttl\fR] [\fBwatch:\fR[\fIspec\fR...]]" Connects to \fIswitch\fR and prints to the console all OpenFlow messages received. Usually, \fIswitch\fR should specify the name of a bridge in the \fBovs\-vswitchd\fR database. This is available only in OpenFlow 1.0 as Nicira extension. .IP If \fImiss-len\fR is provided, \fBovs\-ofctl\fR sends an OpenFlow ``set configuration'' message at connection setup time that requests \fImiss-len\fR bytes of each packet that misses the flow table. Open vSwitch does not send these and other asynchronous messages to an \fBovs\-ofctl monitor\fR client connection unless a nonzero value is specified on this argument. (Thus, if \fImiss\-len\fR is not specified, very little traffic will ordinarily be printed.) .IP If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set configuration'' message at connection setup time that requests \fBINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can receive ``packet-in'' messages when TTL reaches zero on \fBdec_ttl\fR action. Only OpenFlow 1.1 and 1.2 support \fBinvalid_ttl\fR; Open vSwitch also implements it for OpenFlow 1.0 as an extension. .IP \fBwatch:\fR[\fB\fIspec\fR...] causes \fBovs\-ofctl\fR to send a ``monitor request'' Nicira extension message to the switch at connection setup time. This message causes the switch to send information about flow table changes as they occur. The following comma-separated \fIspec\fR syntax is available: .RS .IP "\fB!initial\fR" Do not report the switch's initial flow table contents. .IP "\fB!add\fR" Do not report newly added flows. .IP "\fB!delete\fR" Do not report deleted flows. .IP "\fB!modify\fR" Do not report modifications to existing flows. .IP "\fB!own\fR" Abbreviate changes made to the flow table by \fBovs\-ofctl\fR's own connection to the switch. (These could only occur using the \fBofctl/send\fR command described below under \fBRUNTIME MANAGEMENT COMMANDS\fR.) .IP "\fB!actions\fR" Do not report actions as part of flow updates. .IP "\fBtable=\fItable\fR" Limits the monitoring to the table with the given \fItable\fR, which may be expressed as a number between 0 and 254 or (unless \fB\-\-no\-names\fR is specified) a name. By default, all tables are monitored. .IP "\fBout_port=\fIport\fR" If set, only flows that output to \fIport\fR are monitored. The \fIport\fR may be an OpenFlow port number or keyword (e.g. \fBLOCAL\fR). .IP "\fBout_group=\fIgroup\fR" If set, only flows that output to \fIgroup\fR number are monitored. This field requires OpenFlow 1.4 (-OOpenFlow14) or later. .IP "\fIfield\fB=\fIvalue\fR" Monitors only flows that have \fIfield\fR specified as the given \fIvalue\fR. Any syntax valid for matching on \fBdump\-flows\fR may be used. .RE .IP This command may be useful for debugging switch or controller implementations. With \fBwatch:\fR, it is particularly useful for observing how a controller updates flow tables. . .SS "OpenFlow Switch and Controller Commands" . The following commands, like those in the previous section, may be applied to OpenFlow switches, using any of the connection methods described in that section. Unlike those commands, these may also be applied to OpenFlow controllers. . .TP \fBprobe \fItarget\fR Sends a single OpenFlow echo-request message to \fItarget\fR and waits for the response. With the \fB\-t\fR or \fB\-\-timeout\fR option, this command can test whether an OpenFlow switch or controller is up and running. . .TP \fBping \fItarget \fR[\fIn\fR] Sends a series of 10 echo request packets to \fItarget\fR and times each reply. The echo request packets consist of an OpenFlow header plus \fIn\fR bytes (default: 64) of randomly generated payload. This measures the latency of individual requests. . .TP \fBbenchmark \fItarget n count\fR Sends \fIcount\fR echo request packets that each consist of an OpenFlow header plus \fIn\fR bytes of payload and waits for each response. Reports the total time required. This is a measure of the maximum bandwidth to \fItarget\fR for round-trips of \fIn\fR-byte messages. . .SS "Other Commands" . .IP "\fBofp\-parse\fR \fIfile\fR" Reads \fIfile\fR (or \fBstdin\fR if \fIfile\fR is \fB\-\fR) as a series of OpenFlow messages in the binary format used on an OpenFlow connection, and prints them to the console. This can be useful for printing OpenFlow messages captured from a TCP stream. . .IP "\fBofp\-parse\-pcap\fR \fIfile\fR [\fIport\fR...]" Reads \fIfile\fR, which must be in the PCAP format used by network capture tools such as \fBtcpdump\fR or \fBwireshark\fR, extracts all the TCP streams for OpenFlow connections, and prints the OpenFlow messages in those connections in human-readable format on \fBstdout\fR. .IP OpenFlow connections are distinguished by TCP port number. Non-OpenFlow packets are ignored. By default, data on TCP ports 6633 and 6653 are considered to be OpenFlow. Specify one or more \fIport\fR arguments to override the default. .IP This command cannot usefully print SSL/TLS encrypted traffic. It does not understand IPv6. . .SS "Flow Syntax" .PP Some \fBovs\-ofctl\fR commands accept an argument that describes a flow or flows. Such flow descriptions comprise a series of \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a flow description normally requires quoting to prevent the shell from breaking the description into multiple arguments.) .PP Flow descriptions should be in \fBnormal form\fR. This means that a flow may only specify a value for an L3 field if it also specifies a particular L2 protocol, and that a flow may only specify an L4 field if it also specifies particular L2 and L3 protocol types. For example, if the L2 protocol type \fBdl_type\fR is wildcarded, then L3 fields \fBnw_src\fR, \fBnw_dst\fR, and \fBnw_proto\fR must also be wildcarded. Similarly, if \fBdl_type\fR or \fBnw_proto\fR (the L3 protocol type) is wildcarded, so must be the L4 fields \fBtcp_dst\fR and \fBtcp_src\fR. \fBovs\-ofctl\fR will warn about flows not in normal form. .PP \fBovs\-fields\fR(7) describes the supported fields and how to match them. In addition to match fields, commands that operate on flows accept a few additional key-value pairs: . .IP \fBtable=\fItable\fR For flow dump commands, limits the flows dumped to those in \fItable\fR, which may be expressed as a number between 0 and 255 or (unless \fB\-\-no\-names\fR is specified) a name. If not specified (or if 255 is specified as \fItable\fR), then flows in all tables are dumped. . .IP For flow table modification commands, behavior varies based on the OpenFlow version used to connect to the switch: . .RS .IP "OpenFlow 1.0" OpenFlow 1.0 does not support \fBtable\fR for modifying flows. \fBovs\-ofctl\fR will exit with an error if \fBtable\fR (other than \fBtable=255\fR) is specified for a switch that only supports OpenFlow 1.0. .IP In OpenFlow 1.0, the switch chooses the table into which to insert a new flow. The Open vSwitch software switch always chooses table 0. Other Open vSwitch datapaths and other OpenFlow implementations may choose different tables. .IP The OpenFlow 1.0 behavior in Open vSwitch for modifying or removing flows depends on whether \fB\-\-strict\fR is used. Without \fB\-\-strict\fR, the command applies to matching flows in all tables. With \fB\-\-strict\fR, the command will operate on any single matching flow in any table; it will do nothing if there are matches in more than one table. (The distinction between these behaviors only matters if non-OpenFlow 1.0 commands were also used, because OpenFlow 1.0 alone cannot add flows with the same matching criteria to multiple tables.) . .IP "OpenFlow 1.0 with table_id extension" Open vSwitch implements an OpenFlow extension that allows the controller to specify the table on which to operate. \fBovs\-ofctl\fR automatically enables the extension when \fBtable\fR is specified and OpenFlow 1.0 is used. \fBovs\-ofctl\fR automatically detects whether the switch supports the extension. As of this writing, this extension is only known to be implemented by Open vSwitch. . .IP With this extension, \fBovs\-ofctl\fR operates on the requested table when \fBtable\fR is specified, and acts as described for OpenFlow 1.0 above when no \fBtable\fR is specified (or for \fBtable=255\fR). . .IP "OpenFlow 1.1" OpenFlow 1.1 requires flow table modification commands to specify a table. When \fBtable\fR is not specified (or \fBtable=255\fR is specified), \fBovs\-ofctl\fR defaults to table 0. . .IP "OpenFlow 1.2 and later" OpenFlow 1.2 and later allow flow deletion commands, but not other flow table modification commands, to operate on all flow tables, with the behavior described above for OpenFlow 1.0. .RE .IP "\fBduration=\fR..." .IQ "\fBn_packet=\fR..." .IQ "\fBn_bytes=\fR..." \fBovs\-ofctl\fR ignores assignments to these ``fields'' to allow output from the \fBdump\-flows\fR command to be used as input for other commands that parse flows. . .PP The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands require an additional field, which must be the final field specified: . .IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR Specifies a comma-separated list of actions to take on a packet when the flow entry matches. If no \fIaction\fR is specified, then packets matching the flow are dropped. See \fBovs\-actions\fR(7) for details on the syntax and semantics of actions. K .PP An opaque identifier called a cookie can be used as a handle to identify a set of flows: . .IP \fBcookie=\fIvalue\fR . A cookie can be associated with a flow using the \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands. \fIvalue\fR can be any 64-bit number and need not be unique among flows. If this field is omitted, a default cookie value of 0 is used. . .IP \fBcookie=\fIvalue\fR\fB/\fImask\fR . When using NXM, the cookie can be used as a handle for querying, modifying, and deleting flows. \fIvalue\fR and \fImask\fR may be supplied for the \fBdel\-flows\fR, \fBmod\-flows\fR, \fBdump\-flows\fR, and \fBdump\-aggregate\fR commands to limit matching cookies. A 1-bit in \fImask\fR indicates that the corresponding bit in \fIcookie\fR must match exactly, and a 0-bit wildcards that bit. A mask of \-1 may be used to exactly match a cookie. .IP The \fBmod\-flows\fR command can update the cookies of flows that match a cookie by specifying the \fIcookie\fR field twice (once with a mask for matching and once without to indicate the new value): .RS .IP "\fBovs\-ofctl mod\-flows br0 cookie=1,actions=normal\fR" Change all flows' cookies to 1 and change their actions to \fBnormal\fR. .IP "\fBovs\-ofctl mod\-flows br0 cookie=1/\-1,cookie=2,actions=normal\fR" Update cookies with a value of 1 to 2 and change their actions to \fBnormal\fR. .RE .IP The ability to match on cookies was added in Open vSwitch 1.5.0. . .PP The following additional field sets the priority for flows added by the \fBadd\-flow\fR and \fBadd\-flows\fR commands. For \fBmod\-flows\fR and \fBdel\-flows\fR when \fB\-\-strict\fR is specified, priority must match along with the rest of the flow specification. For \fBmod-flows\fR without \fB\-\-strict\fR, priority is only significant if the command creates a new flow, that is, non-strict \fBmod\-flows\fR does not match on priority and will not change the priority of existing flows. Other commands do not allow priority to be specified. . .IP \fBpriority=\fIvalue\fR The priority at which a wildcarded entry will match in comparison to others. \fIvalue\fR is a number between 0 and 65535, inclusive. A higher \fIvalue\fR will match before a lower one. An exact-match entry will always have priority over an entry containing wildcards, so it has an implicit priority value of 65535. When adding a flow, if the field is not specified, the flow's priority will default to 32768. .IP OpenFlow leaves behavior undefined when two or more flows with the same priority can match a single packet. Some users expect ``sensible'' behavior, such as more specific flows taking precedence over less specific flows, but OpenFlow does not specify this and Open vSwitch does not implement it. Users should therefore take care to use priorities to ensure the behavior that they expect. . .PP The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands support the following additional options. These options affect only new flows. Thus, for \fBadd\-flow\fR and \fBadd\-flows\fR, these options are always significant, but for \fBmod\-flows\fR they are significant only if the command creates a new flow, that is, their values do not update or affect existing flows. . .IP "\fBidle_timeout=\fIseconds\fR" Causes the flow to expire after the given number of seconds of inactivity. A value of 0 (the default) prevents a flow from expiring due to inactivity. . .IP \fBhard_timeout=\fIseconds\fR Causes the flow to expire after the given number of seconds, regardless of activity. A value of 0 (the default) gives the flow no hard expiration deadline. . .IP "\fBimportance=\fIvalue\fR" Sets the importance of a flow. The flow entry eviction mechanism can use importance as a factor in deciding which flow to evict. A value of 0 (the default) makes the flow non-evictable on the basis of importance. Specify a value between 0 and 65535. .IP Only OpenFlow 1.4 and later support \fBimportance\fR. . .IP "\fBsend_flow_rem\fR" Marks the flow with a flag that causes the switch to generate a ``flow removed'' message and send it to interested controllers when the flow later expires or is removed. . .IP "\fBcheck_overlap\fR" Forces the switch to check that the flow match does not overlap that of any different flow with the same priority in the same table. (This check is expensive so it is best to avoid it.) . .IP "\fBreset_counts\fR" When this flag is specified on a flow being added to a switch, and the switch already has a flow with an identical match, an OpenFlow 1.2 (or later) switch resets the flow's packet and byte counters to 0. Without the flag, the packet and byte counters are preserved. .IP OpenFlow 1.0 and 1.1 switches always reset counters in this situation, as if \fBreset_counts\fR were always specified. .IP Open vSwitch 1.10 added support for \fBreset_counts\fR. . .IP "\fBno_packet_counts\fR" .IQ "\fBno_byte_counts\fR" Adding these flags to a flow advises an OpenFlow 1.3 (or later) switch that the controller does not need packet or byte counters, respectively, for the flow. Some switch implementations might achieve higher performance or reduce resource consumption when these flags are used. These flags provide no benefit to the Open vSwitch software switch implementation. .IP OpenFlow 1.2 and earlier do not support these flags. .IP Open vSwitch 1.10 added support for \fBno_packet_counts\fR and \fBno_byte_counts\fR. . .PP The \fBdump\-flows\fR, \fBdump\-aggregate\fR, \fBdel\-flow\fR and \fBdel\-flows\fR commands support these additional optional fields: . .TP \fBout_port=\fIport\fR If set, a matching flow must include an output action to \fIport\fR, which must be an OpenFlow port number or name (e.g. \fBlocal\fR). . .TP \fBout_group=\fIgroup\fR If set, a matching flow must include an \fBgroup\fR action naming \fIgroup\fR, which must be an OpenFlow group number. This field is supported in Open vSwitch 2.5 and later and requires OpenFlow 1.1 or later. . .SS "Table Entry Output" . The \fBdump\-tables\fR and \fBdump\-aggregate\fR commands print information about the entries in a datapath's tables. Each line of output is a flow entry as described in \fBFlow Syntax\fR, above, plus some additional fields: . .IP \fBduration=\fIsecs\fR The time, in seconds, that the entry has been in the table. \fIsecs\fR includes as much precision as the switch provides, possibly to nanosecond resolution. . .IP \fBn_packets\fR The number of packets that have matched the entry. . .IP \fBn_bytes\fR The total number of bytes from packets that have matched the entry. . .PP The following additional fields are included only if the switch is Open vSwitch 1.6 or later and the NXM flow format is used to dump the flow (see the description of the \fB\-\-flow-format\fR option below). The values of these additional fields are approximations only and in particular \fBidle_age\fR will sometimes become nonzero even for busy flows. . .IP \fBhard_age=\fIsecs\fR The integer number of seconds since the flow was added or modified. \fBhard_age\fR is displayed only if it differs from the integer part of \fBduration\fR. (This is separate from \fBduration\fR because \fBmod\-flows\fR restarts the \fBhard_timeout\fR timer without zeroing \fBduration\fR.) . .IP \fBidle_age=\fIsecs\fR The integer number of seconds that have passed without any packets passing through the flow. . .SS "Packet\-Out Syntax" .PP \fBovs\-ofctl bundle\fR command accepts packet-outs to be specified in the bundle file. Each packet-out comprises of a series of \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a packet-out description normally requires quoting to prevent the shell from breaking the description into multiple arguments.). Unless noted otherwise only the last instance of each field is honoured. This same syntax is also supported by the \fBovs\-ofctl packet-out\fR command. .PP .IP \fBin_port=\fIport\fR The port number to be considered the in_port when processing actions. This can be any valid OpenFlow port number, or any of the \fBLOCAL\fR, \fBCONTROLLER\fR, or \fBNONE\fR. . This field is required. .IP \fIpipeline_field\fR=\fIvalue\fR Optionally, user can specify a list of pipeline fields for a packet-out message. The supported pipeline fields includes \fBtunnel fields\fR and \fBregister fields\fR as defined in \fBovs\-fields\fR(7). .IP \fBpacket=\fIhex-string\fR The actual packet to send, expressed as a string of hexadecimal bytes. . This field is required. .IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR The syntax of actions are identical to the \fBactions=\fR field described in \fBFlow Syntax\fR above. Specifying \fBactions=\fR is optional, but omitting actions is interpreted as a drop, so the packet will not be sent anywhere from the switch. . \fBactions\fR must be specified at the end of each line, like for flow mods. .RE . .SS "Group Syntax" .PP Some \fBovs\-ofctl\fR commands accept an argument that describes a group or groups. Such flow descriptions comprise a series \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a group description normally requires quoting to prevent the shell from breaking the description into multiple arguments.). Unless noted otherwise only the last instance of each field is honoured. .PP .IP \fBgroup_id=\fIid\fR The integer group id of group. When this field is specified in \fBdel\-groups\fR or \fBdump\-groups\fR, the keyword "all" may be used to designate all groups. . This field is required. .IP \fBtype=\fItype\fR The type of the group. The \fBadd-group\fR, \fBadd-groups\fR and \fBmod-groups\fR commands require this field. It is prohibited for other commands. The following keywords designated the allowed types: .RS .IP \fBall\fR Execute all buckets in the group. .IP \fBselect\fR Execute one bucket in the group, balancing across the buckets according to their weights. To select a bucket, for each live bucket, Open vSwitch hashes flow data with the bucket ID and multiplies by the bucket weight to obtain a ``score,'' and then selects the bucket with the highest score. Use \fBselection_method\fR to control the flow data used for selection. .IP \fBindirect\fR Executes the one bucket in the group. .IP \fBff\fR .IQ \fBfast_failover\fR Executes the first live bucket in the group which is associated with a live port or group. .RE .IP \fBcommand_bucket_id=\fIid\fR The bucket to operate on. The \fBinsert-buckets\fR and \fBremove-buckets\fR commands require this field. It is prohibited for other commands. \fIid\fR may be an integer or one of the following keywords: .RS .IP \fBall\fR Operate on all buckets in the group. Only valid when used with the \fBremove-buckets\fR command in which case the effect is to remove all buckets from the group. .IP \fBfirst\fR Operate on the first bucket present in the group. In the case of the \fBinsert-buckets\fR command the effect is to insert new bucets just before the first bucket already present in the group; or to replace the buckets of the group if there are no buckets already present in the group. In the case of the \fBremove-buckets\fR command the effect is to remove the first bucket of the group; or do nothing if there are no buckets present in the group. .IP \fBlast\fR Operate on the last bucket present in the group. In the case of the \fBinsert-buckets\fR command the effect is to insert new bucets just after the last bucket already present in the group; or to replace the buckets of the group if there are no buckets already present in the group. In the case of the \fBremove-buckets\fR command the effect is to remove the last bucket of the group; or do nothing if there are no buckets present in the group. .RE .IP If \fIid\fR is an integer then it should correspond to the \fBbucket_id\fR of a bucket present in the group. In case of the \fBinsert-buckets\fR command the effect is to insert buckets just before the bucket in the group whose \fBbucket_id\fR is \fIid\fR. In case of the \fBiremove-buckets\fR command the effect is to remove the in the group whose \fBbucket_id\fR is \fIid\fR. It is an error if there is no bucket persent group in whose \fBbucket_id\fR is \fIid\fR. .IP \fBselection_method\fR=\fImethod\fR The selection method used to select a bucket for a select group. This is a string of 1 to 15 bytes in length known to lower layers. This field is optional for \fBadd\-group\fR, \fBadd\-groups\fR and \fBmod\-group\fR commands on groups of type \fBselect\fR. Prohibited otherwise. If no selection method is specified, Open vSwitch up to release 2.9 applies the \fBhash\fR method with default fields. From 2.10 onwards Open vSwitch defaults to the \fBdp_hash\fR method with symmetric L3/L4 hash algorithm, as long as the weighted group buckets can be mapped to dp_hash values with sufficient accuracy. In 2.10 this was restricted to a maximum of 64 buckets, and in 2.17 the limit was raised to 256 buckets. In those rare cases Open vSwitch 2.10 and later fall back to the \fBhash\fR method with the default set of hash fields. .RS .IP \fBdp_hash\fR Use a datapath computed hash value. The hash algorithm varies across different datapath implementations. \fBdp_hash\fR uses the upper 32 bits of the \fBselection_method_param\fR as the datapath hash algorithm selector. The supported values are \fB0\fR (corresponding to hash computation over the IP 5-tuple) and \fB1\fR (corresponding to a \fIsymmetric\fR hash computation over the IP 5-tuple). Selecting specific fields with the \fBfields\fR option is not supported with \fBdp_hash\fR). The lower 32 bits are used as the hash basis. .IP Using \fBdp_hash\fR has the advantage that it does not require the generated datapath flows to exact match any additional packet header fields. For example, even if multiple TCP connections thus hashed to different select group buckets have different source port numbers, generally all of them would be handled with a small set of already established datapath flows, resulting in less latency for TCP SYN packets. The downside is that the shared datapath flows must match each packet twice, as the datapath hash value calculation happens only when needed, and a second match is required to match some bits of its value. This double-matching incurs a small additional latency cost for each packet, but this latency is orders of magnitude less than the latency of creating new datapath flows for new TCP connections. .IP \fBhash\fR Use a hash computed over the fields specified with the \fBfields\fR option, see below. If no hash fields are specified, \fBhash\fR defaults to a symmetric hash over the combination of MAC addresses, VLAN tags, Ether type, IP addresses and L4 port numbers. \fBhash\fR uses the \fBselection_method_param\fR as the hash basis. .IP Note that the hashed fields become exact matched by the datapath flows. For example, if the TCP source port is hashed, the created datapath flows will match the specific TCP source port value present in the packet received. Since each TCP connection generally has a different source port value, a separate datapath flow will be need to be inserted for each TCP connection thus hashed to a select group bucket. .RE .IP This option uses a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBselection_method_param\fR=\fIparam\fR 64-bit integer parameter to the selection method selected by the \fBselection_method\fR field. The parameter's use is defined by the lower-layer that implements the \fBselection_method\fR. It is optional if the \fBselection_method\fR field is specified as a non-empty string. Prohibited otherwise. The default value is zero. .IP This option uses a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBfields\fR=\fIfield\fR .IQ \fBfields(\fIfield\fR[\fB=\fImask\fR]\fR...\fB)\fR The field parameters to selection method selected by the \fBselection_method\fR field. The syntax is described in \fBFlow Syntax\fR with the additional restrictions that if a value is provided it is treated as a wildcard mask and wildcard masks following a slash are prohibited. The pre-requisites of fields must be provided by any flows that output to the group. The use of the fields is defined by the lower-layer that implements the \fBselection_method\fR. They are optional if the \fBselection_method\fR field is specified as ``hash', prohibited otherwise. The default is no fields. .IP This option will use a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBbucket\fR=\fIbucket_parameters\fR The \fBadd-group\fR, \fBadd-groups\fR and \fBmod-group\fR commands require at least one bucket field. Bucket fields must appear after all other fields. . Multiple bucket fields to specify multiple buckets. The order in which buckets are specified corresponds to their order in the group. If the type of the group is "indirect" then only one group may be specified. . \fIbucket_parameters\fR consists of a list of \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space followed by a comma-separated list of actions. The fields for \fIbucket_parameters\fR are: . .RS .IP \fBbucket_id=\fIid\fR The 32-bit integer group id of the bucket. Values greater than 0xffffff00 are reserved. . This field was added in Open vSwitch 2.4 to conform with the OpenFlow 1.5 specification. It is not supported when earlier versions of OpenFlow are used. Open vSwitch will automatically allocate bucket ids when they are not specified. .IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR The syntax of actions are identical to the \fBactions=\fR field described in \fBFlow Syntax\fR above. Specifying \fBactions=\fR is optional, any unknown bucket parameter will be interpreted as an action. .IP \fBweight=\fIvalue\fR The relative weight of the bucket as an integer. This may be used by the switch during bucket select for groups whose \fBtype\fR is \fBselect\fR. .IP \fBwatch_port=\fIport\fR Port used to determine liveness of group. This or the \fBwatch_group\fR field is required for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR. This or the \fBwatch_group\fR field can also be used for groups whose \fBtype\fR is \fBselect\fR. .IP \fBwatch_group=\fIgroup_id\fR Group identifier of group used to determine liveness of group. This or the \fBwatch_port\fR field is required for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR. This or the \fBwatch_port\fR field can also be used for groups whose \fBtype\fR is \fBselect\fR. .RE . .SS "Meter Syntax" .PP The meter table commands accept an argument that describes a meter. Such meter descriptions comprise a series \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a group description normally requires quoting to prevent the shell from breaking the description into multiple arguments.). Unless noted otherwise only the last instance of each field is honoured. .PP .IP \fBmeter=\fIid\fR The identifier for the meter. An integer is used to specify a user-defined meter. In addition, the keywords "all", "controller", and "slowpath", are also supported as virtual meters. The "controller" and "slowpath" virtual meters apply to packets sent to the controller and to the OVS userspace, respectively. .IP When this field is specified in \fBdel-meter\fR, \fBdump-meter\fR, or \fBmeter-stats\fR, the keyword "all" may be used to designate all meters. This field is required, except for \fBmeter-stats\fR, which dumps all stats when this field is not specified. .IP \fBkbps\fR .IQ \fBpktps\fR The unit for the \fBrate\fR and \fBburst_size\fR band parameters. \fBkbps\fR specifies kilobits per second, and \fBpktps\fR specifies packets per second. A unit is required for the \fBadd-meter\fR and \fBmod-meter\fR commands. .IP \fBburst\fR If set, enables burst support for meter bands through the \fBburst_size\fR parameter. .IP \fBstats\fR If set, enables the collection of meter and band statistics. .IP \fBbands\fR=\fIband_parameters\fR The \fBadd-meter\fR and \fBmod-meter\fR commands require at least one band specification. Bands must appear after all other fields. .RS .IP \fBtype=\fItype\fR The type of the meter band. This keyword starts a new band specification. Each band specifies a rate above which the band is to take some action. The action depends on the band type. If multiple bands' rate is exceeded, then the band with the highest rate among the exceeded bands is selected. The following keywords designate the allowed meter band types: .RS .IP \fBdrop\fR Drop packets exceeding the band's rate limit. .RE . .IP "The other \fIband_parameters\fR are:" .IP \fBrate=\fIvalue\fR The relative rate limit for this band, in kilobits per second or packets per second, depending on whether \fBkbps\fR or \fBpktps\fR was specified. .IP \fBburst_size=\fIsize\fR If \fBburst\fR is specified for the meter entry, configures the maximum burst allowed for the band in kilobits or packets, depending on whether \fBkbps\fR or \fBpktps\fR was specified. If unspecified, the switch is free to select some reasonable value depending on its configuration. .RE . .SH OPTIONS .TP \fB\-\-strict\fR Uses strict matching when running flow modification commands. . .IP "\fB\-\-names\fR" .IQ "\fB\-\-no\-names\fR" Every OpenFlow port has a name and a number, and every OpenFlow flow table has a number and sometimes a name. By default, \fBovs\-ofctl\fR commands accept both port and table names and numbers, and they display port and table names if \fBovs\-ofctl\fR is running on an interactive console, numbers otherwise. With \fB\-\-names\fR, \fBovs\-ofctl\fR commands both accept and display port and table names; with \fB\-\-no\-names\fR, commands neither accept nor display port and table names. .IP If a port or table name contains special characters or might be confused with a keyword within a flow, it may be enclosed in double quotes (escaped from the shell). If necessary, JSON-style escape sequences may be used inside quotes, as specified in RFC 7159. When it displays port and table names, \fBovs\-ofctl\fR quotes any name that does not start with a letter followed by letters or digits. .IP Open vSwitch added support for port names and these options. Open vSwitch 2.10 added support for table names. Earlier versions always behaved as if \fB\-\-no\-names\fR were specified. .IP Open vSwitch does not place its own limit on the length of port names, but OpenFlow limits port names to 15 bytes. Because \fRovs\-ofctl\fR uses OpenFlow to retrieve the mapping between port names and numbers, names longer than this limit will be truncated for both display and acceptance. Truncation can also cause long names that are different to appear to be the same; when a switch has two ports with the same (truncated) name, \fBovs\-ofctl\fR refuses to display or accept the name, using the number instead. .IP OpenFlow and Open vSwitch limit table names to 32 bytes. . .IP "\fB\-\-stats\fR" .IQ "\fB\-\-no\-stats\fR" The \fBdump\-flows\fR command by default, or with \fB\-\-stats\fR, includes flow duration, packet and byte counts, and idle and hard age in its output. With \fB\-\-no\-stats\fR, it omits all of these, as well as cookie values and table IDs if they are zero. . .IP "\fB\-\-read-only\fR" Do not execute read/write commands. . .IP "\fB\-\-bundle\fR" Execute flow mods as an OpenFlow 1.4 atomic bundle transaction. .RS .IP \(bu Within a bundle, all flow mods are processed in the order they appear and as a single atomic transaction, meaning that if one of them fails, the whole transaction fails and none of the changes are made to the \fIswitch\fR's flow table, and that each given datapath packet traversing the OpenFlow tables sees the flow tables either as before the transaction, or after all the flow mods in the bundle have been successfully applied. .IP \(bu The beginning and the end of the flow table modification commands in a bundle are delimited with OpenFlow 1.4 bundle control messages, which makes it possible to stream the included commands without explicit OpenFlow barriers, which are otherwise used after each flow table modification command. This may make large modifications execute faster as a bundle. .IP \(bu Bundles require OpenFlow 1.4 or higher. An explicit \fB-O OpenFlow14\fR option is not needed, but you may need to enable OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR column in the \fIbridge\fR table. .RE . .so lib/ofp-version.man . .IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]" .IQ "\fB\-\-flow\-format=\fIformat\fR[\fB,\fIformat\fR...]" \fBovs\-ofctl\fR supports the following individual flow formats, any number of which may be listed as \fIformat\fR: .RS .IP "\fBOpenFlow10\-table_id\fR" This is the standard OpenFlow 1.0 flow format. All OpenFlow switches and all versions of Open vSwitch support this flow format. . .IP "\fBOpenFlow10+table_id\fR" This is the standard OpenFlow 1.0 flow format plus a Nicira extension that allows \fBovs\-ofctl\fR to specify the flow table in which a particular flow should be placed. Open vSwitch 1.2 and later supports this flow format. . .IP "\fBNXM\-table_id\fR (Nicira Extended Match)" This Nicira extension to OpenFlow is flexible and extensible. It supports all of the Nicira flow extensions, such as \fBtun_id\fR and registers. Open vSwitch 1.1 and later supports this flow format. . .IP "\fBNXM+table_id\fR (Nicira Extended Match)" This combines Nicira Extended match with the ability to place a flow in a specific table. Open vSwitch 1.2 and later supports this flow format. . .IP "\fBOXM-OpenFlow12\fR" .IQ "\fBOXM-OpenFlow13\fR" .IQ "\fBOXM-OpenFlow14\fR" .IQ "\fBOXM-OpenFlow15\fR" These are the standard OXM (OpenFlow Extensible Match) flow format in OpenFlow 1.2 and later. .RE . .IP \fBovs\-ofctl\fR also supports the following abbreviations for collections of flow formats: .RS .IP "\fBany\fR" Any supported flow format. .IP "\fBOpenFlow10\fR" \fBOpenFlow10\-table_id\fR or \fBOpenFlow10+table_id\fR. .IP "\fBNXM\fR" \fBNXM\-table_id\fR or \fBNXM+table_id\fR. .IP "\fBOXM\fR" \fBOXM-OpenFlow12\fR, \fBOXM-OpenFlow13\fR, or \fBOXM-OpenFlow14\fR. .RE . .IP For commands that modify the flow table, \fBovs\-ofctl\fR by default negotiates the most widely supported flow format that supports the flows being added. For commands that query the flow table, \fBovs\-ofctl\fR by default uses the most advanced format supported by the switch. .IP This option, where \fIformat\fR is a comma-separated list of one or more of the formats listed above, limits \fBovs\-ofctl\fR's choice of flow format. If a command cannot work as requested using one of the specified flow formats, \fBovs\-ofctl\fR will report a fatal error. . .IP "\fB\-P \fIformat\fR" .IQ "\fB\-\-packet\-in\-format=\fIformat\fR" \fBovs\-ofctl\fR supports the following ``packet-in'' formats, in order of increasing capability: .RS .IP "\fBstandard\fR" This uses the \fBOFPT_PACKET_IN\fR message, the standard ``packet-in'' message for any given OpenFlow version. Every OpenFlow switch that supports a given OpenFlow version supports this format. . .IP "\fBnxt_packet_in\fR" This uses the \fBNXT_PACKET_IN\fR message, which adds many of the capabilities of the OpenFlow 1.1 and later ``packet-in'' messages before those OpenFlow versions were available in Open vSwitch. Open vSwitch 1.1 and later support this format. Only Open vSwitch 2.6 and later, however, support it for OpenFlow 1.1 and later (but there is little reason to use it with those versions of OpenFlow). . .IP "\fBnxt_packet_in2\fR" This uses the \fBNXT_PACKET_IN2\fR message, which is extensible and should avoid the need to define new formats later. In particular, this format supports passing arbitrary user-provided data to a controller using the \fBuserdata\fB option on the \fBcontroller\fR action. Open vSwitch 2.6 and later support this format. . .RE .IP Without this option, \fBovs\-ofctl\fR prefers \fBnxt_packet_in2\fR if the switch supports it. Otherwise, if OpenFlow 1.0 is in use, \fBovs\-ofctl\fR prefers \fBnxt_packet_in\fR if the switch supports it. Otherwise, \fBovs\-ofctl\fR falls back to the \fBstandard\fR packet-in format. When this option is specified, \fBovs\-ofctl\fR insists on the selected format. If the switch does not support the requested format, \fBovs\-ofctl\fR will report a fatal error. .IP Before version 2.6, Open vSwitch called \fBstandard\fR format \fBopenflow10\fR and \fBnxt_packet_in\fR format \fBnxm\fR, and \fBovs\-ofctl\fR still accepts these names as synonyms. (The name \fBopenflow10\fR was a misnomer because this format actually varies from one OpenFlow version to another; it is not consistently OpenFlow 1.0 format. Similarly, when \fBnxt_packet_in2\fR was introduced, the name \fBnxm\fR became confusing because it also uses OXM/NXM.) . .IP This option affects only the \fBmonitor\fR command. . .IP "\fB\-\-timestamp\fR" Print a timestamp before each received packet. This option only affects the \fBmonitor\fR, \fBsnoop\fR, and \fBofp\-parse\-pcap\fR commands. . .IP "\fB\-m\fR" .IQ "\fB\-\-more\fR" Increases the verbosity of OpenFlow messages printed and logged by \fBovs\-ofctl\fR commands. Specify this option more than once to increase verbosity further. . .IP \fB\-\-sort\fR[\fB=\fIfield\fR] .IQ \fB\-\-rsort\fR[\fB=\fIfield\fR] Display output sorted by flow \fIfield\fR in ascending (\fB\-\-sort\fR) or descending (\fB\-\-rsort\fR) order, where \fIfield\fR is any of the fields that are allowed for matching or \fBpriority\fR to sort by priority. When \fIfield\fR is omitted, the output is sorted by priority. Specify these options multiple times to sort by multiple fields. .IP Any given flow will not necessarily specify a value for a given field. This requires special treatement: .RS .IP \(bu A flow that does not specify any part of a field that is used for sorting is sorted after all the flows that do specify the field. For example, \fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP source port in ascending order, followed by the flows that do not specify a TCP source port at all. .IP \(bu A flow that only specifies some bits in a field is sorted as if the wildcarded bits were zero. For example, \fB\-\-sort=nw_src\fR would sort a flow that specifies \fBnw_src=192.168.0.0/24\fR the same as \fBnw_src=192.168.0.0\fR. .RE .IP These options currently affect only \fBdump\-flows\fR output. . .SS "Daemon Options" .ds DD \ \fBovs\-ofctl\fR detaches only when executing the \fBmonitor\fR or \ \fBsnoop\fR commands. .so lib/daemon.man .so lib/unixctl.man .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/vlog.man .so lib/colors.man .so lib/common.man . .SH "RUNTIME MANAGEMENT COMMANDS" \fBovs\-appctl\fR(8) can send commands to a running \fBovs\-ofctl\fR process. The supported commands are listed below. . .IP "\fBexit\fR" Causes \fBovs\-ofctl\fR to gracefully terminate. This command applies only when executing the \fBmonitor\fR or \fBsnoop\fR commands. . .IP "\fBofctl/set\-output\-file \fIfile\fR" Causes all subsequent output to go to \fIfile\fR instead of stderr. This command applies only when executing the \fBmonitor\fR or \fBsnoop\fR commands. . .IP "\fBofctl/send \fIofmsg\fR..." Sends each \fIofmsg\fR, specified as a sequence of hex digits that express an OpenFlow message, on the OpenFlow connection. This command is useful only when executing the \fBmonitor\fR command. . .IP "\fBofctl/packet\-out \fIpacket-out\fR" Sends an OpenFlow PACKET_OUT message specified in \fBPacket\-Out Syntax\fR, on the OpenFlow connection. See \fBPacket\-Out Syntax\fR section for more information. This command is useful only when executing the \fBmonitor\fR command. . .IP "\fBofctl/barrier\fR" Sends an OpenFlow barrier request on the OpenFlow connection and waits for a reply. This command is useful only for the \fBmonitor\fR command. . .SH EXAMPLES . The following examples assume that \fBovs\-vswitchd\fR has a bridge named \fBbr0\fR configured. . .TP \fBovs\-ofctl dump\-tables br0\fR Prints out the switch's table stats. (This is more interesting after some traffic has passed through.) . .TP \fBovs\-ofctl dump\-flows br0\fR Prints the flow entries in the switch. . .TP \fBovs\-ofctl add\-flow table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]), resubmit(,1)\fR \fBovs\-ofctl add\-flow table=1 priority=0 actions=flood\fR Implements a level 2 MAC learning switch using the learn. . .TP \fBovs\-ofctl add\-flow br0 'table=0,priority=0 actions=load:3->NXM_NX_REG0[0..15],learn(table=0,priority=1,idle_timeout=10,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2\fR In this use of a learn action, the first packet from each source MAC will be sent to port 2. Subsequent packets will be output to port 3, with an idle timeout of 10 seconds. NXM field names and match field names are both accepted, e.g. \fBNXM_NX_REG0\fR or \fBreg0\fR for the first register, and empty brackets may be omitted. .IP Additional examples may be found documented as part of related sections. . .SH "SEE ALSO" . .BR ovs\-fields (7), .BR ovs\-actions (7), .BR ovs\-appctl (8), .BR ovs\-vswitchd (8), .BR ovs\-vswitchd.conf.db (8) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-ofctl.c000066400000000000000000005201441514270232600230610ustar00rootroot00000000000000/* * Copyright (c) 2008-2017, 2019 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "byte-order.h" #include "classifier.h" #include "command-line.h" #include "daemon.h" #include "colors.h" #include "compiler.h" #include "dirs.h" #include "dp-packet.h" #include "fatal-signal.h" #include "nx-match.h" #include "odp-util.h" #include "ofp-version-opt.h" #include "ofproto/ofproto.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/meta-flow.h" #include "openvswitch/ofp-actions.h" #include "openvswitch/ofp-bundle.h" #include "openvswitch/ofp-ct.h" #include "openvswitch/ofp-errors.h" #include "openvswitch/ofp-group.h" #include "openvswitch/ofp-match.h" #include "openvswitch/ofp-meter.h" #include "openvswitch/ofp-msgs.h" #include "openvswitch/ofp-monitor.h" #include "openvswitch/ofp-port.h" #include "openvswitch/ofp-print.h" #include "openvswitch/ofp-util.h" #include "openvswitch/ofp-parse.h" #include "openvswitch/ofp-queue.h" #include "openvswitch/ofp-switch.h" #include "openvswitch/ofp-table.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/shash.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "packets.h" #include "pcap-file.h" #include "openvswitch/poll-loop.h" #include "random.h" #include "sort.h" #include "stream-ssl.h" #include "socket-util.h" #include "timeval.h" #include "unixctl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(ofctl); /* --bundle: Use OpenFlow 1.3+ bundle for making the flow table change atomic. * NOTE: If OpenFlow 1.3 or higher is not selected with the '-O' option, * OpenFlow 1.4 will be implicitly selected. Also the flow mod will use * OpenFlow 1.4, so the semantics may be different (see the comment in * parse_options() for details). */ static bool bundle = false; /* --color: Use color markers. */ static bool enable_color; /* --read-only: Do not execute read only commands. */ static bool read_only; /* --strict: Use strict matching for flow mod commands? Additionally governs * use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match. */ static bool strict; /* --may-create: If true, the mod-group command creates a group that does not * yet exist; otherwise, such a command has no effect. */ static bool may_create; /* --readd: If true, on replace-flows, re-add even flows that have not changed * (to reset flow counters). */ static bool readd; /* -F, --flow-format: Allowed protocols. By default, any protocol is * allowed. */ static enum ofputil_protocol allowed_protocols = OFPUTIL_P_ANY; /* -P, --packet-in-format: Packet IN format to use in monitor and snoop * commands. Either one of NXPIF_* to force a particular packet_in format, or * -1 to let ovs-ofctl choose the default. */ static int preferred_packet_in_format = -1; /* -m, --more: Additional verbosity for ofp-print functions. */ static int verbosity; /* --timestamp: Print a timestamp before each received packet on "monitor" and * "snoop" command? */ static bool timestamp; /* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop" commands. */ static char *unixctl_path; /* --sort, --rsort: Sort order. */ enum sort_order { SORT_ASC, SORT_DESC }; struct sort_criterion { const struct mf_field *field; /* NULL means to sort by priority. */ enum sort_order order; }; static struct sort_criterion *criteria; static size_t n_criteria, allocated_criteria; /* --names, --no-names: Show port and table names in output and accept them in * input. (When neither is specified, the default is to accept port names but, * for backward compatibility, not to show them unless this is an interactive * console session.) */ static int use_names = -1; static const struct ofputil_port_map *ports_to_accept(const char *vconn_name); static const struct ofputil_port_map *ports_to_show(const char *vconn_name); static const struct ofputil_table_map *tables_to_accept( const char *vconn_name); static const struct ofputil_table_map *tables_to_show(const char *vconn_name); static bool should_accept_names(void); static bool should_show_names(void); /* --stats, --no-stats: Show statistics in flow dumps? */ static int show_stats = 1; /* --pcap: Makes "compose-packet" print a pcap on stdout. */ static int print_pcap = 0; /* --bare: Makes "compose-packet" print a bare hexified payload. */ static int print_bare = 0; /* -bad-csum: Makes "compose-packet" generate an invalid checksum. */ static int bad_csum = 0; /* --raw: Makes "ofp-print" read binary data from stdin. */ static int raw = 0; static const struct ovs_cmdl_command *get_all_commands(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); int main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); fatal_ignore_sigpipe(); ctx.argc = argc - optind; ctx.argv = argv + optind; daemon_become_new_user(false, false); if (read_only) { ovs_cmdl_run_command_read_only(&ctx, get_all_commands()); } else { ovs_cmdl_run_command(&ctx, get_all_commands()); } return 0; } static void add_sort_criterion(enum sort_order order, const char *field) { struct sort_criterion *sc; if (n_criteria >= allocated_criteria) { criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria); } sc = &criteria[n_criteria++]; if (!field || !strcasecmp(field, "priority")) { sc->field = NULL; } else { sc->field = mf_from_name(field); if (!sc->field) { ovs_fatal(0, "%s: unknown field name", field); } } sc->order = order; } static void parse_options(int argc, char *argv[]) { enum { OPT_STRICT = UCHAR_MAX + 1, OPT_READD, OPT_TIMESTAMP, OPT_SORT, OPT_RSORT, OPT_UNIXCTL, OPT_BUNDLE, OPT_COLOR, OPT_MAY_CREATE, OPT_READ_ONLY, DAEMON_OPTION_ENUMS, OFP_VERSION_OPTION_ENUMS, VLOG_OPTION_ENUMS, SSL_OPTION_ENUMS, }; static const struct option long_options[] = { {"timeout", required_argument, NULL, 't'}, {"strict", no_argument, NULL, OPT_STRICT}, {"readd", no_argument, NULL, OPT_READD}, {"flow-format", required_argument, NULL, 'F'}, {"packet-in-format", required_argument, NULL, 'P'}, {"more", no_argument, NULL, 'm'}, {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, {"sort", optional_argument, NULL, OPT_SORT}, {"rsort", optional_argument, NULL, OPT_RSORT}, {"names", no_argument, &use_names, 1}, {"no-names", no_argument, &use_names, 0}, {"stats", no_argument, &show_stats, 1}, {"no-stats", no_argument, &show_stats, 0}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"bundle", no_argument, NULL, OPT_BUNDLE}, {"color", optional_argument, NULL, OPT_COLOR}, {"may-create", no_argument, NULL, OPT_MAY_CREATE}, {"pcap", no_argument, &print_pcap, 1}, {"bare", no_argument, &print_bare, 1}, {"bad-csum", no_argument, &bad_csum, 1}, {"raw", no_argument, &raw, 1}, {"read-only", no_argument, NULL, OPT_READ_ONLY}, DAEMON_LONG_OPTIONS, OFP_VERSION_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); uint32_t versions; enum ofputil_protocol version_protocols; unsigned int timeout = 0; /* For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is * because ovs-ofctl implements command such as "add-flow" as raw OpenFlow * requests, but those requests have subtly different semantics in * different OpenFlow versions. For example: * * - In OpenFlow 1.0, a "mod-flow" operation that does not find any * existing flow to modify adds a new flow. * * - In OpenFlow 1.1, a "mod-flow" operation that does not find any * existing flow to modify adds a new flow, but only if the mod-flow * did not match on the flow cookie. * * - In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a * new flow. */ set_allowed_ofp_versions("OpenFlow10"); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 't': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ovs_fatal(0, "value %s on -t or --timeout is invalid", optarg); } break; case 'F': allowed_protocols = ofputil_protocols_from_string(optarg); if (!allowed_protocols) { ovs_fatal(0, "%s: invalid flow format(s)", optarg); } break; case 'P': preferred_packet_in_format = ofputil_packet_in_format_from_string(optarg); if (preferred_packet_in_format < 0) { ovs_fatal(0, "unknown packet-in format `%s'", optarg); } break; case 'm': verbosity++; break; case 'h': usage(); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case OPT_BUNDLE: bundle = true; break; case OPT_STRICT: strict = true; break; case OPT_READ_ONLY: read_only = true; break; case OPT_READD: readd = true; break; case OPT_TIMESTAMP: timestamp = true; break; case OPT_SORT: add_sort_criterion(SORT_ASC, optarg); break; case OPT_RSORT: add_sort_criterion(SORT_DESC, optarg); break; case OPT_UNIXCTL: unixctl_path = optarg; break; case OPT_COLOR: if (optarg) { if (!strcasecmp(optarg, "always") || !strcasecmp(optarg, "yes") || !strcasecmp(optarg, "force")) { enable_color = true; } else if (!strcasecmp(optarg, "never") || !strcasecmp(optarg, "no") || !strcasecmp(optarg, "none")) { enable_color = false; } else if (!strcasecmp(optarg, "auto") || !strcasecmp(optarg, "tty") || !strcasecmp(optarg, "if-tty")) { /* Determine whether we need colors, i.e. whether standard * output is a tty. */ enable_color = is_stdout_a_tty(); } else { ovs_fatal(0, "incorrect value `%s' for --color", optarg); } } else { enable_color = is_stdout_a_tty(); } break; case OPT_MAY_CREATE: may_create = true; break; DAEMON_OPTION_HANDLERS OFP_VERSION_OPTION_HANDLERS VLOG_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); case 0: break; default: abort(); } } ctl_timeout_setup(timeout); if (n_criteria) { /* Always do a final sort pass based on priority. */ add_sort_criterion(SORT_DESC, "priority"); } free(short_options); /* Implicit OpenFlow 1.4 with the '--bundle' option. */ if (bundle && !(get_allowed_ofp_versions() & ofputil_protocols_to_version_bitmap(OFPUTIL_P_OF13_UP))) { /* Add implicit allowance for OpenFlow 1.4. */ add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF14_OXM)); /* Remove all versions that do not support bundles. */ mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF13_UP)); } versions = get_allowed_ofp_versions(); version_protocols = ofputil_protocols_from_version_bitmap(versions); if (!(allowed_protocols & version_protocols)) { char *protocols = ofputil_protocols_to_string(allowed_protocols); struct ds version_s = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&version_s, versions); ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports " "any of the enabled flow formats (%s). (Use -O to enable " "additional OpenFlow versions or -F to enable additional " "flow formats.)", ds_cstr(&version_s), protocols); } allowed_protocols &= version_protocols; mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( allowed_protocols)); colors_init(enable_color); } static void usage(void) { printf("%s: OpenFlow switch management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" "\nFor OpenFlow switches:\n" " show SWITCH show OpenFlow information\n" " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " dump-table-features SWITCH print table features\n" " dump-table-desc SWITCH print table description (OF1.4+)\n" " mod-port SWITCH IFACE ACT modify port behavior\n" " mod-table SWITCH MOD modify flow table behavior\n" " OF1.1/1.2 MOD: controller, continue, drop\n" " OF1.4+ MOD: evict, noevict, vacancy:low,high, novacancy\n" " get-frags SWITCH print fragment handling behavior\n" " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" " FRAG_MODE: normal, drop, reassemble, nx-match\n" " dump-ports SWITCH [PORT] print port statistics\n" " dump-ports-desc SWITCH [PORT] print port descriptions\n" " dump-flows SWITCH print all flow entries\n" " dump-flows SWITCH FLOW print matching FLOWs\n" " dump-aggregate SWITCH print aggregate flow statistics\n" " dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n" " queue-stats SWITCH [PORT [QUEUE]] dump queue stats\n" " add-flow SWITCH FLOW add flow described by FLOW\n" " add-flows SWITCH FILE add flows from FILE\n" " mod-flows SWITCH FLOW modify actions of matching FLOWs\n" " del-flows SWITCH [FLOW] delete matching FLOWs\n" " replace-flows SWITCH FILE replace flows with those in FILE\n" " diff-flows SOURCE1 SOURCE2 compare flows from two sources\n" " packet-out SWITCH IN_PORT ACTIONS PACKET...\n" " execute ACTIONS on PACKET\n" " monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n" " print packets received from SWITCH\n" " snoop SWITCH snoop on SWITCH and its controller\n" " add-group SWITCH GROUP add group described by GROUP\n" " add-groups SWITCH FILE add group from FILE\n" " [--may-create] mod-group SWITCH GROUP modify specific group\n" " del-groups SWITCH [GROUP] delete matching GROUPs\n" " insert-buckets SWITCH [GROUP] add buckets to GROUP\n" " remove-buckets SWITCH [GROUP] remove buckets from GROUP\n" " dump-group-features SWITCH print group features\n" " dump-groups SWITCH [GROUP] print group description\n" " dump-group-stats SWITCH [GROUP] print group statistics\n" " queue-get-config SWITCH [PORT] print queue config for PORT\n" " add-meter SWITCH METER add meter described by METER\n" " mod-meter SWITCH METER modify specific METER\n" " del-meters SWITCH [METER] delete meters matching METER\n" " dump-meters SWITCH [METER] print METER configuration\n" " meter-stats SWITCH [METER] print meter statistics\n" " meter-features SWITCH print meter features\n" " add-tlv-map SWITCH MAP add TLV option MAPpings\n" " del-tlv-map SWITCH [MAP] delete TLV option MAPpings\n" " dump-tlv-map SWITCH print TLV option mappings\n" " dump-ipfix-bridge SWITCH print ipfix stats of bridge\n" " dump-ipfix-flow SWITCH print flow ipfix of a bridge\n" " ct-flush-zone SWITCH ZONE flush conntrack entries in ZONE\n" " ct-flush SWITCH [ZONE] [mark=X[/M]] [labels=Y[/N]]\n" " [CT_ORIG_TUPLE [CT_REPLY_TUPLE]]\n" " flush conntrack entries specified\n" " by CT_ORIG/REPLY_TUPLE, ZONE, mark\n" " and labels\n" "\nFor OpenFlow switches and controllers:\n" " probe TARGET probe whether TARGET is up\n" " ping TARGET [N] latency of N-byte echos\n" " benchmark TARGET N COUNT bandwidth of COUNT N-byte echos\n" "SWITCH or TARGET is an active OpenFlow connection method.\n" "\nOther commands:\n" " ofp-parse FILE print messages read from FILE\n" " ofp-parse-pcap PCAP print OpenFlow read from PCAP\n", program_name, program_name); vconn_usage(true, false, false); daemon_usage(); ofp_version_usage(); vlog_usage(); printf("\nOther options:\n" " --strict use strict match for flow commands\n" " --read-only do not execute read/write commands\n" " --readd replace flows that haven't changed\n" " -F, --flow-format=FORMAT force particular flow format\n" " -P, --packet-in-format=FRMT force particular packet in format\n" " -m, --more be more verbose printing OpenFlow\n" " --timestamp (monitor, snoop) print timestamps\n" " -t, --timeout=SECS give up after SECS seconds\n" " --sort[=field] sort in ascending order\n" " --rsort[=field] sort in descending order\n" " --names show port names instead of numbers\n" " --no-names show port numbers, but not names\n" " --unixctl=SOCKET set control socket name\n" " --color[=always|never|auto] control use of color in output\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void run(int retval, const char *message, ...) OVS_PRINTF_FORMAT(2, 3); static void run(int retval, const char *message, ...) { if (retval) { va_list args; va_start(args, message); ovs_fatal_valist(retval, message, args); } } /* Generic commands. */ static int open_vconn_socket(const char *name, struct vconn **vconnp) { char *vconn_name = xasprintf("unix:%s", name); int error; error = vconn_open(vconn_name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp); if (error && error != ENOENT) { ovs_fatal(0, "%s: failed to open socket (%s)", name, ovs_strerror(error)); } free(vconn_name); return error; } enum open_target { MGMT, SNOOP }; static enum ofputil_protocol open_vconn__(const char *name, enum open_target target, struct vconn **vconnp) { const char *suffix = target == MGMT ? "mgmt" : "snoop"; char *datapath_name, *datapath_type, *socket_name; enum ofputil_protocol protocol; char *bridge_path; int ofp_version; int error; bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, suffix); ofproto_parse_name(name, &datapath_name, &datapath_type); socket_name = xasprintf("%s/%s.%s", ovs_rundir(), datapath_name, suffix); free(datapath_name); free(datapath_type); if (strchr(name, ':')) { run(vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp), "connecting to %s", name); } else if (!open_vconn_socket(name, vconnp)) { /* Fall Through. */ } else if (!open_vconn_socket(bridge_path, vconnp)) { /* Fall Through. */ } else if (!open_vconn_socket(socket_name, vconnp)) { /* Fall Through. */ } else { free(bridge_path); free(socket_name); ovs_fatal(0, "%s is not a bridge or a socket", name); } if (target == SNOOP) { vconn_set_recv_any_version(*vconnp); } free(bridge_path); free(socket_name); VLOG_DBG("connecting to %s", vconn_get_name(*vconnp)); error = vconn_connect_block(*vconnp, -1); if (error) { ovs_fatal(0, "%s: failed to connect to socket (%s)", name, ovs_strerror(error)); } ofp_version = vconn_get_version(*vconnp); protocol = ofputil_protocol_from_ofp_version(ofp_version); if (!protocol) { ovs_fatal(0, "%s: unsupported OpenFlow version 0x%02x", name, ofp_version); } return protocol; } static enum ofputil_protocol open_vconn(const char *name, struct vconn **vconnp) { return open_vconn__(name, MGMT, vconnp); } static void send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer) { run(vconn_send_block(vconn, buffer), "failed to send packet to switch"); } static void dump_transaction(struct vconn *vconn, struct ofpbuf *request) { const struct ofp_header *oh = request->data; if (ofpmsg_is_stat_request(oh)) { ovs_be32 send_xid = oh->xid; enum ofpraw request_raw; enum ofpraw reply_raw; bool done = false; ofpraw_decode_partial(&request_raw, request->data, request->size); reply_raw = ofpraw_stats_request_to_reply(request_raw, oh->version); send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { enum ofpraw ofpraw; ofp_print(stdout, reply->data, reply->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 1); ofpraw_decode(&ofpraw, reply->data); if (ofptype_from_ofpraw(ofpraw) == OFPTYPE_ERROR) { done = true; } else if (ofpraw == reply_raw) { done = !ofpmp_more(reply->data); } else { ovs_fatal(0, "received bad reply: %s", ofp_to_string( reply->data, reply->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 1)); } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } } else { struct ofpbuf *reply; run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); ofp_print(stdout, reply->data, reply->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 1); ofpbuf_delete(reply); } } static void dump_trivial_transaction(const char *vconn_name, enum ofpraw ofpraw) { struct ofpbuf *request; struct vconn *vconn; open_vconn(vconn_name, &vconn); request = ofpraw_alloc(ofpraw, vconn_get_version(vconn), 0); dump_transaction(vconn, request); vconn_close(vconn); } /* Sends all of the 'requests', which should be requests that only have replies * if an error occurs, and waits for them to succeed or fail. If an error does * occur, prints it and exits with an error. * * Destroys all of the 'requests'. */ static void transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests) { struct ofpbuf *reply; run(vconn_transact_multiple_noreply(vconn, requests, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { ofp_print(stderr, reply->data, reply->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 2); exit(1); } ofpbuf_delete(reply); } /* Frees the error messages as they are printed. */ static void bundle_print_errors(struct ovs_list *errors, struct ovs_list *requests, const char *vconn_name) { struct ofpbuf *error; struct ofpbuf *bmsg; INIT_CONTAINER(bmsg, requests, list_node); LIST_FOR_EACH_SAFE (error, list_node, errors) { const struct ofp_header *error_oh = error->data; ovs_be32 error_xid = error_oh->xid; enum ofperr ofperr; struct ofpbuf payload; ofperr = ofperr_decode_msg(error_oh, &payload); if (!ofperr) { fprintf(stderr, "***decode error***"); } else { /* Default to the likely truncated message. */ const struct ofp_header *ofp_msg = payload.data; size_t msg_len = payload.size; /* Find the failing message from the requests list to be able to * dump the whole message. We assume the errors are returned in * the same order as in which the messages are sent to get O(n) * rather than O(n^2) processing here. If this heuristics fails we * may print the truncated hexdumps instead. */ LIST_FOR_EACH_CONTINUE (bmsg, list_node, requests) { const struct ofp_header *oh = bmsg->data; if (oh->xid == error_xid) { ofp_msg = oh; msg_len = bmsg->size; break; } } fprintf(stderr, "Error %s for: ", ofperr_get_name(ofperr)); ofp_print(stderr, ofp_msg, msg_len, ports_to_show(vconn_name), tables_to_show(vconn_name), verbosity + 1); } ofpbuf_uninit(&payload); ofpbuf_delete(error); } fflush(stderr); } static void bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags) { struct ovs_list errors; int retval = vconn_bundle_transact(vconn, requests, flags, &errors); bundle_print_errors(&errors, requests, vconn_get_name(vconn)); if (retval) { ovs_fatal(retval, "talking to %s", vconn_get_name(vconn)); } } /* Sends 'request', which should be a request that only has a reply if an error * occurs, and waits for it to succeed or fail. If an error does occur, prints * it and exits with an error. * * Destroys 'request'. */ static void transact_noreply(struct vconn *vconn, struct ofpbuf *request) { struct ovs_list requests; ovs_list_init(&requests); ovs_list_push_back(&requests, &request->list_node); transact_multiple_noreply(vconn, &requests); } static void fetch_switch_config(struct vconn *vconn, struct ofputil_switch_config *config) { struct ofpbuf *request; struct ofpbuf *reply; enum ofptype type; request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, vconn_get_version(vconn), 0); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); if (ofptype_decode(&type, reply->data) || type != OFPTYPE_GET_CONFIG_REPLY) { ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn)); } ofputil_decode_get_config_reply(reply->data, config); ofpbuf_delete(reply); } static void set_switch_config(struct vconn *vconn, const struct ofputil_switch_config *config) { enum ofp_version version = vconn_get_version(vconn); transact_noreply(vconn, ofputil_encode_set_config(config, version)); } static void ofctl_show(struct ovs_cmdl_context *ctx) { const char *vconn_name = ctx->argv[1]; enum ofp_version version; struct vconn *vconn; struct ofpbuf *request; struct ofpbuf *reply; bool has_ports; open_vconn(vconn_name, &vconn); version = vconn_get_version(vconn); request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); has_ports = ofputil_switch_features_has_ports(reply); ofp_print(stdout, reply->data, reply->size, NULL, NULL, verbosity + 1); ofpbuf_delete(reply); if (!has_ports) { request = ofputil_encode_port_desc_stats_request(version, OFPP_ANY); dump_transaction(vconn, request); } dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST); vconn_close(vconn); } static void ofctl_dump_desc(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_OFPST_DESC_REQUEST); } static void ofctl_dump_tables(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_OFPST_TABLE_REQUEST); } static void ofctl_dump_table_features(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_table_features_request(vconn_get_version(vconn)); /* The following is similar to dump_trivial_transaction(), but it * maintains the previous 'ofputil_table_features' from one stats reply * message to the next, which allows duplication to be eliminated in the * output across messages. Otherwise the output is much larger and harder * to read, because only 17 or so ofputil_table_features elements fit in a * single 64 kB OpenFlow message and therefore you get a ton of repetition * (every 17th element is printed in full instead of abbreviated). */ const struct ofp_header *request_oh = request->data; ovs_be32 send_xid = request_oh->xid; bool done = false; struct ofputil_table_features prev; int first_ditto = -1, last_ditto = -1; struct ds s = DS_EMPTY_INITIALIZER; int n = 0; send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { enum ofptype type; enum ofperr error; error = ofptype_decode(&type, reply->data); if (error) { ovs_fatal(0, "decode error: %s", ofperr_get_name(error)); } else if (type == OFPTYPE_ERROR) { ofp_print(stdout, reply->data, reply->size, NULL, NULL, verbosity + 1); done = true; } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) { done = !ofpmp_more(reply->data); for (;;) { struct ofputil_table_features tf; struct ofpbuf raw_properties; int retval = ofputil_decode_table_features( reply, &tf, &raw_properties); if (retval) { if (retval != EOF) { ovs_fatal(0, "decode error: %s", ofperr_get_name(retval)); } break; } ofputil_table_features_format( &s, &tf, n ? &prev : NULL, NULL, NULL, &first_ditto, &last_ditto); prev = tf; n++; } } else { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, ports_to_show(ctx->argv[1]), tables_to_show(ctx->argv[1]), verbosity + 1)); } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } ofputil_table_features_format_finish(&s, first_ditto, last_ditto); const char *p = ds_cstr(&s); puts(p + (*p == '\n')); ds_destroy(&s); vconn_close(vconn); } static void ofctl_dump_table_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); if (request) { dump_transaction(vconn, request); } vconn_close(vconn); } static bool str_to_ofp(const char *s, ofp_port_t *ofp_port) { bool ret; uint32_t port_; ret = str_to_uint(s, 10, &port_); *ofp_port = u16_to_ofp(port_); return ret; } struct port_iterator { struct vconn *vconn; enum { PI_FEATURES, PI_PORT_DESC } variant; struct ofpbuf *reply; ovs_be32 send_xid; bool more; }; static void port_iterator_fetch_port_desc(struct port_iterator *pi) { pi->variant = PI_PORT_DESC; pi->more = true; struct ofpbuf *rq = ofputil_encode_port_desc_stats_request( vconn_get_version(pi->vconn), OFPP_ANY); pi->send_xid = ((struct ofp_header *) rq->data)->xid; send_openflow_buffer(pi->vconn, rq); } static void port_iterator_fetch_features(struct port_iterator *pi) { pi->variant = PI_FEATURES; /* Fetch the switch's ofp_switch_features. */ enum ofp_version version = vconn_get_version(pi->vconn); struct ofpbuf *rq = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0); run(vconn_transact(pi->vconn, rq, &pi->reply), "talking to %s", vconn_get_name(pi->vconn)); enum ofptype type; if (ofptype_decode(&type, pi->reply->data) || type != OFPTYPE_FEATURES_REPLY) { ovs_fatal(0, "%s: received bad features reply", vconn_get_name(pi->vconn)); } if (!ofputil_switch_features_has_ports(pi->reply)) { /* The switch features reply does not contain a complete list of ports. * Probably, there are more ports than will fit into a single 64 kB * OpenFlow message. Use OFPST_PORT_DESC to get a complete list of * ports. */ ofpbuf_delete(pi->reply); pi->reply = NULL; port_iterator_fetch_port_desc(pi); return; } struct ofputil_switch_features features; enum ofperr error = ofputil_pull_switch_features(pi->reply, &features); if (error) { ovs_fatal(0, "%s: failed to decode features reply (%s)", vconn_get_name(pi->vconn), ofperr_to_string(error)); } } /* Initializes 'pi' to prepare for iterating through all of the ports on the * OpenFlow switch to which 'vconn' is connected. * * During iteration, the client should not make other use of 'vconn', because * that can cause other messages to be interleaved with the replies used by the * iterator and thus some ports may be missed or a hang can occur. */ static void port_iterator_init(struct port_iterator *pi, struct vconn *vconn) { memset(pi, 0, sizeof *pi); pi->vconn = vconn; if (vconn_get_version(vconn) < OFP13_VERSION) { port_iterator_fetch_features(pi); } else { port_iterator_fetch_port_desc(pi); } } /* Obtains the next port from 'pi'. On success, initializes '*pp' with the * port's details and returns true, otherwise (if all the ports have already * been seen), returns false. */ static bool port_iterator_next(struct port_iterator *pi, struct ofputil_phy_port *pp) { for (;;) { if (pi->reply) { int retval = ofputil_pull_phy_port(vconn_get_version(pi->vconn), pi->reply, pp); if (!retval) { return true; } else if (retval != EOF) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(pi->reply->data, pi->reply->size, NULL, NULL, verbosity + 1)); } } if (pi->variant == PI_FEATURES || !pi->more) { return false; } ovs_be32 recv_xid; do { ofpbuf_delete(pi->reply); run(vconn_recv_block(pi->vconn, &pi->reply), "OpenFlow receive failed"); recv_xid = ((struct ofp_header *) pi->reply->data)->xid; } while (pi->send_xid != recv_xid); struct ofp_header *oh = pi->reply->data; enum ofptype type; if (ofptype_pull(&type, pi->reply) || type != OFPTYPE_PORT_DESC_STATS_REPLY) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(pi->reply->data, pi->reply->size, NULL, NULL, verbosity + 1)); } pi->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0; } } /* Destroys iterator 'pi'. */ static void port_iterator_destroy(struct port_iterator *pi) { if (pi) { while (pi->variant == PI_PORT_DESC && pi->more) { /* Drain vconn's queue of any other replies for this request. */ struct ofputil_phy_port pp; port_iterator_next(pi, &pp); } ofpbuf_delete(pi->reply); } } /* Opens a connection to 'vconn_name', fetches the port structure for * 'port_name' (which may be a port name or number), and copies it into * '*pp'. */ static void fetch_ofputil_phy_port(const char *vconn_name, const char *port_name, struct ofputil_phy_port *pp) { struct vconn *vconn; ofp_port_t port_no; bool found = false; /* Try to interpret the argument as a port number. */ if (!str_to_ofp(port_name, &port_no)) { port_no = OFPP_NONE; } /* OpenFlow 1.0, 1.1, and 1.2 put the list of ports in the * OFPT_FEATURES_REPLY message. OpenFlow 1.3 and later versions put it * into the OFPST_PORT_DESC reply. Try it the correct way. */ open_vconn(vconn_name, &vconn); struct port_iterator pi; for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, pp); ) { if (port_no != OFPP_NONE ? port_no == pp->port_no : !strcmp(pp->name, port_name)) { found = true; break; } } port_iterator_destroy(&pi); vconn_close(vconn); if (!found) { ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name); } } static const struct ofputil_port_map * get_port_map(const char *vconn_name) { static struct shash port_maps = SHASH_INITIALIZER(&port_maps); struct ofputil_port_map *map = shash_find_data(&port_maps, vconn_name); if (!map) { map = xmalloc(sizeof *map); ofputil_port_map_init(map); shash_add(&port_maps, vconn_name, map); if (!strchr(vconn_name, ':') || !vconn_verify_name(vconn_name)) { /* For an active vconn (which includes a vconn constructed from a * bridge name), connect to it and pull down the port name-number * mapping. */ struct vconn *vconn; open_vconn(vconn_name, &vconn); struct port_iterator pi; struct ofputil_phy_port pp; for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, &pp); ) { ofputil_port_map_put(map, pp.port_no, pp.name); } port_iterator_destroy(&pi); vconn_close(vconn); } else { /* Don't bother with passive vconns, since it could take a long * time for the remote to try to connect to us. Don't bother with * invalid vconn names either. */ } } return map; } static const struct ofputil_port_map * ports_to_accept(const char *vconn_name) { return should_accept_names() ? get_port_map(vconn_name) : NULL; } static const struct ofputil_port_map * ports_to_show(const char *vconn_name) { return should_show_names() ? get_port_map(vconn_name) : NULL; } struct table_iterator { struct vconn *vconn; enum { TI_STATS, TI_FEATURES } variant; struct ofpbuf *reply; ovs_be32 send_xid; bool more; struct ofputil_table_features features; struct ofpbuf raw_properties; }; /* Initializes 'ti' to prepare for iterating through all of the tables on the * OpenFlow switch to which 'vconn' is connected. * * During iteration, the client should not make other use of 'vconn', because * that can cause other messages to be interleaved with the replies used by the * iterator and thus some tables may be missed or a hang can occur. */ static void table_iterator_init(struct table_iterator *ti, struct vconn *vconn) { memset(ti, 0, sizeof *ti); ti->vconn = vconn; ti->variant = (vconn_get_version(vconn) < OFP13_VERSION ? TI_STATS : TI_FEATURES); ti->more = true; enum ofpraw ofpraw = (ti->variant == TI_STATS ? OFPRAW_OFPST_TABLE_REQUEST : OFPRAW_OFPST13_TABLE_FEATURES_REQUEST); struct ofpbuf *rq = ofpraw_alloc(ofpraw, vconn_get_version(vconn), 0); ti->send_xid = ((struct ofp_header *) rq->data)->xid; send_openflow_buffer(ti->vconn, rq); } /* Obtains the next table from 'ti'. On success, returns the next table's * features; on failure, returns NULL. */ static const struct ofputil_table_features * table_iterator_next(struct table_iterator *ti) { for (;;) { if (ti->reply) { int retval; if (ti->variant == TI_STATS) { struct ofputil_table_stats ts; retval = ofputil_decode_table_stats_reply(ti->reply, &ts, &ti->features); } else { ovs_assert(ti->variant == TI_FEATURES); retval = ofputil_decode_table_features(ti->reply, &ti->features, &ti->raw_properties); } if (!retval) { return &ti->features; } else if (retval != EOF) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(ti->reply->data, ti->reply->size, NULL, NULL, verbosity + 1)); } } if (!ti->more) { return NULL; } ovs_be32 recv_xid; do { ofpbuf_delete(ti->reply); run(vconn_recv_block(ti->vconn, &ti->reply), "OpenFlow receive failed"); recv_xid = ((struct ofp_header *) ti->reply->data)->xid; } while (ti->send_xid != recv_xid); struct ofp_header *oh = ti->reply->data; enum ofptype type; if (ofptype_pull(&type, ti->reply) || type != (ti->variant == TI_STATS ? OFPTYPE_TABLE_STATS_REPLY : OFPTYPE_TABLE_FEATURES_STATS_REPLY)) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(ti->reply->data, ti->reply->size, NULL, NULL, verbosity + 1)); } ti->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0; } } /* Destroys iterator 'ti'. */ static void table_iterator_destroy(struct table_iterator *ti) { if (ti) { while (ti->more) { /* Drain vconn's queue of any other replies for this request. */ table_iterator_next(ti); } ofpbuf_delete(ti->reply); } } static const struct ofputil_table_map * get_table_map(const char *vconn_name) { static struct shash table_maps = SHASH_INITIALIZER(&table_maps); struct ofputil_table_map *map = shash_find_data(&table_maps, vconn_name); if (!map) { map = xmalloc(sizeof *map); ofputil_table_map_init(map); shash_add(&table_maps, vconn_name, map); if (!strchr(vconn_name, ':') || !vconn_verify_name(vconn_name)) { /* For an active vconn (which includes a vconn constructed from a * bridge name), connect to it and pull down the port name-number * mapping. */ struct vconn *vconn; open_vconn(vconn_name, &vconn); struct table_iterator ti; table_iterator_init(&ti, vconn); for (;;) { const struct ofputil_table_features *tf = table_iterator_next(&ti); if (!tf) { break; } if (tf->name[0]) { ofputil_table_map_put(map, tf->table_id, tf->name); } } table_iterator_destroy(&ti); vconn_close(vconn); } else { /* Don't bother with passive vconns, since it could take a long * time for the remote to try to connect to us. Don't bother with * invalid vconn names either. */ } } return map; } static const struct ofputil_table_map * tables_to_accept(const char *vconn_name) { return should_accept_names() ? get_table_map(vconn_name) : NULL; } static const struct ofputil_table_map * tables_to_show(const char *vconn_name) { return should_show_names() ? get_table_map(vconn_name) : NULL; } /* We accept port and table names unless the feature is turned off * explicitly. */ static bool should_accept_names(void) { return use_names != 0; } /* We show port and table names only if the feature is turned on explicitly, or * if we're interacting with a user on the console. */ static bool should_show_names(void) { static int interactive = -1; if (interactive == -1) { interactive = isatty(STDOUT_FILENO); } return use_names > 0 || (use_names == -1 && interactive); } /* Returns the port number corresponding to 'port_name' (which may be a port * name or number) within the switch 'vconn_name'. */ static ofp_port_t str_to_port_no(const char *vconn_name, const char *port_name) { ofp_port_t port_no; if (ofputil_port_from_string(port_name, NULL, &port_no) || ofputil_port_from_string(port_name, ports_to_accept(vconn_name), &port_no)) { return port_no; } ovs_fatal(0, "%s: unknown port `%s'", vconn_name, port_name); } static bool try_set_protocol(struct vconn *vconn, enum ofputil_protocol want, enum ofputil_protocol *cur) { for (;;) { struct ofpbuf *request, *reply; enum ofputil_protocol next; request = ofputil_encode_set_protocol(*cur, want, &next); if (!request) { return *cur == want; } run(vconn_transact_noreply(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { char *s = ofp_to_string(reply->data, reply->size, NULL, NULL, 2); VLOG_DBG("%s: failed to set protocol, switch replied: %s", vconn_get_name(vconn), s); free(s); ofpbuf_delete(reply); return false; } *cur = next; } } static enum ofputil_protocol set_protocol_for_flow_dump(struct vconn *vconn, enum ofputil_protocol cur_protocol, enum ofputil_protocol usable_protocols) { char *usable_s; int i; for (i = 0; i < ofputil_n_flow_dump_protocols; i++) { enum ofputil_protocol f = ofputil_flow_dump_protocols[i]; if (f & usable_protocols & allowed_protocols && try_set_protocol(vconn, f, &cur_protocol)) { return f; } } usable_s = ofputil_protocols_to_string(usable_protocols); if (usable_protocols & allowed_protocols) { ovs_fatal(0, "switch does not support any of the usable flow " "formats (%s)", usable_s); } else { char *allowed_s = ofputil_protocols_to_string(allowed_protocols); ovs_fatal(0, "none of the usable flow formats (%s) is among the " "allowed flow formats (%s)", usable_s, allowed_s); } } static struct vconn * prepare_dump_flows(int argc, char *argv[], bool aggregate, struct ofputil_flow_stats_request *fsr, enum ofputil_protocol *protocolp) { const char *vconn_name = argv[1]; enum ofputil_protocol usable_protocols, protocol; struct vconn *vconn; char *error; const char *match = argc > 2 ? argv[2] : ""; const struct ofputil_port_map *port_map = *match ? ports_to_accept(vconn_name) : NULL; const struct ofputil_table_map *table_map = *match ? tables_to_accept(vconn_name) : NULL; error = parse_ofp_flow_stats_request_str(fsr, aggregate, match, port_map, table_map, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } protocol = open_vconn(vconn_name, &vconn); *protocolp = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); return vconn; } static void ofctl_dump_flows__(int argc, char *argv[], bool aggregate) { struct ofputil_flow_stats_request fsr; enum ofputil_protocol protocol; struct vconn *vconn; vconn = prepare_dump_flows(argc, argv, aggregate, &fsr, &protocol); dump_transaction(vconn, ofputil_encode_flow_stats_request(&fsr, protocol)); vconn_close(vconn); } static void get_match_field(const struct mf_field *field, const struct match *match, union mf_value *value) { if (!match->tun_md.valid || (field->id < MFF_TUN_METADATA0 || field->id >= MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS)) { mf_get_value(field, &match->flow, value); } else { const struct tun_metadata_loc *loc = &match->tun_md.entry[field->id - MFF_TUN_METADATA0].loc; /* Since we don't have a tunnel mapping table, extract the value * from the locally allocated location in the match. */ memset(value, 0, field->n_bytes - loc->len); memcpy(value->tun_metadata + field->n_bytes - loc->len, match->flow.tunnel.metadata.opts.u8 + loc->c.offset, loc->len); } } static int compare_flows(const void *afs_, const void *bfs_) { const struct ofputil_flow_stats *afs = afs_; const struct ofputil_flow_stats *bfs = bfs_; const struct match *a = &afs->match; const struct match *b = &bfs->match; const struct sort_criterion *sc; for (sc = criteria; sc < &criteria[n_criteria]; sc++) { const struct mf_field *f = sc->field; int ret; if (!f) { int a_pri = afs->priority; int b_pri = bfs->priority; ret = a_pri < b_pri ? -1 : a_pri > b_pri; } else { bool ina, inb; ina = mf_are_prereqs_ok(f, &a->flow, NULL) && !mf_is_all_wild(f, &a->wc); inb = mf_are_prereqs_ok(f, &b->flow, NULL) && !mf_is_all_wild(f, &b->wc); if (ina != inb) { /* Skip the test for sc->order, so that missing fields always * sort to the end whether we're sorting in ascending or * descending order. */ return ina ? -1 : 1; } else { union mf_value aval, bval; get_match_field(f, a, &aval); get_match_field(f, b, &bval); ret = memcmp(&aval, &bval, f->n_bytes); } } if (ret) { return sc->order == SORT_ASC ? ret : -ret; } } return a < b ? -1 : 1; } static void ofctl_dump_flows(struct ovs_cmdl_context *ctx) { if (!n_criteria && !should_show_names() && show_stats) { ofctl_dump_flows__(ctx->argc, ctx->argv, false); return; } else { struct ofputil_flow_stats_request fsr; enum ofputil_protocol protocol; struct vconn *vconn; vconn = prepare_dump_flows(ctx->argc, ctx->argv, false, &fsr, &protocol); struct ofputil_flow_stats *fses; size_t n_fses; run(vconn_dump_flows(vconn, &fsr, protocol, &fses, &n_fses), "dump flows"); if (n_criteria) { qsort(fses, n_fses, sizeof *fses, compare_flows); } struct ds s = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < n_fses; i++) { ds_clear(&s); ofputil_flow_stats_format(&s, &fses[i], ports_to_show(ctx->argv[1]), tables_to_show(ctx->argv[1]), show_stats); printf(" %s\n", ds_cstr(&s)); } ds_destroy(&s); for (size_t i = 0; i < n_fses; i++) { free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); } free(fses); vconn_close(vconn); } } static void ofctl_dump_aggregate(struct ovs_cmdl_context *ctx) { ofctl_dump_flows__(ctx->argc, ctx->argv, true); } static void ofctl_queue_stats(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; struct ofputil_queue_stats_request oqs; open_vconn(ctx->argv[1], &vconn); if (ctx->argc > 2 && ctx->argv[2][0] && strcasecmp(ctx->argv[2], "all")) { oqs.port_no = str_to_port_no(ctx->argv[1], ctx->argv[2]); } else { oqs.port_no = OFPP_ANY; } if (ctx->argc > 3 && ctx->argv[3][0] && strcasecmp(ctx->argv[3], "all")) { oqs.queue_id = atoi(ctx->argv[3]); } else { oqs.queue_id = OFPQ_ALL; } request = ofputil_encode_queue_stats_request(vconn_get_version(vconn), &oqs); dump_transaction(vconn, request); vconn_close(vconn); } static void ofctl_queue_get_config(struct ovs_cmdl_context *ctx) { const char *vconn_name = ctx->argv[1]; const char *port_name = ctx->argc > 2 ? ctx->argv[2] : "any"; ofp_port_t port = str_to_port_no(vconn_name, port_name); const char *queue_name = ctx->argc > 3 ? ctx->argv[3] : "all"; uint32_t queue = (!strcasecmp(queue_name, "all") ? OFPQ_ALL : atoi(queue_name)); struct vconn *vconn; enum ofputil_protocol protocol = open_vconn(vconn_name, &vconn); enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); if (port == OFPP_ANY && version == OFP10_VERSION) { /* The user requested all queues on all ports. OpenFlow 1.0 only * supports getting queues for an individual port, so to implement the * user's request we have to get a list of all the ports. * * We use a second vconn to avoid having to accumulate a list of all of * the ports. */ struct vconn *vconn2; enum ofputil_protocol protocol2 = open_vconn(vconn_name, &vconn2); enum ofp_version version2 = ofputil_protocol_to_ofp_version(protocol2); struct port_iterator pi; struct ofputil_phy_port pp; for (port_iterator_init(&pi, vconn); port_iterator_next(&pi, &pp); ) { if (ofp_to_u16(pp.port_no) < ofp_to_u16(OFPP_MAX)) { dump_transaction(vconn2, ofputil_encode_queue_get_config_request( version2, pp.port_no, queue)); } } port_iterator_destroy(&pi); vconn_close(vconn2); } else { dump_transaction(vconn, ofputil_encode_queue_get_config_request( version, port, queue)); } vconn_close(vconn); } static enum ofputil_protocol open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp, enum ofputil_protocol usable_protocols) { enum ofputil_protocol cur_protocol; char *usable_s; int i; if (!(usable_protocols & allowed_protocols)) { char *allowed_s = ofputil_protocols_to_string(allowed_protocols); usable_s = ofputil_protocols_to_string(usable_protocols); ovs_fatal(0, "none of the usable flow formats (%s) is among the " "allowed flow formats (%s)", usable_s, allowed_s); } /* If the initial flow format is allowed and usable, keep it. */ cur_protocol = open_vconn(remote, vconnp); if (usable_protocols & allowed_protocols & cur_protocol) { return cur_protocol; } /* Otherwise try each flow format in turn. */ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { enum ofputil_protocol f = 1 << i; if (f != cur_protocol && f & usable_protocols & allowed_protocols && try_set_protocol(*vconnp, f, &cur_protocol)) { return f; } } usable_s = ofputil_protocols_to_string(usable_protocols); ovs_fatal(0, "switch does not support any of the usable flow " "formats (%s)", usable_s); } static void bundle_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct vconn *vconn; struct ovs_list requests; size_t i; ovs_list_init(&requests); /* Bundles need OpenFlow 1.3+. */ usable_protocols &= OFPUTIL_P_OF13_UP; protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *request = ofputil_encode_flow_mod(fm, protocol); ovs_list_push_back(&requests, &request->list_node); free(CONST_CAST(struct ofpact *, fm->ofpacts)); minimatch_destroy(&fm->match); } bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); ofpbuf_list_delete(&requests); vconn_close(vconn); } static void ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct vconn *vconn; struct ds ds = DS_EMPTY_INITIALIZER; size_t i; if (bundle) { bundle_flow_mod__(remote, fms, n_fms, usable_protocols); return; } protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *buf = ofputil_encode_flow_mod(fm, protocol); /* If user has opted for verbosity of 5 or more dump the * constructed OpenFlow packet in hex format */ if (verbosity == 5) { ds_put_hex_dump(&ds, buf->data, buf->size, 0, true); } else if (verbosity > 5) { ds_put_hex(&ds, buf->data, buf->size); ds_put_char(&ds, '\n'); } transact_noreply(vconn, buf); free(CONST_CAST(struct ofpact *, fm->ofpacts)); minimatch_destroy(&fm->match); } fputs(ds_cstr(&ds), stdout); ds_destroy(&ds); vconn_close(vconn); } static void ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod *fms = NULL; size_t n_fms = 0; char *error; if (command == OFPFC_ADD) { /* Allow the file to specify a mix of commands. If none specified at * the beginning of any given line, then the default is OFPFC_ADD, so * this is backwards compatible. */ command = -2; } error = parse_ofp_flow_mod_file(argv[2], ports_to_accept(argv[1]), tables_to_accept(argv[1]), command, &fms, &n_fms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols); free(fms); } static void ofctl_flow_mod(int argc, char *argv[], uint16_t command) { if (argc > 2 && !strcmp(argv[2], "-")) { ofctl_flow_mod_file(argc, argv, command); } else { struct ofputil_flow_mod fm; char *error; enum ofputil_protocol usable_protocols; error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", ports_to_accept(argv[1]), tables_to_accept(argv[1]), command, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols); } } static void ofctl_add_flow(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, OFPFC_ADD); } static void ofctl_add_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod_file(ctx->argc, ctx->argv, OFPFC_ADD); } static void ofctl_mod_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY); } static void ofctl_del_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE); } static bool set_packet_in_format(struct vconn *vconn, enum ofputil_packet_in_format packet_in_format, bool must_succeed) { struct ofpbuf *spif; spif = ofputil_encode_set_packet_in_format(vconn_get_version(vconn), packet_in_format); if (must_succeed) { transact_noreply(vconn, spif); } else { struct ofpbuf *reply; run(vconn_transact_noreply(vconn, spif, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { char *s = ofp_to_string(reply->data, reply->size, NULL, NULL, 2); VLOG_DBG("%s: failed to set packet in format to nx_packet_in, " "controller replied: %s.", vconn_get_name(vconn), s); free(s); ofpbuf_delete(reply); return false; } else { VLOG_DBG("%s: using user-specified packet in format %s", vconn_get_name(vconn), ofputil_packet_in_format_to_string(packet_in_format)); } } return true; } static int monitor_set_invalid_ttl_to_controller(struct vconn *vconn) { struct ofputil_switch_config config; fetch_switch_config(vconn, &config); if (!config.invalid_ttl_to_controller) { config.invalid_ttl_to_controller = 1; set_switch_config(vconn, &config); /* Then retrieve the configuration to see if it really took. OpenFlow * has ill-defined error reporting for bad flags, so this is about the * best we can do. */ fetch_switch_config(vconn, &config); if (!config.invalid_ttl_to_controller) { ovs_fatal(0, "setting invalid_ttl_to_controller failed (this " "switch probably doesn't support this flag)"); } } return 0; } /* Converts hex digits in 'hex' to an OpenFlow message in '*msgp'. The * caller must free '*msgp'. On success, returns NULL. On failure, returns * an error message and stores NULL in '*msgp'. */ static const char * openflow_from_hex(const char *hex, struct ofpbuf **msgp) { struct ofp_header *oh; struct ofpbuf *msg; msg = ofpbuf_new(strlen(hex) / 2); *msgp = NULL; if (ofpbuf_put_hex(msg, hex, NULL)[0] != '\0') { ofpbuf_delete(msg); return "Trailing garbage in hex data"; } if (msg->size < sizeof(struct ofp_header)) { ofpbuf_delete(msg); return "Message too short for OpenFlow"; } oh = msg->data; if (msg->size != ntohs(oh->length)) { ofpbuf_delete(msg); return "Message size does not match length in OpenFlow header"; } *msgp = msg; return NULL; } static void ofctl_send(struct unixctl_conn *conn, int argc, const char *argv[], void *vconn_) { struct vconn *vconn = vconn_; struct ds reply; bool ok; int i; ok = true; ds_init(&reply); for (i = 1; i < argc; i++) { const char *error_msg; struct ofpbuf *msg; int error; error_msg = openflow_from_hex(argv[i], &msg); if (error_msg) { ds_put_format(&reply, "%s\n", error_msg); ok = false; continue; } fprintf(stderr, "send: "); ofp_print(stderr, msg->data, msg->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity); error = vconn_send_block(vconn, msg); if (error) { ofpbuf_delete(msg); ds_put_format(&reply, "%s\n", ovs_strerror(error)); ok = false; } else { ds_put_cstr(&reply, "sent\n"); } } if (ok) { unixctl_command_reply(conn, ds_cstr(&reply)); } else { unixctl_command_reply_error(conn, ds_cstr(&reply)); } ds_destroy(&reply); } static void unixctl_packet_out(struct unixctl_conn *conn, int OVS_UNUSED argc, const char *argv[], void *vconn_) { struct vconn *vconn = vconn_; enum ofputil_protocol protocol = ofputil_protocol_from_ofp_version(vconn_get_version(vconn)); struct ds reply = DS_EMPTY_INITIALIZER; bool ok = true; enum ofputil_protocol usable_protocols; struct ofputil_packet_out po; char *error_msg; error_msg = parse_ofp_packet_out_str( &po, argv[1], ports_to_accept(vconn_get_name(vconn)), tables_to_accept(vconn_get_name(vconn)), &usable_protocols); if (error_msg) { ds_put_format(&reply, "%s\n", error_msg); free(error_msg); ok = false; } if (ok && !(usable_protocols & protocol)) { ds_put_format(&reply, "PACKET_OUT actions are incompatible with the OpenFlow connection.\n"); ok = false; } if (ok) { struct ofpbuf *msg = ofputil_encode_packet_out(&po, protocol); ofp_print(stderr, msg->data, msg->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity); int error = vconn_send_block(vconn, msg); if (error) { ofpbuf_delete(msg); ds_put_format(&reply, "%s\n", ovs_strerror(error)); ok = false; } } if (ok) { unixctl_command_reply(conn, ds_cstr(&reply)); } else { unixctl_command_reply_error(conn, ds_cstr(&reply)); } ds_destroy(&reply); if (!error_msg) { free(CONST_CAST(void *, po.packet)); free(po.ofpacts); } } struct barrier_aux { struct vconn *vconn; /* OpenFlow connection for sending barrier. */ struct unixctl_conn *conn; /* Connection waiting for barrier response. */ }; static void ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux_) { struct barrier_aux *aux = aux_; struct ofpbuf *msg; int error; if (aux->conn) { unixctl_command_reply_error(conn, "already waiting for barrier reply"); return; } msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn)); error = vconn_send_block(aux->vconn, msg); if (error) { ofpbuf_delete(msg); unixctl_command_reply_error(conn, ovs_strerror(error)); } else { aux->conn = conn; } } static void ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { int fd; fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666); if (fd < 0) { unixctl_command_reply_error(conn, ovs_strerror(errno)); return; } fflush(stderr); dup2(fd, STDERR_FILENO); close(fd); unixctl_command_reply(conn, NULL); } static void ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (!*blocked) { *blocked = true; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already blocking"); } } static void ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (*blocked) { *blocked = false; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already unblocked"); } } /* Prints to stderr all of the messages received on 'vconn'. * * Iff 'reply_to_echo_requests' is true, sends a reply to any echo request * received on 'vconn'. * * If 'resume_continuations' is true, sends an NXT_RESUME in reply to any * NXT_PACKET_IN2 that includes a continuation. */ static void monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests, bool resume_continuations) { struct barrier_aux barrier_aux = { vconn, NULL }; struct unixctl_server *server; bool exiting = false; bool blocked = false; int error; daemon_save_fd(STDERR_FILENO); daemonize_start(false, false); error = unixctl_server_create(unixctl_path, &server); if (error) { ovs_fatal(error, "failed to create unixctl server"); } unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting); unixctl_command_register("ofctl/send", "OFMSG...", 1, INT_MAX, ofctl_send, vconn); unixctl_command_register("ofctl/packet-out", "\"in_port= packet= actions=...\"", 1, 1, unixctl_packet_out, vconn); unixctl_command_register("ofctl/barrier", "", 0, 0, ofctl_barrier, &barrier_aux); unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1, ofctl_set_output_file, NULL); unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked); unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock, &blocked); daemonize_complete(); enum ofp_version version = vconn_get_version(vconn); enum ofputil_protocol protocol = ofputil_protocol_from_ofp_version(version); for (;;) { struct ofpbuf *b; int retval; unixctl_server_run(server); while (!blocked) { enum ofptype type; retval = vconn_recv(vconn, &b); if (retval == EAGAIN) { break; } run(retval, "vconn_recv"); if (timestamp) { char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ", time_wall_msec(), true); fputs(s, stderr); free(s); } ofptype_decode(&type, b->data); ofp_print(stderr, b->data, b->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 2); fflush(stderr); switch ((int) type) { case OFPTYPE_BARRIER_REPLY: if (barrier_aux.conn) { unixctl_command_reply(barrier_aux.conn, NULL); barrier_aux.conn = NULL; } break; case OFPTYPE_ECHO_REQUEST: if (reply_to_echo_requests) { struct ofpbuf *reply; reply = ofputil_encode_echo_reply(b->data); retval = vconn_send_block(vconn, reply); if (retval) { ovs_fatal(retval, "failed to send echo reply"); } } break; case OFPTYPE_PACKET_IN: if (resume_continuations) { struct ofputil_packet_in pin; struct ofpbuf continuation; error = ofputil_decode_packet_in(b->data, true, NULL, NULL, &pin, NULL, NULL, &continuation); if (error) { fprintf(stderr, "decoding packet-in failed: %s", ofperr_to_string(error)); } else if (continuation.size) { struct ofpbuf *reply; reply = ofputil_encode_resume(&pin, &continuation, protocol); fprintf(stderr, "send: "); ofp_print(stderr, reply->data, reply->size, ports_to_show(vconn_get_name(vconn)), tables_to_show(vconn_get_name(vconn)), verbosity + 2); fflush(stderr); retval = vconn_send_block(vconn, reply); if (retval) { ovs_fatal(retval, "failed to send NXT_RESUME"); } } } break; } ofpbuf_delete(b); } if (exiting) { break; } vconn_run(vconn); vconn_run_wait(vconn); if (!blocked) { vconn_recv_wait(vconn); } unixctl_server_wait(server); poll_block(); } vconn_close(vconn); unixctl_server_destroy(server); } static void ofctl_monitor(struct ovs_cmdl_context *ctx) { struct vconn *vconn; int i; enum ofputil_protocol protocol; enum ofputil_protocol usable_protocols; /* If the user wants the invalid_ttl_to_controller feature, limit the * OpenFlow versions to those that support that feature. (Support in * OpenFlow 1.0 is an Open vSwitch extension.) */ for (i = 2; i < ctx->argc; i++) { if (!strcmp(ctx->argv[i], "invalid_ttl")) { uint32_t usable_versions = ((1u << OFP10_VERSION) | (1u << OFP11_VERSION) | (1u << OFP12_VERSION)); uint32_t allowed_versions = get_allowed_ofp_versions(); if (!(allowed_versions & usable_versions)) { struct ds versions = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&versions, usable_versions); ovs_fatal(0, "invalid_ttl requires one of the OpenFlow " "versions %s but none is enabled (use -O)", ds_cstr(&versions)); } mask_allowed_ofp_versions(usable_versions); break; } } protocol = open_vconn(ctx->argv[1], &vconn); bool resume_continuations = false; for (i = 2; i < ctx->argc; i++) { const char *arg = ctx->argv[i]; if (isdigit((unsigned char) *arg)) { struct ofputil_switch_config config; fetch_switch_config(vconn, &config); config.miss_send_len = atoi(arg); set_switch_config(vconn, &config); } else if (!strcmp(arg, "invalid_ttl")) { monitor_set_invalid_ttl_to_controller(vconn); } else if (!strncmp(arg, "watch:", 6)) { struct ofputil_flow_monitor_request fmr; struct ofpbuf *msg; char *error; error = parse_flow_monitor_request(&fmr, arg + 6, ports_to_accept(ctx->argv[1]), tables_to_accept(ctx->argv[1]), &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } if (!(usable_protocols & allowed_protocols)) { char *allowed_s = ofputil_protocols_to_string(allowed_protocols); char *usable_s = ofputil_protocols_to_string(usable_protocols); ovs_fatal(0, "none of the usable flow formats (%s) is among " "the allowed flow formats (%s)", usable_s, allowed_s); } msg = ofpbuf_new(0); ofputil_append_flow_monitor_request(&fmr, msg, protocol); if (verbosity) { ofpmsg_update_length(msg); ofp_print(stdout, msg->data, msg->size, NULL, NULL, verbosity + 2); } dump_transaction(vconn, msg); fflush(stdout); } else if (!strcmp(arg, "resume")) { /* This option is intentionally undocumented because it is meant * only for testing. */ resume_continuations = true; /* Set miss_send_len to ensure that we get packet-ins. */ struct ofputil_switch_config config; fetch_switch_config(vconn, &config); config.miss_send_len = UINT16_MAX; set_switch_config(vconn, &config); } else { ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg); } } if (preferred_packet_in_format >= 0) { /* A particular packet-in format was requested, so we must set it. */ set_packet_in_format(vconn, preferred_packet_in_format, true); } else { /* Otherwise, we always prefer NXT_PACKET_IN2. */ if (!set_packet_in_format(vconn, OFPUTIL_PACKET_IN_NXT2, false)) { /* We can't get NXT_PACKET_IN2. For OpenFlow 1.0 only, request * NXT_PACKET_IN. (Before 2.6, Open vSwitch will accept a request * for NXT_PACKET_IN with OF1.1+, but even after that it still * sends packet-ins in the OpenFlow native format.) */ if (vconn_get_version(vconn) == OFP10_VERSION) { set_packet_in_format(vconn, OFPUTIL_PACKET_IN_NXT, false); } } } monitor_vconn(vconn, true, resume_continuations); } static void ofctl_snoop(struct ovs_cmdl_context *ctx) { struct vconn *vconn; /* We can't use the snoop vconn to send table features request or port * description request messages to show names, because ovs-vswitchd will * not respond to these messages on snoop vconn. */ use_names = 0; open_vconn__(ctx->argv[1], SNOOP, &vconn); monitor_vconn(vconn, false, false); } static void ofctl_dump_ports(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; ofp_port_t port; open_vconn(ctx->argv[1], &vconn); port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY; request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port); dump_transaction(vconn, request); vconn_close(vconn); } static void ofctl_dump_ports_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; ofp_port_t port; open_vconn(ctx->argv[1], &vconn); port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY; request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn), port); dump_transaction(vconn, request); vconn_close(vconn); } static void ofctl_probe(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; struct ofpbuf *reply; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_echo_request(vconn_get_version(vconn)); run(vconn_transact(vconn, request, &reply), "talking to %s", ctx->argv[1]); if (reply->size != sizeof(struct ofp_header)) { ovs_fatal(0, "reply does not match request"); } ofpbuf_delete(reply); vconn_close(vconn); } static void ofctl_packet_out(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; struct ofputil_packet_out po; struct vconn *vconn; struct ofpbuf *opo; char *error; match_init_catchall(&po.flow_metadata); /* Use the old syntax when more than 4 arguments are given. */ if (ctx->argc > 4) { struct ofpbuf ofpacts; int i; ofpbuf_init(&ofpacts, 64); struct ofpact_parse_params pp = { .port_map = ports_to_accept(ctx->argv[1]), .table_map = tables_to_accept(ctx->argv[1]), .ofpacts = &ofpacts, .usable_protocols = &usable_protocols }; error = ofpacts_parse_actions(ctx->argv[3], &pp); if (error) { ovs_fatal(0, "%s", error); } po.buffer_id = UINT32_MAX; match_set_in_port(&po.flow_metadata, str_to_port_no(ctx->argv[1], ctx->argv[2])); po.ofpacts = ofpacts.data; po.ofpacts_len = ofpacts.size; po.flow_metadata.flow.packet_type = htonl(PT_ETH); protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); for (i = 4; i < ctx->argc; i++) { struct dp_packet *packet; const char *error_msg; error_msg = eth_from_hex(ctx->argv[i], &packet); if (error_msg) { ovs_fatal(0, "%s", error_msg); } po.packet = dp_packet_data(packet); po.packet_len = dp_packet_size(packet); opo = ofputil_encode_packet_out(&po, protocol); transact_noreply(vconn, opo); dp_packet_delete(packet); } vconn_close(vconn); ofpbuf_uninit(&ofpacts); } else if (ctx->argc == 3) { error = parse_ofp_packet_out_str(&po, ctx->argv[2], ports_to_accept(ctx->argv[1]), tables_to_accept(ctx->argv[1]), &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); opo = ofputil_encode_packet_out(&po, protocol); transact_noreply(vconn, opo); vconn_close(vconn); free(CONST_CAST(void *, po.packet)); free(po.ofpacts); } else { ovs_fatal(0, "Too many arguments (%d)", ctx->argc); } } static void ofctl_mod_port(struct ovs_cmdl_context *ctx) { struct ofp_config_flag { const char *name; /* The flag's name. */ enum ofputil_port_config bit; /* Bit to turn on or off. */ bool on; /* Value to set the bit to. */ }; static const struct ofp_config_flag flags[] = { { "up", OFPUTIL_PC_PORT_DOWN, false }, { "down", OFPUTIL_PC_PORT_DOWN, true }, { "stp", OFPUTIL_PC_NO_STP, false }, { "receive", OFPUTIL_PC_NO_RECV, false }, { "receive-stp", OFPUTIL_PC_NO_RECV_STP, false }, { "flood", OFPUTIL_PC_NO_FLOOD, false }, { "forward", OFPUTIL_PC_NO_FWD, false }, { "packet-in", OFPUTIL_PC_NO_PACKET_IN, false }, }; const struct ofp_config_flag *flag; enum ofputil_protocol protocol; struct ofputil_port_mod pm; struct ofputil_phy_port pp; struct vconn *vconn; const char *command; bool not; fetch_ofputil_phy_port(ctx->argv[1], ctx->argv[2], &pp); pm.port_no = pp.port_no; pm.hw_addr = pp.hw_addr; pm.config = 0; pm.mask = 0; pm.advertise = 0; if (!strncasecmp(ctx->argv[3], "no-", 3)) { command = ctx->argv[3] + 3; not = true; } else if (!strncasecmp(ctx->argv[3], "no", 2)) { command = ctx->argv[3] + 2; not = true; } else { command = ctx->argv[3]; not = false; } for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) { if (!strcasecmp(command, flag->name)) { pm.mask = flag->bit; pm.config = flag->on ^ not ? flag->bit : 0; goto found; } } ovs_fatal(0, "unknown mod-port command '%s'", ctx->argv[3]); found: protocol = open_vconn(ctx->argv[1], &vconn); transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol)); vconn_close(vconn); } /* This function uses OFPMP14_TABLE_DESC request to get the current * table configuration from switch. The function then modifies * only that table-config property, which has been requested. */ static void fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm, struct ofputil_table_desc *td) { struct ofpbuf *request; ovs_be32 send_xid; bool done = false; bool found = false; request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { struct ofp_header *oh = reply->data; struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); enum ofptype type; if (ofptype_pull(&type, &b) || type != OFPTYPE_TABLE_DESC_REPLY) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, NULL, NULL, verbosity + 1)); } uint16_t flags = ofpmp_flags(oh); done = !(flags & OFPSF_REPLY_MORE); if (found) { /* We've already found the table desc consisting of current * table configuration, but we need to drain the queue of * any other replies for this request. */ continue; } while (!ofputil_decode_table_desc(&b, td, oh->version)) { if (td->table_id == tm->table_id) { found = true; break; } } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { tm->vacancy = td->vacancy; tm->table_vacancy.vacancy_down = td->table_vacancy.vacancy_down; tm->table_vacancy.vacancy_up = td->table_vacancy.vacancy_up; } else if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { tm->eviction = td->eviction; tm->eviction_flags = td->eviction_flags; } } static void change_table_name(struct vconn *vconn, uint8_t table_id, const char *new_name) { /* Get all tables' features and properties. */ struct table { struct ofputil_table_features tf; struct ofpbuf *raw_properties; } *tables[256]; memset(tables, 0, sizeof tables); struct table_iterator ti; table_iterator_init(&ti, vconn); while (table_iterator_next(&ti)) { struct table *t = tables[ti.features.table_id] = xmalloc(sizeof *t); t->tf = ti.features; t->raw_properties = ofpbuf_clone(&ti.raw_properties); } table_iterator_destroy(&ti); /* Change the name for table 'table_id'. */ struct table *t = tables[table_id]; if (!t) { ovs_fatal(0, "switch does not have table %"PRIu8, table_id); } ovs_strlcpy(t->tf.name, new_name, OFP_MAX_TABLE_NAME_LEN); /* Compose the transaction. */ enum ofp_version version = vconn_get_version(vconn); struct ovs_list requests = OVS_LIST_INITIALIZER(&requests); struct ofpbuf *tfr = ofputil_encode_table_features_request(version); ovs_list_push_back(&requests, &tfr->list_node); if (version >= OFP15_VERSION) { /* For OpenFlow 1.5, we can use a single OFPTFC15_MODIFY without any * properties. */ t->tf.command = OFPTFC15_MODIFY; t->tf.any_properties = false; ofputil_append_table_features(&t->tf, NULL, &requests); } else { /* For OpenFlow 1.3 and 1.4, we have to regurgitate all of the tables * and their properties. */ for (size_t i = 0; i < 256; i++) { if (tables[i]) { ofputil_append_table_features(&tables[i]->tf, tables[i]->raw_properties, &requests); } } } /* Transact. * * The reply repeats the entire new configuration of the tables, so we * don't bother printing it unless there's an error. */ struct ovs_list replies; struct ofpbuf *reply; vconn_transact_multipart(vconn, &requests, &replies); LIST_FOR_EACH (reply, list_node, &replies) { enum ofptype type; enum ofperr error = ofptype_decode(&type, reply->data); if (error) { ovs_fatal(0, "decode error: %s", ofperr_get_name(error)); } else if (type == OFPTYPE_ERROR) { ofp_print(stderr, reply->data, reply->size, NULL, NULL, verbosity + 1); exit(1); } } ofpbuf_list_delete(&replies); /* Clean up. */ for (size_t i = 0; i < ARRAY_SIZE(tables); i++) { if (tables[i]) { ofpbuf_delete(tables[i]->raw_properties); free(tables[i]); } } } static void ofctl_mod_table(struct ovs_cmdl_context *ctx) { uint32_t usable_versions; struct ofputil_table_mod tm; const char *name; struct vconn *vconn; char *error; int i; error = parse_ofp_table_mod(&tm, &name, ctx->argv[2], ctx->argv[3], tables_to_accept(ctx->argv[1]), &usable_versions); if (error) { ovs_fatal(0, "%s", error); } uint32_t allowed_versions = get_allowed_ofp_versions(); if (!(allowed_versions & usable_versions)) { struct ds versions = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&versions, usable_versions); ovs_fatal(0, "table_mod '%s' requires one of the OpenFlow " "versions %s", ctx->argv[3], ds_cstr(&versions)); } mask_allowed_ofp_versions(usable_versions); enum ofputil_protocol protocol = open_vconn(ctx->argv[1], &vconn); if (name) { change_table_name(vconn, tm.table_id, name); } else { /* For OpenFlow 1.4+, ovs-ofctl mod-table should not affect * table-config properties that the user didn't ask to change, so it is * necessary to restore the current configuration of table-config * parameters using OFPMP14_TABLE_DESC request. */ if (allowed_versions & ((1u << OFP14_VERSION) | (1u << OFP15_VERSION))) { struct ofputil_table_desc td; if (tm.table_id == OFPTT_ALL) { for (i = 0; i < OFPTT_MAX; i++) { tm.table_id = i; fetch_table_desc(vconn, &tm, &td); transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } } else { fetch_table_desc(vconn, &tm, &td); transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } } else { transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } } vconn_close(vconn); } static void ofctl_get_frags(struct ovs_cmdl_context *ctx) { struct ofputil_switch_config config; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); fetch_switch_config(vconn, &config); puts(ofputil_frag_handling_to_string(config.frag)); vconn_close(vconn); } static void ofctl_set_frags(struct ovs_cmdl_context *ctx) { struct ofputil_switch_config config; enum ofputil_frag_handling frag; struct vconn *vconn; if (!ofputil_frag_handling_from_string(ctx->argv[2], &frag)) { ovs_fatal(0, "%s: unknown fragment handling mode", ctx->argv[2]); } open_vconn(ctx->argv[1], &vconn); fetch_switch_config(vconn, &config); if (frag != config.frag) { /* Set the configuration. */ config.frag = frag; set_switch_config(vconn, &config); /* Then retrieve the configuration to see if it really took. OpenFlow * has ill-defined error reporting for bad flags, so this is about the * best we can do. */ fetch_switch_config(vconn, &config); if (frag != config.frag) { ovs_fatal(0, "%s: setting fragment handling mode failed (this " "switch probably doesn't support mode \"%s\")", ctx->argv[1], ofputil_frag_handling_to_string(frag)); } } vconn_close(vconn); } static void ofctl_ofp_parse(struct ovs_cmdl_context *ctx) { const char *filename = ctx->argv[1]; struct ofpbuf b; FILE *file; file = !strcmp(filename, "-") ? stdin : fopen(filename, "r"); if (file == NULL) { ovs_fatal(errno, "%s: open", filename); } ofpbuf_init(&b, 65536); for (;;) { struct ofp_header *oh; size_t length, tail_len; void *tail; size_t n; ofpbuf_clear(&b); oh = ofpbuf_put_uninit(&b, sizeof *oh); n = fread(oh, 1, sizeof *oh, file); if (n == 0) { break; } else if (n < sizeof *oh) { ovs_fatal(0, "%s: unexpected end of file mid-message", filename); } length = ntohs(oh->length); if (length < sizeof *oh) { ovs_fatal(0, "%s: %"PRIuSIZE"-byte message is too short for OpenFlow", filename, length); } tail_len = length - sizeof *oh; tail = ofpbuf_put_uninit(&b, tail_len); n = fread(tail, 1, tail_len, file); if (n < tail_len) { ovs_fatal(0, "%s: unexpected end of file mid-message", filename); } ofp_print(stdout, b.data, b.size, NULL, NULL, verbosity + 2); } ofpbuf_uninit(&b); if (file != stdin) { fclose(file); } } static bool is_openflow_port(ovs_be16 port_, char *ports[]) { uint16_t port = ntohs(port_); if (ports[0]) { int i; for (i = 0; ports[i]; i++) { if (port == atoi(ports[i])) { return true; } } return false; } else { return port == OFP_PORT || port == OFP_OLD_PORT; } } static void ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) { struct tcp_reader *reader; struct pcap_file *p_file; int error; bool first; p_file = ovs_pcap_open(ctx->argv[1], "rb"); if (!p_file) { ovs_fatal(errno, "%s: open failed", ctx->argv[1]); } reader = tcp_reader_open(); first = true; for (;;) { struct dp_packet *packet; long long int when; struct flow flow; error = ovs_pcap_read(p_file, &packet, &when); if (error) { break; } pkt_metadata_init(&packet->md, ODPP_NONE); flow_extract(packet, &flow); if (flow.dl_type == htons(ETH_TYPE_IP) && flow.nw_proto == IPPROTO_TCP && (is_openflow_port(flow.tp_src, ctx->argv + 2) || is_openflow_port(flow.tp_dst, ctx->argv + 2))) { struct dp_packet *payload = tcp_reader_run(reader, &flow, packet); if (payload) { while (dp_packet_size(payload) >= sizeof(struct ofp_header)) { const struct ofp_header *oh; void *data = dp_packet_data(payload); int length; /* Align OpenFlow on 8-byte boundary for safe access. */ dp_packet_shift(payload, -((intptr_t) data & 7)); oh = dp_packet_data(payload); length = ntohs(oh->length); if (dp_packet_size(payload) < length || length < sizeof *oh) { break; } if (!first) { putchar('\n'); } first = false; if (timestamp) { char *s = xastrftime_msec("%H:%M:%S.### ", when, true); fputs(s, stdout); free(s); } printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n", IP_ARGS(flow.nw_src), ntohs(flow.tp_src), IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst)); ofp_print(stdout, dp_packet_data(payload), length, NULL, NULL, verbosity + 1); dp_packet_pull(payload, length); } } } dp_packet_delete(packet); } tcp_reader_close(reader); ovs_pcap_close(p_file); } static void ofctl_ping(struct ovs_cmdl_context *ctx) { size_t max_payload = 65535 - sizeof(struct ofp_header); unsigned int payload; struct vconn *vconn; int i; payload = ctx->argc > 2 ? atoi(ctx->argv[2]) : 64; if (payload > max_payload) { ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload); } open_vconn(ctx->argv[1], &vconn); for (i = 0; i < 10; i++) { struct timeval start, end; struct ofpbuf *request, *reply; const struct ofp_header *rpy_hdr; enum ofptype type; request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, vconn_get_version(vconn), payload); random_bytes(ofpbuf_put_uninit(request, payload), payload); xgettimeofday(&start); run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact"); xgettimeofday(&end); rpy_hdr = reply->data; if (ofptype_pull(&type, reply) || type != OFPTYPE_ECHO_REPLY || reply->size != payload || memcmp(request->msg, reply->msg, payload)) { printf("Reply does not match request. Request:\n"); ofp_print(stdout, request, request->size, NULL, NULL, verbosity + 2); printf("Reply:\n"); ofp_print(stdout, reply, reply->size, NULL, NULL, verbosity + 2); } printf("%"PRIu32" bytes from %s: xid=%08"PRIx32" time=%.1f ms\n", reply->size, ctx->argv[1], ntohl(rpy_hdr->xid), (1000*(double)(end.tv_sec - start.tv_sec)) + (.001*(end.tv_usec - start.tv_usec))); ofpbuf_delete(request); ofpbuf_delete(reply); } vconn_close(vconn); } static void ofctl_benchmark(struct ovs_cmdl_context *ctx) { size_t max_payload = 65535 - sizeof(struct ofp_header); struct timeval start, end; unsigned int payload_size, message_size; struct vconn *vconn; double duration; int count; int i; payload_size = atoi(ctx->argv[2]); if (payload_size > max_payload) { ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload); } message_size = sizeof(struct ofp_header) + payload_size; count = atoi(ctx->argv[3]); printf("Sending %d packets * %u bytes (with header) = %u bytes total\n", count, message_size, count * message_size); open_vconn(ctx->argv[1], &vconn); xgettimeofday(&start); for (i = 0; i < count; i++) { struct ofpbuf *request, *reply; request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, vconn_get_version(vconn), payload_size); ofpbuf_put_zeros(request, payload_size); run(vconn_transact(vconn, request, &reply), "transact"); ofpbuf_delete(reply); } xgettimeofday(&end); vconn_close(vconn); duration = ((1000*(double)(end.tv_sec - start.tv_sec)) + (.001*(end.tv_usec - start.tv_usec))); printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n", duration, count / (duration / 1000.0), count * message_size / (duration / 1000.0)); } static void ofctl_dump_ipfix_bridge(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_NXST_IPFIX_BRIDGE_REQUEST); } static void ofctl_ct_flush_zone(struct ovs_cmdl_context *ctx) { uint16_t zone_id; char *error = str_to_u16(ctx->argv[2], "zone_id", &zone_id); if (error) { ovs_fatal(0, "%s", error); } struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); enum ofp_version version = vconn_get_version(vconn); struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_CT_FLUSH_ZONE, version, 0); struct nx_zone_id *nzi = ofpbuf_put_zeros(msg, sizeof *nzi); nzi->zone_id = htons(zone_id); transact_noreply(vconn, msg); vconn_close(vconn); } static void ofctl_ct_flush(struct ovs_cmdl_context *ctx) { struct vconn *vconn; struct ofp_ct_match match = {0}; struct ds ds = DS_EMPTY_INITIALIZER; uint16_t zone; int args = ctx->argc - 2; bool with_zone = false; if (args && !ofp_ct_match_parse((const char **) &ctx->argv[2], args, &ds, &match, &with_zone, &zone)) { ovs_fatal(0, "Failed to parse CT match: %s", ds_cstr(&ds)); } open_vconn(ctx->argv[1], &vconn); enum ofp_version version = vconn_get_version(vconn); struct ofpbuf *msg = ofp_ct_match_encode(&match, with_zone ? &zone : NULL, version); ds_destroy(&ds); transact_noreply(vconn, msg); vconn_close(vconn); } static void ofctl_dump_ipfix_flow(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_NXST_IPFIX_FLOW_REQUEST); } static void bundle_group_mod__(const char *remote, struct ofputil_group_mod *gms, size_t n_gms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; enum ofp_version version; struct vconn *vconn; struct ovs_list requests; size_t i; ovs_list_init(&requests); /* Bundles need OpenFlow 1.3+. */ usable_protocols &= OFPUTIL_P_OF13_UP; protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); for (i = 0; i < n_gms; i++) { struct ofputil_group_mod *gm = &gms[i]; struct ofpbuf *request = ofputil_encode_group_mod(version, gm, NULL, -1); ovs_list_push_back(&requests, &request->list_node); ofputil_uninit_group_mod(gm); } bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); ofpbuf_list_delete(&requests); vconn_close(vconn); } static void ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms, size_t n_gms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct ofputil_group_mod *gm; enum ofp_version version; struct ofpbuf *request; struct vconn *vconn; size_t i; if (bundle) { bundle_group_mod__(remote, gms, n_gms, usable_protocols); return; } protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); for (i = 0; i < n_gms; i++) { gm = &gms[i]; request = ofputil_encode_group_mod(version, gm, NULL, -1); transact_noreply(vconn, request); ofputil_uninit_group_mod(gm); } vconn_close(vconn); } static void ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], int command) { struct ofputil_group_mod *gms = NULL; enum ofputil_protocol usable_protocols; size_t n_gms = 0; char *error; if (command == OFPGC11_ADD) { /* Allow the file to specify a mix of commands. If none specified at * the beginning of any given line, then the default is OFPGC11_ADD, so * this is backwards compatible. */ command = -2; } error = parse_ofp_group_mod_file(argv[2], ports_to_accept(argv[1]), tables_to_accept(argv[1]), command, &gms, &n_gms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_group_mod__(argv[1], gms, n_gms, usable_protocols); free(gms); } static void ofctl_group_mod(int argc, char *argv[], uint16_t command) { if (argc > 2 && !strcmp(argv[2], "-")) { ofctl_group_mod_file(argc, argv, command); } else { enum ofputil_protocol usable_protocols; struct ofputil_group_mod gm; char *error; error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "", ports_to_accept(argv[1]), tables_to_accept(argv[1]), &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_group_mod__(argv[1], &gm, 1, usable_protocols); } } static void ofctl_add_group(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC11_ADD); } static void ofctl_add_groups(struct ovs_cmdl_context *ctx) { ofctl_group_mod_file(ctx->argc, ctx->argv, OFPGC11_ADD); } static void ofctl_mod_group(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, may_create ? OFPGC11_ADD_OR_MOD : OFPGC11_MODIFY); } static void ofctl_del_groups(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC11_DELETE); } static void ofctl_insert_bucket(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC15_INSERT_BUCKET); } static void ofctl_remove_bucket(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC15_REMOVE_BUCKET); } static void ofctl_dump_group_stats(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_group_mod gm; struct ofpbuf *request; struct vconn *vconn; uint32_t group_id; char *error; memset(&gm, 0, sizeof gm); error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE, ctx->argc > 2 ? ctx->argv[2] : "", ports_to_accept(ctx->argv[1]), tables_to_accept(ctx->argv[1]), &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } group_id = gm.group_id; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_group_stats_request(vconn_get_version(vconn), group_id); if (request) { dump_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_dump_group_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; uint32_t group_id; open_vconn(ctx->argv[1], &vconn); if (ctx->argc < 3 || !ofputil_group_from_string(ctx->argv[2], &group_id)) { group_id = OFPG_ALL; } request = ofputil_encode_group_desc_request(vconn_get_version(vconn), group_id); if (request) { dump_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_dump_group_features(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_group_features_request(vconn_get_version(vconn)); if (request) { dump_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_bundle(struct ovs_cmdl_context *ctx) { enum ofputil_protocol protocol, usable_protocols; struct ofputil_bundle_msg *bms; struct ovs_list requests; struct vconn *vconn; size_t n_bms; char *error; error = parse_ofp_bundle_file(ctx->argv[2], ports_to_accept(ctx->argv[1]), tables_to_accept(ctx->argv[1]), &bms, &n_bms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } /* Implicit OpenFlow 1.4. */ if (!(get_allowed_ofp_versions() & ofputil_protocols_to_version_bitmap(OFPUTIL_P_OF13_UP))) { /* Add implicit allowance for OpenFlow 1.4. */ add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF14_OXM)); /* Remove all versions that do not support bundles. */ mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF13_UP)); allowed_protocols = ofputil_protocols_from_version_bitmap( get_allowed_ofp_versions()); } /* Bundles need OpenFlow 1.3+. */ usable_protocols &= OFPUTIL_P_OF13_UP; protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); ovs_list_init(&requests); ofputil_encode_bundle_msgs(bms, n_bms, &requests, protocol); ofputil_free_bundle_msgs(bms, n_bms); bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); ofpbuf_list_delete(&requests); vconn_close(vconn); } static void ofctl_tlv_mod(struct ovs_cmdl_context *ctx, uint16_t command) { enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; struct ofputil_tlv_table_mod ttm; char *error; enum ofp_version version; struct ofpbuf *request; struct vconn *vconn; error = parse_ofp_tlv_table_mod_str(&ttm, command, ctx->argc > 2 ? ctx->argv[2] : "", &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); request = ofputil_encode_tlv_table_mod(version, &ttm); if (request) { transact_noreply(vconn, request); } vconn_close(vconn); ofputil_uninit_tlv_table(&ttm.mappings); } static void ofctl_add_tlv_map(struct ovs_cmdl_context *ctx) { ofctl_tlv_mod(ctx, NXTTMC_ADD); } static void ofctl_del_tlv_map(struct ovs_cmdl_context *ctx) { ofctl_tlv_mod(ctx, ctx->argc > 2 ? NXTTMC_DELETE : NXTTMC_CLEAR); } static void ofctl_dump_tlv_map(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_NXT_TLV_TABLE_REQUEST); } static void ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } static void ofctl_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovs_cmdl_print_commands(get_all_commands()); } /* replace-flows and diff-flows commands. */ struct flow_tables { struct classifier tables[OFPTT_MAX + 1]; }; #define FOR_EACH_TABLE(CLS, TABLES) \ for ((CLS) = (TABLES)->tables; \ (CLS) < &(TABLES)->tables[ARRAY_SIZE((TABLES)->tables)]; \ (CLS)++) static void flow_tables_init(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_init(cls, NULL); } } static void flow_tables_defer(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_defer(cls); } } static void flow_tables_publish(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_publish(cls); } } /* A flow table entry, possibly with two different versions. */ struct fte { struct cls_rule rule; /* Within a "struct classifier". */ struct fte_version *versions[2]; }; /* One version of a Flow Table Entry. */ struct fte_version { ovs_be64 cookie; uint16_t idle_timeout; uint16_t hard_timeout; uint16_t importance; uint16_t flags; struct ofpact *ofpacts; size_t ofpacts_len; uint8_t table_id; }; /* A FTE entry that has been queued for later insertion after all * flows have been scanned to correctly allocation tunnel metadata. */ struct fte_pending { struct minimatch match; int priority; struct fte_version *version; int index; struct ovs_list list_node; }; /* Processing state during two stage processing of flow table entries. * Tracks the maximum size seen for each tunnel metadata entry as well * as a list of the pending FTE entries. */ struct fte_state { int tun_metadata_size[TUN_METADATA_NUM_OPTS]; struct ovs_list fte_pending_list; /* The final metadata table that we have constructed. */ struct tun_table *tun_tab; /* Port and table map. There is only one of each, not one per source, * because it only makes sense to display a single name for a given port * or table number. */ const struct ofputil_port_map *port_map; const struct ofputil_table_map *table_map; }; /* Frees 'version' and the data that it owns. */ static void fte_version_free(struct fte_version *version) { if (version) { free(CONST_CAST(struct ofpact *, version->ofpacts)); free(version); } } /* Returns true if 'a' and 'b' are the same, false if they differ. * * Ignores differences in 'flags' because there's no way to retrieve flags from * an OpenFlow switch. We have to assume that they are the same. */ static bool fte_version_equals(const struct fte_version *a, const struct fte_version *b) { return (a->cookie == b->cookie && a->idle_timeout == b->idle_timeout && a->hard_timeout == b->hard_timeout && a->importance == b->importance && a->table_id == b->table_id && ofpacts_equal_stringwise(a->ofpacts, a->ofpacts_len, b->ofpacts, b->ofpacts_len)); } /* Clears 's', then if 's' has a version 'index', formats 'fte' and version * 'index' into 's', followed by a new-line. */ static void fte_version_format(const struct fte_state *fte_state, const struct fte *fte, int index, struct ds *s) { const struct fte_version *version = fte->versions[index]; ds_clear(s); if (!version) { return; } if (version->table_id) { ds_put_format(s, "table=%"PRIu8" ", version->table_id); } cls_rule_format(&fte->rule, fte_state->tun_tab, fte_state->port_map, s); if (version->cookie != htonll(0)) { ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie)); } if (version->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout); } if (version->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout); } if (version->importance != 0) { ds_put_format(s, " importance=%"PRIu16, version->importance); } ds_put_cstr(s, " actions="); struct ofpact_format_params fp = { .port_map = fte_state->port_map, .s = s, }; ofpacts_format(version->ofpacts, version->ofpacts_len, &fp); ds_put_char(s, '\n'); } static struct fte * fte_from_cls_rule(const struct cls_rule *cls_rule) { return cls_rule ? CONTAINER_OF(cls_rule, struct fte, rule) : NULL; } /* Frees 'fte' and its versions. */ static void fte_free(struct fte *fte) { if (fte) { fte_version_free(fte->versions[0]); fte_version_free(fte->versions[1]); cls_rule_destroy(&fte->rule); free(fte); } } /* Frees all of the FTEs within 'tables'. */ static void fte_free_all(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { struct fte *fte; classifier_defer(cls); CLS_FOR_EACH (fte, rule, cls) { classifier_remove_assert(cls, &fte->rule); ovsrcu_postpone(fte_free, fte); } classifier_destroy(cls); } } /* Searches 'tables' for an FTE matching 'rule', inserting a new one if * necessary. Sets 'version' as the version of that rule with the given * 'index', replacing any existing version, if any. * * Takes ownership of 'version'. */ static void fte_insert(struct flow_tables *tables, const struct minimatch *match, int priority, struct fte_version *version, int index) { struct classifier *cls = &tables->tables[version->table_id]; struct fte *old, *fte; fte = xzalloc(sizeof *fte); cls_rule_init_from_minimatch(&fte->rule, match, priority); fte->versions[index] = version; old = fte_from_cls_rule(classifier_replace(cls, &fte->rule, OVS_VERSION_MIN, NULL, 0)); if (old) { fte->versions[!index] = old->versions[!index]; old->versions[!index] = NULL; ovsrcu_postpone(fte_free, old); } } /* Given a list of the field sizes for each tunnel metadata entry, install * a mapping table for later operations. */ static void generate_tun_metadata(struct fte_state *state) { struct ofputil_tlv_table_mod ttm; int i; ttm.command = NXTTMC_ADD; ovs_list_init(&ttm.mappings); for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) { if (state->tun_metadata_size[i] != -1) { struct ofputil_tlv_map *map = xmalloc(sizeof *map); ovs_list_push_back(&ttm.mappings, &map->list_node); /* We don't care about the actual option class and type since there * won't be any lookup. We just need to make them unique. */ map->option_class = i / UINT8_MAX; map->option_type = i; map->option_len = ROUND_UP(state->tun_metadata_size[i], 4); map->index = i; } } tun_metadata_table_mod(&ttm, NULL, &state->tun_tab); ofputil_uninit_tlv_table(&ttm.mappings); } /* Once we have created a tunnel mapping table with a consistent overall * allocation, we need to remap each flow to use this table from its own * allocation. Since the mapping table has already been installed, we * can just read the data from the match and rewrite it. On rewrite, it * will use the new table. */ static void remap_match(struct fte_state *state, struct minimatch *minimatch) { int i; if (!minimatch->tun_md || !minimatch->tun_md->valid) { return; } struct match match; minimatch_expand(minimatch, &match); struct tun_metadata flow = match.flow.tunnel.metadata; struct tun_metadata flow_mask = match.wc.masks.tunnel.metadata; memset(&match.flow.tunnel.metadata, 0, sizeof match.flow.tunnel.metadata); memset(&match.wc.masks.tunnel.metadata, 0, sizeof match.wc.masks.tunnel.metadata); match.tun_md.valid = false; match.flow.tunnel.metadata.tab = state->tun_tab; match.wc.masks.tunnel.metadata.tab = match.flow.tunnel.metadata.tab; ULLONG_FOR_EACH_1 (i, flow_mask.present.map) { const struct mf_field *field = mf_from_id(MFF_TUN_METADATA0 + i); int offset = match.tun_md.entry[i].loc.c.offset; int len = match.tun_md.entry[i].loc.len; union mf_value value, mask; memset(&value, 0, field->n_bytes - len); memset(&mask, match.tun_md.entry[i].masked ? 0 : 0xff, field->n_bytes - len); memcpy(value.tun_metadata + field->n_bytes - len, flow.opts.u8 + offset, len); memcpy(mask.tun_metadata + field->n_bytes - len, flow_mask.opts.u8 + offset, len); mf_set(field, &value, &mask, &match, NULL); } minimatch_destroy(minimatch); minimatch_init(minimatch, &match); } /* In order to correctly handle tunnel metadata, we need to have * two passes over the flows. This happens because tunnel metadata * doesn't have fixed locations in a flow entry but is instead dynamically * allocated space. In the case of flows coming from a file, we don't * even know the size of each field when we need to do the allocation. * When the flows come in, each flow has an individual allocation based * on its own fields. However, this allocation is not the same across * different flows and therefore fields are not directly comparable. * * In the first pass, we record the maximum size of each tunnel metadata * field as well as queue FTE entries for later processing. * * In the second pass, we use the metadata size information to create a * tunnel mapping table and set that through the tunnel metadata processing * code. We then remap all individual flows to use this common allocation * scheme. Finally, we load the queued entries into the classifier for * comparison. * * fte_state_init() should be called before processing any flows. */ static void fte_state_init(struct fte_state *state) { int i; for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) { state->tun_metadata_size[i] = -1; } ovs_list_init(&state->fte_pending_list); state->tun_tab = NULL; state->port_map = NULL; state->table_map = NULL; } static void fte_state_destroy(struct fte_state *state) { tun_metadata_free(state->tun_tab); } /* The first pass of the processing described in the comment about * fte_state_init(). fte_queue() is the first pass to be called as each * flow is read from its source. */ static void fte_queue(struct fte_state *state, const struct minimatch *match, int priority, struct fte_version *version, int index) { struct fte_pending *pending = xmalloc(sizeof *pending); int i; minimatch_clone(&pending->match, match); pending->priority = priority; pending->version = version; pending->index = index; ovs_list_push_back(&state->fte_pending_list, &pending->list_node); if (!match->tun_md || !match->tun_md->valid) { return; } uint64_t map = miniflow_get_tun_metadata_present_map(&match->mask->masks); ULLONG_FOR_EACH_1 (i, map) { if (match->tun_md->entry[i].loc.len > state->tun_metadata_size[i]) { state->tun_metadata_size[i] = match->tun_md->entry[i].loc.len; } } } /* The second pass of the processing described in the comment about * fte_state_init(). This should be called once all flows (from both * sides of the comparison) have been added through fte_queue(). */ static void fte_fill(struct fte_state *state, struct flow_tables *tables) { struct fte_pending *pending; generate_tun_metadata(state); flow_tables_init(tables); flow_tables_defer(tables); LIST_FOR_EACH_POP(pending, list_node, &state->fte_pending_list) { remap_match(state, &pending->match); fte_insert(tables, &pending->match, pending->priority, pending->version, pending->index); minimatch_destroy(&pending->match); free(pending); } flow_tables_publish(tables); } /* Reads the flows in 'filename' as flow table entries in 'tables' for the * version with the specified 'index'. Returns the flow formats able to * represent the flows that were read. */ static enum ofputil_protocol read_flows_from_file(const char *filename, struct fte_state *state, int index) { enum ofputil_protocol usable_protocols; int line_number; struct ds s; FILE *file; file = !strcmp(filename, "-") ? stdin : fopen(filename, "r"); if (file == NULL) { ovs_fatal(errno, "%s: open", filename); } ds_init(&s); usable_protocols = OFPUTIL_P_ANY; line_number = 0; while (!ds_get_preprocessed_line(&s, file, &line_number)) { struct fte_version *version; struct ofputil_flow_mod fm; char *error; enum ofputil_protocol usable; error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), state->port_map, state->table_map, &usable); if (error) { ovs_fatal(0, "%s:%d: %s", filename, line_number, error); } usable_protocols &= usable; version = xmalloc(sizeof *version); version->cookie = fm.new_cookie; version->idle_timeout = fm.idle_timeout; version->hard_timeout = fm.hard_timeout; version->importance = fm.importance; version->flags = fm.flags & (OFPUTIL_FF_SEND_FLOW_REM | OFPUTIL_FF_EMERG); version->ofpacts = fm.ofpacts; version->ofpacts_len = fm.ofpacts_len; version->table_id = fm.table_id != OFPTT_ALL ? fm.table_id : 0; fte_queue(state, &fm.match, fm.priority, version, index); minimatch_destroy(&fm.match); } ds_destroy(&s); if (file != stdin) { fclose(file); } return usable_protocols; } /* Reads the OpenFlow flow table from 'vconn', which has currently active flow * format 'protocol', and adds them as flow table entries in 'tables' for the * version with the specified 'index'. */ static void read_flows_from_switch(struct vconn *vconn, enum ofputil_protocol protocol, struct fte_state *state, int index) { struct ofputil_flow_stats_request fsr; fsr.aggregate = false; match_init_catchall(&fsr.match); fsr.out_port = OFPP_ANY; fsr.out_group = OFPG_ANY; fsr.table_id = 0xff; fsr.cookie = fsr.cookie_mask = htonll(0); struct ofputil_flow_stats *fses; size_t n_fses; run(vconn_dump_flows(vconn, &fsr, protocol, &fses, &n_fses), "dump flows"); for (size_t i = 0; i < n_fses; i++) { const struct ofputil_flow_stats *fs = &fses[i]; struct fte_version *version; version = xmalloc(sizeof *version); version->cookie = fs->cookie; version->idle_timeout = fs->idle_timeout; version->hard_timeout = fs->hard_timeout; version->importance = fs->importance; version->flags = 0; version->ofpacts_len = fs->ofpacts_len; version->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); version->table_id = fs->table_id; struct minimatch match; minimatch_init(&match, &fs->match); fte_queue(state, &match, fs->priority, version, index); minimatch_destroy(&match); } for (size_t i = 0; i < n_fses; i++) { free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); } free(fses); } static void fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, enum ofputil_protocol protocol, struct ovs_list *packets) { const struct fte_version *version = fte->versions[index]; struct ofpbuf *ofm; struct ofputil_flow_mod fm = { .priority = fte->rule.priority, .new_cookie = version->cookie, .modify_cookie = true, .table_id = version->table_id, .command = command, .idle_timeout = version->idle_timeout, .hard_timeout = version->hard_timeout, .importance = version->importance, .buffer_id = UINT32_MAX, .out_port = OFPP_ANY, .out_group = OFPG_ANY, .flags = version->flags, }; minimatch_clone(&fm.match, &fte->rule.match); if (command == OFPFC_ADD || command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { fm.ofpacts = version->ofpacts; fm.ofpacts_len = version->ofpacts_len; } else { fm.ofpacts = NULL; fm.ofpacts_len = 0; } ofm = ofputil_encode_flow_mod(&fm, protocol); minimatch_destroy(&fm.match); ovs_list_push_back(packets, &ofm->list_node); } static void ofctl_replace_flows(struct ovs_cmdl_context *ctx) { enum { FILE_IDX = 0, SWITCH_IDX = 1 }; enum ofputil_protocol usable_protocols, protocol; struct fte_state fte_state; struct flow_tables tables; struct classifier *cls; struct ovs_list requests; struct vconn *vconn; struct fte *fte; fte_state_init(&fte_state); fte_state.port_map = ports_to_accept(ctx->argv[1]); fte_state.table_map = tables_to_accept(ctx->argv[1]); usable_protocols = read_flows_from_file(ctx->argv[2], &fte_state, FILE_IDX); protocol = open_vconn(ctx->argv[1], &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); read_flows_from_switch(vconn, protocol, &fte_state, SWITCH_IDX); fte_fill(&fte_state, &tables); ovs_list_init(&requests); FOR_EACH_TABLE (cls, &tables) { /* Delete flows that exist on the switch but not in the file. */ CLS_FOR_EACH (fte, rule, cls) { struct fte_version *file_ver = fte->versions[FILE_IDX]; struct fte_version *sw_ver = fte->versions[SWITCH_IDX]; if (sw_ver && !file_ver) { fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT, protocol, &requests); } } /* Add flows that exist in the file but not on the switch. * Update flows that exist in both places but differ. */ CLS_FOR_EACH (fte, rule, cls) { struct fte_version *file_ver = fte->versions[FILE_IDX]; struct fte_version *sw_ver = fte->versions[SWITCH_IDX]; if (file_ver && (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) { fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests); } } } if (bundle) { bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); } else { transact_multiple_noreply(vconn, &requests); } ofpbuf_list_delete(&requests); vconn_close(vconn); fte_free_all(&tables); fte_state_destroy(&fte_state); } static void read_flows_from_source(const char *source, struct fte_state *state, int index) { struct stat s; if (source[0] == '/' || source[0] == '.' || (!strchr(source, ':') && !stat(source, &s))) { read_flows_from_file(source, state, index); } else { enum ofputil_protocol protocol; struct vconn *vconn; protocol = open_vconn(source, &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY); read_flows_from_switch(vconn, protocol, state, index); vconn_close(vconn); if (!state->port_map) { state->port_map = ports_to_show(source); } } } static void ofctl_diff_flows(struct ovs_cmdl_context *ctx) { bool differences = false; struct fte_state fte_state; struct flow_tables tables; struct classifier *cls; struct ds a_s, b_s; struct fte *fte; fte_state_init(&fte_state); read_flows_from_source(ctx->argv[1], &fte_state, 0); read_flows_from_source(ctx->argv[2], &fte_state, 1); fte_fill(&fte_state, &tables); ds_init(&a_s); ds_init(&b_s); FOR_EACH_TABLE (cls, &tables) { CLS_FOR_EACH (fte, rule, cls) { struct fte_version *a = fte->versions[0]; struct fte_version *b = fte->versions[1]; if (!a || !b || !fte_version_equals(a, b)) { fte_version_format(&fte_state, fte, 0, &a_s); fte_version_format(&fte_state, fte, 1, &b_s); if (a_s.length) { printf("-%s", ds_cstr(&a_s)); } if (b_s.length) { printf("+%s", ds_cstr(&b_s)); } differences = true; } } } ds_destroy(&a_s); ds_destroy(&b_s); fte_free_all(&tables); fte_state_destroy(&fte_state); if (differences) { exit(2); } } static void ofctl_meter_mod__(const char *bridge, const char *str, int command) { struct ofputil_meter_mod mm; struct vconn *vconn; enum ofputil_protocol protocol; enum ofputil_protocol usable_protocols; enum ofp_version version; memset(&mm, 0, sizeof mm); if (str) { char *error; error = parse_ofp_meter_mod_str(&mm, str, command, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } } else { usable_protocols = OFPUTIL_P_OF13_UP; mm.command = command; mm.meter.meter_id = OFPM13_ALL; } protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); transact_noreply(vconn, ofputil_encode_meter_mod(version, &mm)); free(mm.meter.bands); vconn_close(vconn); } static void ofctl_meter_request__(const char *bridge, const char *str, enum ofputil_meter_request_type type) { struct ofputil_meter_mod mm; struct vconn *vconn; enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; enum ofp_version version; memset(&mm, 0, sizeof mm); if (str) { char *error; error = parse_ofp_meter_mod_str(&mm, str, -1, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } } else { usable_protocols = OFPUTIL_P_OF13_UP; mm.meter.meter_id = OFPM13_ALL; } protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); dump_transaction(vconn, ofputil_encode_meter_request(version, type, mm.meter.meter_id)); free(mm.meter.bands); vconn_close(vconn); } static void ofctl_add_meter(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argv[2], OFPMC13_ADD); } static void ofctl_mod_meter(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argv[2], OFPMC13_MODIFY); } static void ofctl_del_meters(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPMC13_DELETE); } static void ofctl_dump_meters(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPUTIL_METER_CONFIG); } static void ofctl_meter_stats(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPUTIL_METER_STATS); } static void ofctl_meter_features(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], NULL, OFPUTIL_METER_FEATURES); } /* Undocumented commands for unit testing. */ static void ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol = 0; char *usable_s; size_t i; usable_s = ofputil_protocols_to_string(usable_protocols); printf("usable protocols: %s\n", usable_s); free(usable_s); if (!(usable_protocols & allowed_protocols)) { ovs_fatal(0, "no usable protocol"); } for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { protocol = 1 << i; if (protocol & usable_protocols & allowed_protocols) { break; } } ovs_assert(is_pow2(protocol)); printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol)); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *msg; msg = ofputil_encode_flow_mod(fm, protocol); ofp_print(stdout, msg->data, msg->size, NULL, NULL, verbosity); ofpbuf_delete(msg); free(CONST_CAST(struct ofpact *, fm->ofpacts)); minimatch_destroy(&fm->match); } } /* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints * it back to stdout. */ static void ofctl_parse_flow(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod fm; char *error; error = parse_ofp_flow_mod_str(&fm, ctx->argv[1], NULL, NULL, OFPFC_ADD, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_parse_flows__(&fm, 1, usable_protocols); } /* "parse-flows FILENAME": reads the named file as a sequence of flows (like * add-flows) and prints each of the flows back to stdout. */ static void ofctl_parse_flows(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod *fms = NULL; size_t n_fms = 0; char *error; error = parse_ofp_flow_mod_file(ctx->argv[1], NULL, NULL, OFPFC_ADD, &fms, &n_fms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_parse_flows__(fms, n_fms, usable_protocols); free(fms); } /* "parse-group GROUP": parses the argument as a group (like add-group) and * prints it back to stdout. */ static void ofctl_parse_group(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_group_mod gm; char *error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD, ctx->argv[1], NULL, NULL, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } char *usable_s = ofputil_protocols_to_string(usable_protocols); printf("usable protocols: %s\n", usable_s); free(usable_s); if (!(usable_protocols & allowed_protocols)) { ovs_fatal(0, "no usable protocol"); } enum ofputil_protocol protocol = 0; for (int i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { protocol = 1 << i; if (protocol & usable_protocols & allowed_protocols) { break; } } enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); printf("chosen version: %s\n", ofputil_version_to_string(version)); struct ofpbuf *msg = ofputil_encode_group_mod(version, &gm, NULL, false); ofp_print(stdout, msg->data, msg->size, NULL, NULL, verbosity); ofpbuf_delete(msg); ofputil_uninit_group_mod(&gm); } static void ofctl_parse_nxm__(bool oxm, enum ofp_version version) { struct ds in; ds_init(&in); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf nx_match; struct match match; ovs_be64 cookie, cookie_mask; enum ofperr error; int match_len; /* Convert string to nx_match. */ ofpbuf_init(&nx_match, 0); if (oxm) { match_len = oxm_match_from_string(ds_cstr(&in), &nx_match); } else { match_len = nx_match_from_string(ds_cstr(&in), &nx_match); } /* Convert nx_match to match. */ if (strict) { if (oxm) { error = oxm_pull_match(&nx_match, false, NULL, NULL, &match); } else { error = nx_pull_match(&nx_match, match_len, &match, &cookie, &cookie_mask, false, NULL, NULL); } } else { if (oxm) { error = oxm_pull_match_loose(&nx_match, false, NULL, &match); } else { error = nx_pull_match_loose(&nx_match, match_len, &match, &cookie, &cookie_mask, false, NULL); } } if (!error) { char *out; /* Convert match back to nx_match. */ ofpbuf_uninit(&nx_match); ofpbuf_init(&nx_match, 0); if (oxm) { match_len = oxm_put_match(&nx_match, &match, version); out = oxm_match_to_string(&nx_match, match_len); } else { match_len = nx_put_match(&nx_match, &match, cookie, cookie_mask); out = nx_match_to_string(nx_match.data, match_len); } puts(out); free(out); if (verbosity > 0) { ovs_hex_dump(stdout, nx_match.data, nx_match.size, 0, false); } } else { printf("nx_pull_match() returned error %s\n", ofperr_get_name(error)); } ofpbuf_uninit(&nx_match); } ds_destroy(&in); } /* "parse-nxm": reads a series of NXM nx_match specifications as strings from * stdin, does some internal fussing with them, and then prints them back as * strings on stdout. */ static void ofctl_parse_nxm(struct ovs_cmdl_context *ctx OVS_UNUSED) { ofctl_parse_nxm__(false, 0); } /* "parse-oxm VERSION": reads a series of OXM nx_match specifications as * strings from stdin, does some internal fussing with them, and then prints * them back as strings on stdout. VERSION must specify an OpenFlow version, * e.g. "OpenFlow12". */ static void ofctl_parse_oxm(struct ovs_cmdl_context *ctx) { enum ofp_version version = ofputil_version_from_string(ctx->argv[1]); if (version < OFP12_VERSION) { ovs_fatal(0, "%s: not a valid version for OXM", ctx->argv[1]); } ofctl_parse_nxm__(true, version); } static void print_differences(const char *prefix, const void *a_, size_t a_len, const void *b_, size_t b_len) { const uint8_t *a = a_; const uint8_t *b = b_; size_t i; for (i = 0; i < MIN(a_len, b_len); i++) { if (a[i] != b[i]) { printf("%s%2"PRIuSIZE": %02"PRIx8" -> %02"PRIx8"\n", prefix, i, a[i], b[i]); } } for (i = a_len; i < b_len; i++) { printf("%s%2"PRIuSIZE": (none) -> %02"PRIx8"\n", prefix, i, b[i]); } for (i = b_len; i < a_len; i++) { printf("%s%2"PRIuSIZE": %02"PRIx8" -> (none)\n", prefix, i, a[i]); } } static void ofctl_parse_actions__(const char *version_s, bool instructions) { enum ofp_version version; struct ds in; version = ofputil_version_from_string(version_s); if (!version) { ovs_fatal(0, "%s: not a valid OpenFlow version", version_s); } ds_init(&in); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf of_out; struct ofpbuf of_in; struct ofpbuf ofpacts; const char *table_id; char *actions; enum ofperr error; size_t size; struct ds s; /* Parse table_id separated with the follow-up actions by ",", if * any. */ actions = ds_cstr(&in); table_id = NULL; if (strstr(actions, ",")) { table_id = strsep(&actions, ","); } /* Parse hex bytes. */ ofpbuf_init(&of_in, 0); if (ofpbuf_put_hex(&of_in, actions, NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } /* Convert to ofpacts. */ ofpbuf_init(&ofpacts, 0); size = of_in.size; error = (instructions ? ofpacts_pull_openflow_instructions : ofpacts_pull_openflow_actions)( &of_in, of_in.size, version, NULL, NULL, &ofpacts); if (!error && instructions) { /* Verify actions, enforce consistency. */ struct match match = MATCH_CATCHALL_INITIALIZER; struct ofpact_check_params cp = { .match = &match, .max_ports = OFPP_MAX, .table_id = table_id ? atoi(table_id) : 0, .n_tables = OFPTT_MAX + 1, }; error = ofpacts_check_consistency( ofpacts.data, ofpacts.size, ofputil_protocols_from_ofp_version(version), &cp); } if (error) { printf("bad %s %s: %s\n\n", version_s, instructions ? "instructions" : "actions", ofperr_get_name(error)); ofpbuf_uninit(&ofpacts); ofpbuf_uninit(&of_in); continue; } ofpbuf_push_uninit(&of_in, size); /* Print cls_rule. */ ds_init(&s); ds_put_cstr(&s, "actions="); struct ofpact_format_params fp = { .s = &s }; ofpacts_format(ofpacts.data, ofpacts.size, &fp); puts(ds_cstr(&s)); ds_destroy(&s); /* Convert back to ofp10 actions and print differences from input. */ ofpbuf_init(&of_out, 0); if (instructions) { ofpacts_put_openflow_instructions(ofpacts.data, ofpacts.size, &of_out, version); } else { ofpacts_put_openflow_actions(ofpacts.data, ofpacts.size, &of_out, version); } print_differences("", of_in.data, of_in.size, of_out.data, of_out.size); putchar('\n'); ofpbuf_uninit(&ofpacts); ofpbuf_uninit(&of_in); ofpbuf_uninit(&of_out); } ds_destroy(&in); } /* "parse-actions VERSION": reads a series of action specifications for the * given OpenFlow VERSION as hex bytes from stdin, converts them to ofpacts, * prints them as strings on stdout, and then converts them back to hex bytes * and prints any differences from the input. */ static void ofctl_parse_actions(struct ovs_cmdl_context *ctx) { ofctl_parse_actions__(ctx->argv[1], false); } /* "parse-actions VERSION": reads a series of instruction specifications for * the given OpenFlow VERSION as hex bytes from stdin, converts them to * ofpacts, prints them as strings on stdout, and then converts them back to * hex bytes and prints any differences from the input. */ static void ofctl_parse_instructions(struct ovs_cmdl_context *ctx) { ofctl_parse_actions__(ctx->argv[1], true); } /* "parse-ofp10-match": reads a series of ofp10_match specifications as hex * bytes from stdin, converts them to cls_rules, prints them as strings on * stdout, and then converts them back to hex bytes and prints any differences * from the input. * * The input hex bytes may contain "x"s to represent "don't-cares", bytes whose * values are ignored in the input and will be set to zero when OVS converts * them back to hex bytes. ovs-ofctl actually sets "x"s to random bits when * it does the conversion to hex, to ensure that in fact they are ignored. */ static void ofctl_parse_ofp10_match(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct ds expout; struct ds in; ds_init(&in); ds_init(&expout); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf match_in, match_expout; struct ofp10_match match_out; struct ofp10_match match_normal; struct match match; char *p; /* Parse hex bytes to use for expected output. */ ds_clear(&expout); ds_put_cstr(&expout, ds_cstr(&in)); for (p = ds_cstr(&expout); *p; p++) { if (*p == 'x') { *p = '0'; } } ofpbuf_init(&match_expout, 0); if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_expout.size != sizeof(struct ofp10_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_expout.size, sizeof(struct ofp10_match)); } /* Parse hex bytes for input. */ for (p = ds_cstr(&in); *p; p++) { if (*p == 'x') { *p = "0123456789abcdef"[random_uint32() & 0xf]; } } ofpbuf_init(&match_in, 0); if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_in.size != sizeof(struct ofp10_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_in.size, sizeof(struct ofp10_match)); } /* Convert to cls_rule and print. */ ofputil_match_from_ofp10_match(match_in.data, &match); match_print(&match, NULL); /* Convert back to ofp10_match and print differences from input. */ ofputil_match_to_ofp10_match(&match, &match_out); print_differences("", match_expout.data, match_expout.size, &match_out, sizeof match_out); /* Normalize, then convert and compare again. */ ofputil_normalize_match(&match); ofputil_match_to_ofp10_match(&match, &match_normal); print_differences("normal: ", &match_out, sizeof match_out, &match_normal, sizeof match_normal); putchar('\n'); ofpbuf_uninit(&match_in); ofpbuf_uninit(&match_expout); } ds_destroy(&in); ds_destroy(&expout); } /* "parse-ofp11-match": reads a series of ofp11_match specifications as hex * bytes from stdin, converts them to "struct match"es, prints them as strings * on stdout, and then converts them back to hex bytes and prints any * differences from the input. */ static void ofctl_parse_ofp11_match(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct ds in; ds_init(&in); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf match_in; struct ofp11_match match_out; struct match match; enum ofperr error; /* Parse hex bytes. */ ofpbuf_init(&match_in, 0); if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_in.size != sizeof(struct ofp11_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_in.size, sizeof(struct ofp11_match)); } /* Convert to match. */ error = ofputil_match_from_ofp11_match(match_in.data, &match); if (error) { printf("bad ofp11_match: %s\n\n", ofperr_get_name(error)); ofpbuf_uninit(&match_in); continue; } /* Print match. */ match_print(&match, NULL); /* Convert back to ofp11_match and print differences from input. */ ofputil_match_to_ofp11_match(&match, &match_out); print_differences("", match_in.data, match_in.size, &match_out, sizeof match_out); putchar('\n'); ofpbuf_uninit(&match_in); } ds_destroy(&in); } /* "parse-pcap PCAP...": read packets from each PCAP file and print their * flows. */ static void ofctl_parse_pcap(struct ovs_cmdl_context *ctx) { int error = 0; for (int i = 1; i < ctx->argc; i++) { const char *filename = ctx->argv[i]; struct pcap_file *pcap = ovs_pcap_open(filename, "rb"); if (!pcap) { error = errno; ovs_error(error, "%s: open failed", filename); continue; } for (;;) { struct dp_packet *packet; struct flow flow; int retval; retval = ovs_pcap_read(pcap, &packet, NULL); if (retval == EOF) { break; } else if (retval) { error = retval; ovs_error(error, "%s: read failed", filename); break; } pkt_metadata_init(&packet->md, u32_to_odp(ofp_to_u16(OFPP_ANY))); flow_extract(packet, &flow); flow_print(stdout, &flow, NULL); putchar('\n'); dp_packet_delete(packet); } ovs_pcap_close(pcap); } exit(error); } /* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and * mask values to and from various formats and prints the results. */ static void ofctl_check_vlan(struct ovs_cmdl_context *ctx) { struct match match; char *string_s; struct ofputil_flow_mod fm; struct ofpbuf nxm; struct match nxm_match; int nxm_match_len; char *nxm_s; struct ofp10_match of10_raw; struct match of10_match; struct ofp11_match of11_raw; struct match of11_match; enum ofperr error; char *error_s; enum ofputil_protocol usable_protocols; /* Unused for now. */ match_init_catchall(&match); match.flow.vlans[0].tci = htons(strtoul(ctx->argv[1], NULL, 16)); match.wc.masks.vlans[0].tci = htons(strtoul(ctx->argv[2], NULL, 16)); /* Convert to and from string. */ string_s = match_to_string(&match, NULL, OFP_DEFAULT_PRIORITY); printf("%s -> ", string_s); fflush(stdout); error_s = parse_ofp_str(&fm, -1, string_s, NULL, NULL, &usable_protocols); if (error_s) { ovs_fatal(0, "%s", error_s); } struct match fm_match; minimatch_expand(&fm.match, &fm_match); printf("%04"PRIx16"/%04"PRIx16"\n", ntohs(fm_match.flow.vlans[0].tci), ntohs(fm_match.wc.masks.vlans[0].tci)); free(string_s); minimatch_destroy(&fm.match); /* Convert to and from NXM. */ ofpbuf_init(&nxm, 0); nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0)); nxm_s = nx_match_to_string(nxm.data, nxm_match_len); error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL, false, NULL, NULL); printf("NXM: %s -> ", nxm_s); if (error) { printf("%s\n", ofperr_to_string(error)); } else { printf("%04"PRIx16"/%04"PRIx16"\n", ntohs(nxm_match.flow.vlans[0].tci), ntohs(nxm_match.wc.masks.vlans[0].tci)); } free(nxm_s); ofpbuf_uninit(&nxm); /* Convert to and from OXM. */ ofpbuf_init(&nxm, 0); nxm_match_len = oxm_put_match(&nxm, &match, OFP12_VERSION); nxm_s = oxm_match_to_string(&nxm, nxm_match_len); error = oxm_pull_match(&nxm, false, NULL, NULL, &nxm_match); printf("OXM: %s -> ", nxm_s); if (error) { printf("%s\n", ofperr_to_string(error)); } else { uint16_t vid = ntohs(nxm_match.flow.vlans[0].tci) & (VLAN_VID_MASK | VLAN_CFI); uint16_t mask = ntohs(nxm_match.wc.masks.vlans[0].tci) & (VLAN_VID_MASK | VLAN_CFI); printf("%04"PRIx16"/%04"PRIx16",", vid, mask); if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlans[0].tci)) { printf("%02d\n", vlan_tci_to_pcp(nxm_match.flow.vlans[0].tci)); } else { printf("--\n"); } } free(nxm_s); ofpbuf_uninit(&nxm); /* Convert to and from OpenFlow 1.0. */ ofputil_match_to_ofp10_match(&match, &of10_raw); ofputil_match_from_ofp10_match(&of10_raw, &of10_match); printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n", ntohs(of10_raw.dl_vlan), (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0, of10_raw.dl_vlan_pcp, (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0, ntohs(of10_match.flow.vlans[0].tci), ntohs(of10_match.wc.masks.vlans[0].tci)); /* Convert to and from OpenFlow 1.1. */ ofputil_match_to_ofp11_match(&match, &of11_raw); ofputil_match_from_ofp11_match(&of11_raw, &of11_match); printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n", ntohs(of11_raw.dl_vlan), (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0, of11_raw.dl_vlan_pcp, (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0, ntohs(of11_match.flow.vlans[0].tci), ntohs(of11_match.wc.masks.vlans[0].tci)); } /* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow * version. */ static void ofctl_print_error(struct ovs_cmdl_context *ctx) { enum ofperr error; int version; error = ofperr_from_name(ctx->argv[1]); if (!error) { ovs_fatal(0, "unknown error \"%s\"", ctx->argv[1]); } for (version = 0; version <= UINT8_MAX; version++) { const char *name = ofperr_domain_get_name(version); if (name) { int vendor = ofperr_get_vendor(error, version); int type = ofperr_get_type(error, version); int code = ofperr_get_code(error, version); if (vendor != -1 || type != -1 || code != -1) { printf("%s: vendor %#x, type %d, code %d\n", name, vendor, type, code); } } } } /* "encode-error-reply ENUM REQUEST": Encodes an error reply to REQUEST for the * error named ENUM and prints the error reply in hex. */ static void ofctl_encode_error_reply(struct ovs_cmdl_context *ctx) { const struct ofp_header *oh; struct ofpbuf request, *reply; enum ofperr error; error = ofperr_from_name(ctx->argv[1]); if (!error) { ovs_fatal(0, "unknown error \"%s\"", ctx->argv[1]); } ofpbuf_init(&request, 0); if (ofpbuf_put_hex(&request, ctx->argv[2], NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (request.size < sizeof(struct ofp_header)) { ovs_fatal(0, "Request too short"); } oh = request.data; if (request.size != ntohs(oh->length)) { ovs_fatal(0, "Request size inconsistent"); } reply = ofperr_encode_reply(error, request.data); ofpbuf_uninit(&request); ovs_hex_dump(stdout, reply->data, reply->size, 0, false); ofpbuf_delete(reply); } /* Usage: * ofp-print HEXSTRING [VERBOSITY] * ofp-print - [VERBOSITY] < HEXSTRING_FILE * ofp-print --raw - [VERBOSITY] < RAW_FILE * * Converts the hex digits in HEXSTRING into binary data, interpreting them as * an OpenFlow message, and prints the OpenFlow message on stdout, at VERBOSITY * (level 2 by default). With -, hex data is read from HEXSTRING_FILE, and * with --raw -, raw binary data is read from RAW_FILE. */ static void ofctl_ofp_print(struct ovs_cmdl_context *ctx) { int verbosity_ = ctx->argc > 2 ? atoi(ctx->argv[2]) : 2; struct ofpbuf packet; ofpbuf_init(&packet, 0); char *buffer = NULL; if (!strcmp(ctx->argv[1], "-")) { if (raw) { for (;;) { int c = getchar(); if (c == EOF) { break; } ofpbuf_put(&packet, &c, 1); } } else { struct ds line = DS_EMPTY_INITIALIZER; if (ds_get_line(&line, stdin)) { VLOG_FATAL("Failed to read stdin"); } buffer = ds_steal_cstr(&line); } } else { buffer = xstrdup(ctx->argv[1]); } if (buffer && ofpbuf_put_hex(&packet, buffer, NULL)[0] != '\0') { ovs_fatal(0, "trailing garbage following hex bytes"); } free(buffer); ofp_print(stdout, packet.data, packet.size, NULL, NULL, verbosity_); ofpbuf_uninit(&packet); } /* "encode-hello BITMAP...": Encodes each BITMAP as an OpenFlow hello message * and dumps each message in hex. */ static void ofctl_encode_hello(struct ovs_cmdl_context *ctx) { uint32_t bitmap = strtol(ctx->argv[1], NULL, 0); struct ofpbuf *hello; hello = ofputil_encode_hello(bitmap); ovs_hex_dump(stdout, hello->data, hello->size, 0, false); ofp_print(stdout, hello->data, hello->size, NULL, NULL, verbosity); ofpbuf_delete(hello); } static void ofctl_parse_key_value(struct ovs_cmdl_context *ctx) { for (size_t i = 1; i < ctx->argc; i++) { char *s = ctx->argv[i]; char *key, *value; int j = 0; while (ofputil_parse_key_value(&s, &key, &value)) { if (j++) { fputs(", ", stdout); } fputs(key, stdout); if (value[0]) { printf("=%s", value); } } putchar('\n'); } } /* "compose-packet [--pcap|--bare] [--bad-csum] FLOW [L7]": Converts the * OpenFlow flow specification FLOW to a packet with flow_compose() and prints * the hex bytes of the packet, with offsets, to stdout. * * With --pcap, prints the packet in pcap format, so that you can do something * like "ovs-ofctl --pcap compose-packet udp | tcpdump -vvvv -r-" to use * another tool to dump the packet contents. * * With --bare, prints the packet as a single bare hex string with no * spaces or offsets, so that you can pass the result directly to e.g. * "ovs-appctl netdev-dummy/receive vif $(ovs-ofctl compose-packet --bare * FLOW)" * * With --bad-csum, produces a packet with an invalid IP checksum. (For IPv4.) * * Regardless of the mode, the command also verifies that the flow extracted * from that packet matches the original FLOW. * * If L7 is specified, draws the L7 payload data from it, otherwise defaults to * 64 bytes of payload. */ static void ofctl_compose_packet(struct ovs_cmdl_context *ctx) { if (print_pcap && print_bare) { ovs_fatal(1, "--bare and --pcap are mutually exclusive"); } if (print_pcap && isatty(STDOUT_FILENO)) { ovs_fatal(1, "not writing pcap data to stdout; redirect to a file " "or pipe to tcpdump instead"); } struct flow flow1; char *error = parse_ofp_exact_flow(&flow1, NULL, NULL, ctx->argv[1], NULL); if (error) { ovs_fatal(0, "%s", error); } struct dp_packet p; memset(&p, 0, sizeof p); dp_packet_init(&p, 0); void *l7 = NULL; size_t l7_len = 64; if (ctx->argc > 2) { struct dp_packet payload; memset(&payload, 0, sizeof payload); dp_packet_init(&payload, 0); if (dp_packet_put_hex(&payload, ctx->argv[2], NULL)[0] != '\0') { ovs_fatal(0, "%s: trailing garbage in packet data", ctx->argv[2]); } l7_len = dp_packet_size(&payload); l7 = dp_packet_steal_data(&payload); } flow_compose(&p, &flow1, l7, l7_len, bad_csum); free(l7); if (print_pcap) { struct pcap_file *p_file = ovs_pcap_stdout(); ovs_pcap_write_header(p_file); ovs_pcap_write(p_file, &p); ovs_pcap_close(p_file); } else if (print_bare) { /* Binary to a bare hex string. */ for (int i = 0; i < dp_packet_size(&p); i++) { uint8_t val = ((uint8_t *) dp_packet_data(&p))[i]; /* Don't use ds_put_hex because it adds 0x prefix as well as * it doesn't guarantee an even number of payload characters, which * may be important elsewhere (e.g. in netdev-dummy/receive). */ printf("%02" PRIx8, val); } } else { ovs_hex_dump(stdout, dp_packet_data(&p), dp_packet_size(&p), 0, false); } struct flow flow2; flow_extract(&p, &flow2); flow2.in_port.ofp_port = OFPP_ANY; dp_packet_uninit(&p); if (!flow_equal(&flow1, &flow2)) { fprintf(stderr, "specified and extracted flows differ:\n"); fputs("specified: ", stderr); flow_print(stderr, &flow1, NULL); fputs("\nextracted: ", stderr); flow_print(stderr, &flow2, NULL); exit(1); } } /* "parse-packet" reads an Ethernet packet from stdin and prints it out its * extracted flow fields. */ static void ofctl_parse_packet(struct ovs_cmdl_context *ctx OVS_UNUSED) { char packet[65535]; ssize_t size = read(STDIN_FILENO, packet, sizeof packet); if (size < 0) { ovs_fatal(errno, "failed to read packet from stdin"); } /* Make a copy of the packet in allocated memory to better allow Valgrind * and Address Sanitizer to catch out-of-range access. */ void *packet_copy = xmemdup(packet, size); ofp_print_packet(stdout, packet_copy, size, 0); free(packet_copy); } static const struct ovs_cmdl_command all_commands[] = { { "show", "switch", 1, 1, ofctl_show, OVS_RO }, { "monitor", "switch [misslen] [invalid_ttl] [watch:[...]]", 1, 3, ofctl_monitor, OVS_RO }, { "snoop", "switch", 1, 1, ofctl_snoop, OVS_RO }, { "dump-desc", "switch", 1, 1, ofctl_dump_desc, OVS_RO }, { "dump-tables", "switch", 1, 1, ofctl_dump_tables, OVS_RO }, { "dump-table-features", "switch", 1, 1, ofctl_dump_table_features, OVS_RO }, { "dump-table-desc", "switch", 1, 1, ofctl_dump_table_desc, OVS_RO }, { "dump-flows", "switch", 1, 2, ofctl_dump_flows, OVS_RO }, { "dump-aggregate", "switch", 1, 2, ofctl_dump_aggregate, OVS_RO }, { "queue-stats", "switch [port [queue]]", 1, 3, ofctl_queue_stats, OVS_RO }, { "queue-get-config", "switch [port [queue]]", 1, 3, ofctl_queue_get_config, OVS_RO }, { "add-flow", "switch flow", 2, 2, ofctl_add_flow, OVS_RW }, { "add-flows", "switch file", 2, 2, ofctl_add_flows, OVS_RW }, { "mod-flows", "switch flow", 2, 2, ofctl_mod_flows, OVS_RW }, { "del-flows", "switch [flow]", 1, 2, ofctl_del_flows, OVS_RW }, { "replace-flows", "switch file", 2, 2, ofctl_replace_flows, OVS_RW }, { "diff-flows", "source1 source2", 2, 2, ofctl_diff_flows, OVS_RW }, { "add-meter", "switch meter", 2, 2, ofctl_add_meter, OVS_RW }, { "mod-meter", "switch meter", 2, 2, ofctl_mod_meter, OVS_RW }, { "del-meter", "switch meter", 1, 2, ofctl_del_meters, OVS_RW }, { "del-meters", "switch", 1, 2, ofctl_del_meters, OVS_RW }, { "dump-meter", "switch meter", 1, 2, ofctl_dump_meters, OVS_RO }, { "dump-meters", "switch", 1, 2, ofctl_dump_meters, OVS_RO }, { "meter-stats", "switch [meter]", 1, 2, ofctl_meter_stats, OVS_RO }, { "meter-features", "switch", 1, 1, ofctl_meter_features, OVS_RO }, { "packet-out", "switch \"in_port= packet= actions=...\"", 2, INT_MAX, ofctl_packet_out, OVS_RW }, { "dump-ports", "switch [port]", 1, 2, ofctl_dump_ports, OVS_RO }, { "dump-ports-desc", "switch [port]", 1, 2, ofctl_dump_ports_desc, OVS_RO }, { "mod-port", "switch iface act", 3, 3, ofctl_mod_port, OVS_RW }, { "mod-table", "switch mod", 3, 3, ofctl_mod_table, OVS_RW }, { "get-frags", "switch", 1, 1, ofctl_get_frags, OVS_RO }, { "set-frags", "switch frag_mode", 2, 2, ofctl_set_frags, OVS_RW }, { "probe", "target", 1, 1, ofctl_probe, OVS_RO }, { "ping", "target [n]", 1, 2, ofctl_ping, OVS_RO }, { "benchmark", "target n count", 3, 3, ofctl_benchmark, OVS_RO }, { "dump-ipfix-bridge", "switch", 1, 1, ofctl_dump_ipfix_bridge, OVS_RO }, { "dump-ipfix-flow", "switch", 1, 1, ofctl_dump_ipfix_flow, OVS_RO }, { "ct-flush-zone", "switch zone", 2, 2, ofctl_ct_flush_zone, OVS_RW }, { "ct-flush", "switch [zone=N] [mark=X[/M]] [labels=Y[/N]] " "[ct-orig-tuple [ct-reply-tuple]]", 1, 6, ofctl_ct_flush, OVS_RW }, { "ofp-parse", "file", 1, 1, ofctl_ofp_parse, OVS_RW }, { "ofp-parse-pcap", "pcap", 1, INT_MAX, ofctl_ofp_parse_pcap, OVS_RW }, { "add-group", "switch group", 1, 2, ofctl_add_group, OVS_RW }, { "add-groups", "switch file", 2, 2, ofctl_add_groups, OVS_RW }, { "mod-group", "switch group", 1, 2, ofctl_mod_group, OVS_RW }, { "del-groups", "switch [group]", 1, 2, ofctl_del_groups, OVS_RW }, { "insert-buckets", "switch [group]", 1, 2, ofctl_insert_bucket, OVS_RW }, { "remove-buckets", "switch [group]", 1, 2, ofctl_remove_bucket, OVS_RW }, { "dump-groups", "switch [group]", 1, 2, ofctl_dump_group_desc, OVS_RO }, { "dump-group-stats", "switch [group]", 1, 2, ofctl_dump_group_stats, OVS_RO }, { "dump-group-features", "switch", 1, 1, ofctl_dump_group_features, OVS_RO }, { "bundle", "switch file", 2, 2, ofctl_bundle, OVS_RW }, { "add-tlv-map", "switch map", 2, 2, ofctl_add_tlv_map, OVS_RO }, { "del-tlv-map", "switch [map]", 1, 2, ofctl_del_tlv_map, OVS_RO }, { "dump-tlv-map", "switch", 1, 1, ofctl_dump_tlv_map, OVS_RO }, { "help", NULL, 0, INT_MAX, ofctl_help, OVS_RO }, { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands, OVS_RO }, /* Undocumented commands for testing. */ { "parse-flow", NULL, 1, 1, ofctl_parse_flow, OVS_RW }, { "parse-flows", NULL, 1, 1, ofctl_parse_flows, OVS_RW }, { "parse-group", NULL, 1, 1, ofctl_parse_group, OVS_RW }, { "parse-nx-match", NULL, 0, 0, ofctl_parse_nxm, OVS_RW }, { "parse-nxm", NULL, 0, 0, ofctl_parse_nxm, OVS_RW }, { "parse-oxm", NULL, 1, 1, ofctl_parse_oxm, OVS_RW }, { "parse-actions", NULL, 1, 1, ofctl_parse_actions, OVS_RW }, { "parse-instructions", NULL, 1, 1, ofctl_parse_instructions, OVS_RW }, { "parse-ofp10-match", NULL, 0, 0, ofctl_parse_ofp10_match, OVS_RW }, { "parse-ofp11-match", NULL, 0, 0, ofctl_parse_ofp11_match, OVS_RW }, { "parse-pcap", NULL, 1, INT_MAX, ofctl_parse_pcap, OVS_RW }, { "check-vlan", NULL, 2, 2, ofctl_check_vlan, OVS_RW }, { "print-error", NULL, 1, 1, ofctl_print_error, OVS_RW }, { "encode-error-reply", NULL, 2, 2, ofctl_encode_error_reply, OVS_RW }, { "ofp-print", NULL, 1, 2, ofctl_ofp_print, OVS_RW }, { "encode-hello", NULL, 1, 1, ofctl_encode_hello, OVS_RW }, { "parse-key-value", NULL, 1, INT_MAX, ofctl_parse_key_value, OVS_RW }, { "compose-packet", NULL, 1, 2, ofctl_compose_packet, OVS_RO }, { "parse-packet", NULL, 0, 0, ofctl_parse_packet, OVS_RO }, { NULL, NULL, 0, 0, NULL, OVS_RO }, }; static const struct ovs_cmdl_command *get_all_commands(void) { return all_commands; } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-parse-backtrace.in000077500000000000000000000054721514270232600251720ustar00rootroot00000000000000#! @PYTHON3@ # # Copyright (c) 2012 Nicira, Inc. # # 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. import optparse import os import re import subprocess import sys addr2line_cache = {} # None if addr2line is missing or broken. def addr2line(binary, addr): global addr2line_cache if addr2line_cache is None: return "" if addr in addr2line_cache: return addr2line_cache[addr] cmd = ["addr2line", "-f", "-s", "-e", binary, addr] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) lines = proc.stdout.readlines() failed = proc.returncode except OSError: failed = True if failed: addr2line_cache = None return "" lines = [l.strip() for l in lines] return " ".join(lines) def main(): parser = optparse.OptionParser(version='@VERSION@@VERSION_SUFFIX@', usage="usage: %prog [binary]", description="""\ Parses the output of ovs-appctl backtrace producing a more human readable result. Expected usage is for ovs-appctl backtrace to be piped in.""") options, args = parser.parse_args() if len(args) > 1: parser.print_help() sys.exit(1) if len(args) == 1: binary = args[0] else: binary = "@sbindir@/ovs-vswitchd" debug = "/usr/lib/debug%s.debug" % binary if os.path.exists(debug): binary = debug print("Binary: %s\n" % binary) stdin = sys.stdin.read() traces = [] for trace in stdin.strip().split("\n\n"): lines = trace.splitlines() match = re.search(r'Count (\d+)', lines[0]) if match: count = int(match.group(1)) else: count = 0 traces.append((lines[1:], count)) traces = sorted(traces, key=(lambda x: x[1]), reverse=True) for lines, count in traces: longest = max(len(l) for l in lines) print("Backtrace Count: %d" % count) for line in lines: match = re.search(r'\[(0x.*)]', line) if match: print("%s %s" % (line.ljust(longest), addr2line(binary, match.group(1)))) else: print(line) print() if __name__ == "__main__": main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-pcap.1.in000066400000000000000000000011731514270232600232140ustar00rootroot00000000000000.so lib/ovs.tmac .TH ovs\-pcap 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME ovs\-pcap \- print packets from a pcap file as hex . .SH SYNOPSIS \fBovs\-pcap\fR \fIfile\fR .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-pcap\fR program reads the pcap \fIfile\fR named on the command line and prints each packet's contents as a sequence of hex digits on a line of its own. This format is suitable for use with the \fBofproto/trace\fR command supported by \fBovs\-vswitchd\fR(8). . .SH "OPTIONS" .so lib/common.man . .SH "SEE ALSO" . .BR ovs\-vswitchd (8), .BR ovs\-tcpundump (1), .BR tcpdump (8), .BR wireshark (8). openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-pcap.in000077500000000000000000000065461514270232600230710ustar00rootroot00000000000000#! @PYTHON3@ # # Copyright (c) 2010 Nicira, Inc. # # 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. import binascii import getopt import struct import sys class PcapException(Exception): pass class PcapReader(object): def __init__(self, file_name): self.file = open(file_name, "rb") header = self.file.read(24) if len(header) != 24: raise PcapException("end of file reading pcap header") magic, version, thiszone, sigfigs, snaplen, network = \ struct.unpack(">6I", header) if magic == 0xa1b2c3d4 or magic == 0xa1b23c4d: self.header_format = ">4I" elif magic == 0xd4c3b2a1 or magic == 0x4d3cb2a1: self.header_format = "<4I" else: raise PcapException("bad magic %u reading pcap file " "(expected 0xa1b2c3d4, 0xa1b23c4d, 0x4d3cb2a1 or " "0xd4c3b2a1)" % magic) def read(self): header = self.file.read(16) if len(header) == 0: return None elif len(header) != 16: raise PcapException("end of file within pcap record header") ts_sec, ts_usec, incl_len, orig_len = struct.unpack(self.header_format, header) packet = self.file.read(incl_len) if len(packet) != incl_len: raise PcapException("end of file reading pcap packet data") return packet argv0 = sys.argv[0] def usage(): print("""\ %(argv0)s: print pcap file packet data as hex usage: %(argv0)s FILE where FILE is a PCAP file. The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0}) sys.exit(0) if __name__ == "__main__": try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['help', 'version']) except getopt.GetoptError as geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print("ovs-pcap (Open vSwitch) @VERSION@@VERSION_SUFFIX@") else: sys.exit(0) if len(args) != 1: sys.stderr.write("%s: exactly 1 non-option argument required " "(use --help for help)\n" % argv0) sys.exit(1) reader = PcapReader(args[0]) while True: packet = reader.read() if packet is None: break print(binascii.hexlify(packet).decode().strip()) except PcapException as e: sys.stderr.write("%s: %s\n" % (argv0, e)) sys.exit(1) # Local variables: # mode: python # End: openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-pipegen.py000077500000000000000000000073601514270232600236120ustar00rootroot00000000000000#! /usr/bin/env python3 # Copyright (c) 2013, 2014, 2015, 2020 Nicira, Inc. # # 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. import argparse import random import sys import textwrap def flow_str(stage, match, action, priority=32768): mtd_match = "metadata=%d" % stage if match: mtd_match += "," + match return "priority=%d %s,actions=%s" % (priority, mtd_match, action) def resubmit(nxt): return "load:%d->OXM_OF_METADATA[],resubmit(,0)" % nxt def rand_ip_mask(): return ("%d.%d.%d.%d" % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)), random.choice([8, 16, 24, 32])) def rand_bool(): return bool(random.randint(0, 1)) def l2(stage, action): mac = ["%x" % random.randint(0, 2 ** 8 - 1) for x in range(6)] mac = [x.zfill(2) for x in mac] mac = ":".join(mac) return flow_str(stage, "dl_dst=%s" % mac, action) def l3(stage, action): ip, mask = rand_ip_mask() return flow_str(stage, "ip,ip_dst=%s/%d" % (ip, mask), action, priority=mask) def l4(stage, action): match = "tcp" if rand_bool(): match += ",ip_src=%s/%d" % rand_ip_mask() if rand_bool(): match += ",ip_dst=%s/%d" % rand_ip_mask() src_dst = "tp_src" if rand_bool() else "tp_dst" match += ",%s=%d" % (src_dst, random.randint(1024, 2 ** 16 - 1)) return flow_str(stage, match, action) def pipeline(size): pipeline = [l2, l3, l4, l2] flows = [] for stage in range(len(pipeline)): action = resubmit(stage + 1) flows += [pipeline[stage](stage, action) for _ in range(size)] flows.append(flow_str(stage, "", action, priority=1)) flows.append(flow_str(len(pipeline), "", "in_port")) for f in flows: print(f) def main(): description = textwrap.dedent( """ Generate a test OpenFlow pipeline. Open vSwitch relies heavily on flow caching to get good performance for packet processing. While on average, this produces good results, performance is heavily depedent on the slow path OpenFlow tables, and how they're translated into datapath megaflows. For this reason, when doing performance testing it's important to run with "realistic" OpenFlow tables to ensure results will stand up in the real world. This script generates a simple OpenFlow pipeline intended to simulate realistic network virtualization workloads. All traffic received is run through a series of OpenFlow tables designed to simulate a logical switch, router, and firewall, before forwarded back on the in_port. """) epilog = textwrap.dedent( """ typical usage: ovs-ofctl del-flows bridge \\ && %s | ovs-ofctl add-flows bridge - \\ && ovs-ofctl dump-flows bridge """ % sys.argv[0]) parser = argparse.ArgumentParser(description=description, epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--size", dest="size", default=1000, help="Size (rules) of each OpenFlow table.") args = parser.parse_args() pipeline(int(args.size)) if __name__ == "__main__": main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-pki.in000077500000000000000000000414041514270232600227210ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. # # 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. set -e pkidir='@PKIDIR@' command= prev= force=no batch=no unique_name=no log='@LOGDIR@/ovs-pki.log' keytype=rsa bits=3072 # OS-specific compatibility routines case $(uname -s) in FreeBSD|NetBSD|Darwin) file_mod_epoch() { stat -r "$1" | awk '{print $10}' } file_mod_date() { stat -f '%Sm' "$1" } sha1sum() { sha1 "$@" } ;; *) file_mod_epoch() { date -r "$1" +%s } file_mod_date() { date -r "$1" } ;; esac case $(uname -s) in MINGW*|MSYS*) chmod() { local PERM=$1 local FILE=$2 local INH= if test -d "${FILE}"; then # Inheritance rules for folders: apply to a folder itself, # subfolders and files within. INH='(OI)(CI)' fi case "${PERM}" in *700 | *600) # Reset all own and inherited ACEs and grant full access to the # "Creator Owner". We're giving full access even for 0600, # because it doesn't matter for a use case of ovs-pki. icacls "${FILE}" /inheritance:r /grant:r "*S-1-3-0:${INH}F" ;; *750) # Reset all own and inherited ACEs, grant full access to the # "Creator Owner" and a read+execute access to the "Creator Group". icacls "${FILE}" /inheritance:r /grant:r \ "*S-1-3-0:${INH}F" "*S-1-3-1:${INH}RX" ;; *) echo >&2 "Unable to set ${PERM} mode for ${FILE}." exit 1 ;; esac } mkdir() { ARG_P= PERM= for arg; do shift case ${arg} in -m?*) PERM=${arg#??} continue ;; -m) PERM=$1 shift continue ;; -p) ARG_P=-p continue ;; *) set -- "$@" "${arg}" ;; esac done command mkdir ${ARG_P} $@ if [ ${PERM} ]; then for dir; do shift chmod ${PERM} ${dir} done fi } ;; esac for option; do # This option-parsing mechanism borrowed from a Autoconf-generated # configure script under the following license: # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # If the previous option needs an argument, assign it. if test -n "$prev"; then eval $prev=\$option prev= continue fi case $option in *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;; *) optarg=yes ;; esac case $dashdash$option in --) dashdash=yes ;; -h|--help) cat <&2 exit 1 ;; *) if test -z "$command"; then command=$option elif test -z "${arg1+set}"; then arg1=$option elif test -z "${arg2+set}"; then arg2=$option else echo "$option: only two arguments may be specified" >&2 exit 1 fi ;; esac shift done if test -n "$prev"; then option=--`echo $prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $option" >&2 { (exit 1); exit 1; }; } fi if test -z "$command"; then echo "$0: missing command name; use --help for help" >&2 exit 1 fi if test "$keytype" != rsa && test "$keytype" != dsa; then echo "$0: argument to -k or --key must be rsa or dsa" >&2 exit 1 fi if test "$bits" -lt 2048; then echo "$0: argument to -B or --bits must be at least 2048" >&2 exit 1 fi if test -z "$dsaparam"; then dsaparam=$pkidir/dsaparam.pem fi case $log in /* | ?:[\\/]*) ;; *) log=`pwd`/$log ;; esac logdir=$(dirname "$log") if test ! -d "$logdir"; then mkdir -p -m750 "$logdir" 2>/dev/null || true if test ! -d "$logdir"; then echo "$0: log directory $logdir does not exist and cannot be created" >&2 exit 1 fi fi if test "$command" = "init"; then if test -e "$pkidir" && test "$force" != "yes"; then echo "$0: $pkidir already exists and --force not specified" >&2 exit 1 fi if test ! -d "$pkidir"; then mkdir -p "$pkidir" fi cd "$pkidir" exec 3>>$log if test $keytype = dsa && test ! -e dsaparam.pem; then echo "Generating DSA parameters, please wait..." >&2 openssl dsaparam -out dsaparam.pem $bits 1>&3 2>&3 fi # Get the current date to add some uniqueness to this certificate curr_date=`date +"%Y %b %d %T"` # Create the CAs. for ca in controllerca switchca; do echo "Creating $ca..." >&2 oldpwd=`pwd` mkdir -p $ca cd $ca mkdir -p certs crl newcerts mkdir -p -m 0700 private touch index.txt test -e crlnumber || echo 01 > crlnumber test -e serial || echo 01 > serial # Put DSA parameters in directory. if test $keytype = dsa && test ! -e dsaparam.pem; then cp ../dsaparam.pem . fi # Write CA configuration file. if test ! -e ca.cnf; then sed "s/@ca@/$ca/g;s/@curr_date@/$curr_date/g" > ca.cnf <<'EOF' [ req ] prompt = no distinguished_name = req_distinguished_name [ req_distinguished_name ] C = US ST = CA L = Palo Alto O = Open vSwitch OU = @ca@ CN = OVS @ca@ CA Certificate (@curr_date@) [ ca ] default_ca = the_ca [ the_ca ] dir = . # top dir database = $dir/index.txt # index file. new_certs_dir = $dir/newcerts # new certs dir certificate = $dir/cacert.pem # The CA cert serial = $dir/serial # serial no file private_key = $dir/private/cakey.pem# CA private key RANDFILE = $dir/private/.rand # random number file default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha512 # message digest to use policy = policy # default policy email_in_dn = no # Don't add the email into cert DN name_opt = ca_default # Subject name display option cert_opt = ca_default # Certificate display option copy_extensions = copy # Copy extensions from request unique_subject = no # Allow certs with duplicate subjects # For the CA policy [ policy ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the x509v3 extension [ ca_cert ] basicConstraints=CA:true [ usr_cert ] basicConstraints=CA:false EOF fi # Create certificate authority. if test $keytype = dsa; then newkey=dsa:dsaparam.pem else newkey=rsa:$bits fi openssl req -config ca.cnf -nodes \ -newkey $newkey -keyout private/cakey.pem -out careq.pem \ 1>&3 2>&3 openssl ca -config ca.cnf -create_serial \ -extensions ca_cert -out cacert.pem \ -days 3650 -batch -keyfile private/cakey.pem -selfsign \ -infiles careq.pem 1>&3 2>&3 chmod 0600 private/cakey.pem cd "$oldpwd" done exit 0 fi one_arg() { if test -z "$arg1" || test -n "$arg2"; then echo "$0: $command must have exactly one argument; use --help for help" >&2 exit 1 fi } one_or_two_args() { if test -z "$arg1"; then echo "$0: $command must have one or two arguments; use --help for help" >&2 exit 1 fi } must_not_exist() { if test -e "$1" && test "$force" != "yes"; then echo "$0: $1 already exists and --force not supplied" >&2 exit 1 fi } make_tmpdir() { TMP=/tmp/ovs-pki.tmp$$ rm -rf $TMP trap "rm -rf $TMP" 0 mkdir -m 0700 $TMP } fingerprint() { file=$1 name=${1-$2} date=$(file_mod_date "$file") if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then fingerprint=$(openssl x509 -noout -in "$file" -fingerprint | sed 's/SHA1 Fingerprint=//' | tr -d ':') else fingerprint=$(sha1sum "$file" | awk '{print $1}') fi printf "$name\\t$date\\n" case $file in $fingerprint*) printf "\\t(correct fingerprint in filename)\\n" ;; *) printf "\\tfingerprint $fingerprint\\n" ;; esac } verify_fingerprint() { fingerprint "$@" if test $batch != yes; then echo "Does fingerprint match? (yes/no)" read answer if test "$answer" != yes; then echo "Match failure, aborting" >&2 exit 1 fi fi } check_type() { if test x = x"$1"; then type=switch elif test "$1" = switch || test "$1" = controller; then type=$1 else echo "$0: type argument must be 'switch' or 'controller'" >&2 exit 1 fi } parse_age() { number=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\1/') unit=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\2/') case $unit in s) factor=1 ;; min) factor=60 ;; h) factor=3600 ;; day) factor=86400 ;; *) echo "$1: age not in the form Ns, Nmin, Nh, Nday (e.g. 1day)" >&2 exit 1 ;; esac echo $(($number * $factor)) } must_exist() { if test ! -e "$1"; then echo "$0: $1 does not exist" >&2 exit 1 fi } pkidir_must_exist() { if test ! -e "$pkidir"; then echo "$0: $pkidir does not exist (need to run 'init' or use '--dir'?)" >&2 exit 1 elif test ! -d "$pkidir"; then echo "$0: $pkidir is not a directory" >&2 exit 1 fi } make_request() { must_not_exist "$arg1-privkey.pem" must_not_exist "$arg1-req.pem" make_tmpdir if test $unique_name != yes; then # Use uuidgen or date to create unique subject DNs. unique=`(uuidgen) 2>/dev/null` || unique=`date +"%Y %b %d %T"` cn="$arg1 id:$unique" else cn="$arg1" fi cat > "$TMP/req.cnf" <&3 2>&3 || exit $? else must_exist "$dsaparam" (umask 077 && openssl gendsa -out "$TMP/privkey.pem" "$dsaparam") \ 1>&3 2>&3 || exit $? fi # Windows: applying permissions (ACEs) to the file itself, just in case. # 'mv' should technically preserve all the inherited ACEs from a TMP # folder, but it's better to not rely on that. chmod 0600 "$TMP/privkey.pem" mv "$TMP/privkey.pem" "$1-privkey.pem" openssl req -config "$TMP/req.cnf" -new -text \ -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3 } sign_request() { must_exist "$1" must_not_exist "$2" pkidir_must_exist case "$1" in /* | ?:[\\/]*) request_file="$1" ;; *) request_file="`pwd`/$1" ;; esac (cd "$pkidir/${type}ca" && openssl ca -config ca.cnf -extensions usr_cert -batch -in "$request_file") \ > "$2.tmp$$" 2>&3 mv "$2.tmp$$" "$2" } glob() { files=$(echo $1) if test "$files" != "$1"; then echo "$files" fi } exec 3>>$log || true if test "$command" = req; then one_arg make_request "$arg1" fingerprint "$arg1-req.pem" elif test "$command" = sign; then one_or_two_args check_type "$arg2" verify_fingerprint "$arg1-req.pem" sign_request "$arg1-req.pem" "$arg1-cert.pem" elif test "$command" = req+sign; then one_or_two_args check_type "$arg2" pkidir_must_exist make_request "$arg1" sign_request "$arg1-req.pem" "$arg1-cert.pem" fingerprint "$arg1-req.pem" elif test "$command" = verify; then one_or_two_args must_exist "$arg1-cert.pem" check_type "$arg2" pkidir_must_exist openssl verify -CAfile "$pkidir/${type}ca/cacert.pem" "$arg1-cert.pem" elif test "$command" = fingerprint; then one_arg fingerprint "$arg1" elif test "$command" = self-sign; then one_arg must_exist "$arg1-req.pem" must_exist "$arg1-privkey.pem" must_not_exist "$arg1-cert.pem" make_tmpdir cat > "$TMP/v3.ext" <&3 || exit $? else echo "$0: $command command unknown; use --help for help" >&2 exit 1 fi openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-save000077500000000000000000000131031514270232600224620ustar00rootroot00000000000000#! /bin/sh # Copyright (c) 2011, 2013, 2016 Nicira, Inc. # # 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. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 usage() { UTIL=$(basename $0) cat < /dev/null 2>&1; then :; else echo "$0: ip not found in $PATH" >&2 exit 1 fi if test "$#" = 0; then exit 0 fi devs="$@" for dev in $devs; do state=`ip link show dev $dev` || continue echo "# $dev" # Link state (Ethernet addresses, up/down, ...) linkcmd= case $state in *"state UP"* | *[,\<]"UP"[,\>]* ) linkcmd="$linkcmd up" ;; *"state DOWN"*) linkcmd="$linkcmd down" ;; esac if expr "$state" : '.*\bdynamic\b' > /dev/null; then linkcmd="$linkcmd dynamic" fi if qlen=`expr "$state" : '.*qlen \([0-9]\+\)'`; then linkcmd="$linkcmd txqueuelen $qlen" fi if hwaddr=`expr "$state" : '.*link/ether \([^ ]*\)'`; then linkcmd="$linkcmd address $hwaddr" fi if brd=`expr "$state" : '.*brd \([^ ]*\)'`; then linkcmd="$linkcmd broadcast $brd" fi if mtu=`expr "$state" : '.*mtu \([0-9]\+\)'`; then linkcmd="$linkcmd mtu $mtu" fi if test -n "$linkcmd"; then echo ip link set dev $dev down # Required to change hwaddr. echo ip link set dev $dev $linkcmd fi move_ip_address $dev $dev move_ip_routes $dev $dev echo done if (iptables-save) > /dev/null 2>&1; then echo "# global" echo "iptables-restore <<'EOF'" iptables-save echo "EOF" else echo "# iptables-save not found in $PATH, not saving iptables state" fi } get_highest_ofp_version() { ovs-vsctl get bridge "$1" protocols | \ sed 's/[][]//g' | sed 's/\ //g' | \ awk -F ',' '{ print (NF>0)? $(NF) : "OpenFlow14" }' } save_flows () { if (ovs-ofctl --version) > /dev/null 2>&1; then :; else echo "$0: ovs-ofctl not found in $PATH" >&2 exit 1 fi # OVS 2.7 and earlier do not enable OpenFlow 1.4 (by default) and lack # other features needed to save and restore flows. Don't try. case `ovs-appctl version | sed 1q` in "ovs-vswitchd (Open vSwitch) 1."*.*) return ;; "ovs-vswitchd (Open vSwitch) 2."[0-7].*) return ;; esac workdir=$(mktemp -d "${TMPDIR:-/tmp}/ovs-save.XXXXXXXXXX") for bridge in "$@"; do # Get the highest enabled OpenFlow version ofp_version=$(get_highest_ofp_version "$bridge") printf "%s" "ovs-ofctl -O $ofp_version add-tlv-map ${bridge} '" ovs-ofctl dump-tlv-map ${bridge} -O $ofp_version | \ awk '/^ *0x/ {if (cnt != 0) printf ","; \ cnt++;printf "{class="$1",type="$2",len="$3"}->"$4}' echo "'" # If possible use OpenFlow 1.4 atomic bundle txn for flows and groups [ ${ofp_version#OpenFlow} -ge 14 ] && bundle=" --bundle" || bundle="" echo "ovs-ofctl -O $ofp_version add-groups ${bridge} \ \"$workdir/$bridge.groups.dump\" ${bundle}" echo "ovs-ofctl -O $ofp_version replace-flows ${bridge} \ \"$workdir/$bridge.flows.dump\" ${bundle}" ovs-ofctl -O $ofp_version dump-groups "$bridge" | \ sed -e '/^OFPST_GROUP_DESC/d' \ -e '/^NXST_GROUP_DESC/d' > \ "$workdir/$bridge.groups.dump" ovs-ofctl -O $ofp_version dump-flows --no-names --no-stats "$bridge" | \ sed -e '/NXST_FLOW/d' \ -e '/OFPST_FLOW/d' \ -e 's/\(idle\|hard\)_age=[^,]*,//g' \ -e 's/igmp_type/tp_src/g' \ -e 's/igmp_code/tp_dst/g' \ -e 's/igmp/ip,nw_proto=2/g' > \ "$workdir/$bridge.flows.dump" done echo "rm -rf \"$workdir\"" } while [ $# -ne 0 ] do case $1 in "save-flows") shift save_flows "$@" exit 0 ;; "save-interfaces") shift save_interfaces "$@" exit 0 ;; -h | --help) usage exit 0 ;; *) echo >&2 "$0: unknown command \"$1\" (use --help for help)" exit 1 ;; esac done exit 0 openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-sim.in000077500000000000000000000170711514270232600227310ustar00rootroot00000000000000#! /usr/bin/env bash # # Copyright (c) 2013, 2015, 2016 Nicira, Inc. # # 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. set -e sim_builddir='@abs_builddir@'; export sim_builddir sim_srcdir='@abs_top_srcdir@'; export sim_srcdir interactive=false scripts= for option; do case $option in -h|--help) cat <&2 exit 1 ;; *) case $option in /*) ;; *) option=`pwd`/$option ;; esac scripts="$scripts $option" ;; esac shift done if test -z "$scripts"; then interactive=: fi # Check that we've got proper builddir and srcdir. if test ! -e "$sim_builddir"/vswitchd/ovs-vswitchd; then echo "$sim_builddir/vswitchd/ovs-vswitchd does not exist (need to run \"make\"?)" >&2 exit 1 fi if test ! -e "$sim_srcdir"/README.rst; then echo "$sim_srcdir/README.rst does not exist" >&2 exit 1 fi # Put built tools early in $PATH. PATH=$sim_builddir/ovsdb:$sim_builddir/vswitchd:$sim_builddir/utilities:$PATH export PATH rm -rf sandbox mkdir sandbox cd sandbox sim_base=`pwd`; export sim_base trap_signals() { for signal in 0 1 2 3 13 14 15; do trap " set +e cd '$sim_base' && (kill \`cat */*.pid\`) >/dev/null 2>&1 trap - $signal kill -$signal $$" $signal done } export -f trap_signals trap_signals sim_setvars() { sandbox=$1 OVS_RUNDIR=$sim_base/$1; export OVS_RUNDIR OVS_LOGDIR=$sim_base/$1; export OVS_LOGDIR OVS_DBDIR=$sim_base/$1; export OVS_DBDIR OVS_SYSCONFDIR=$sim_base/$1; export OVS_SYSCONFDIR PS1="|$1: $sim_PS1" } export -f sim_setvars ovs-vsctl () { command ovs-vsctl -vsyslog:off "$@"; }; export -f ovs-vsctl vtep-ctl () { command vtep-ctl -vsyslog:off "$@"; }; export -f vtep-ctl as() { case $# in 0) echo >&2 "$FUNCNAME: missing arguments (use --help for help)" return 1 ;; 1) if test "$1" != --help; then sim_setvars $1 else cat <&2 "$FUNCNAME: missing argument (use --help for help)" return 1 fi set X $1; shift if test $# != 1; then echo >&2 "$FUNCNAME: sandbox name must be a single word" return 1 fi if test -e "$sim_base/$1"; then echo >&2 "$1 already exists" return 1 fi # Create sandbox. mkdir "$sim_base"/$1 || return 1 daemon_opts="--detach --no-chdir --pidfile -vconsole:off -vsyslog:off --log-file" # Create database and start ovsdb-server. touch $sim_base/$1/.conf.db.~lock~ as $1 ovsdb-tool create $sim_base/$1/conf.db "$sim_srcdir/vswitchd/vswitch.ovsschema" as $1 ovsdb-server --remote=punix:"$sim_base"/$1/db.sock $daemon_opts # Initialize database. as $1 ovs-vsctl --no-wait -- init # Start ovs-vswitchd. as $1 ovs-vswitchd --enable-dummy=system -vvconn -vnetdev_dummy $daemon_opts } export -f sim_add net_add() { if test "$1" = --help; then cat <&2 "$FUNCNAME: missing argument (use --help for help)" return 1 fi as main ovs-vsctl add-br "$1" } export -f net_add net_attach() { if test "$1" = --help; then cat <&2 "$FUNCNAME: wrong number of arguments (use --help for help)" return 1 fi if test $sandbox = main; then echo >&2 "$FUNCNAME: can only attach interconnection networks to sandboxes other than main" return 1 fi local net=$1 bridge=$2 port=${sandbox}_$bridge as main ovs-vsctl \ -- add-port $net "$port" \ -- set Interface "$port" options:pstream="punix:$sim_base/main/$port.sock" options:rxq_pcap="$sim_base/main/$port-rx.pcap" options:tx_pcap="$sim_base/main/$port-tx.pcap" options:header=extended ovs-vsctl \ -- set Interface $bridge options:tx_pcap="$sim_base/$sandbox/$bridge-tx.pcap" options:rxq_pcap="$sim_base/$sandbox/$bridge-rx.pcap" \ -- add-port $bridge ${bridge}_$net \ -- set Interface ${bridge}_$net options:stream="unix:$sim_base/main/$port.sock" options:rxq_pcap="$sim_base/$sandbox/${bridge}_$net-rx.pcap" options:tx_pcap="$sim_base/$sandbox/${bridge}_$net-tx.pcap" options:header=extended } export -f net_attach # Easy access to OVS manpages. mkdir $sim_base/man mandir=`cd $sim_base/man && pwd` (cd "$sim_builddir" && ${MAKE-make} install-man install-man-rst mandir=$mandir EXTRA_RST_MANPAGES=ovs-sim.1.rst >/dev/null) MANPATH=$mandir:; export MANPATH export scripts export interactive rc=' if [ -f /etc/bashrc ]; then . /etc/bashrc fi if [ -f ~/.bashrc ]; then . ~/.bashrc fi trap_signals sim_PS1=$PS1 sim_add main as main for script in $scripts; do . $script || exit $? done $interactive || exit 0 cat < IFNAMSIZ_LINUX - 2: return "ovsmi%06d" % randint(1, 999999) return "mi%s" % interface_name _make_taps['linux'] = _install_dst_if_linux _make_taps['linux2'] = _install_dst_if_linux _del_taps['linux'] = _remove_dst_if_linux _del_taps['linux2'] = _remove_dst_if_linux _make_mirror_name['linux'] = _make_linux_mirror_name _make_mirror_name['linux2'] = _make_linux_mirror_name def username(): return pwd.getpwuid(os.getuid())[0] def usage(): print("""\ %(prog)s: Open vSwitch tcpdump helper. usage: %(prog)s -i interface [TCPDUMP OPTIONS] where TCPDUMP OPTIONS represents the options normally passed to tcpdump. The following options are available: -h, --help display this help message -V, --version display version information --db-sock A connection string to reach the Open vSwitch ovsdb-server. Default 'unix:@RUNDIR@/db.sock' --dump-cmd Command to use for tcpdump (default 'tcpdump') -i, --interface Open vSwitch interface to mirror and tcpdump --mirror-to The name for the mirror port to use (optional) Default 'miINTERFACE' --span If specified, mirror all ports (optional) --filter Set an OpenFlow formatted preselection filter """ % {'prog': sys.argv[0]}) sys.exit(0) class OVSDBException(Exception): pass class OVSDB(object): @staticmethod def wait_for_db_change(idl): seq = idl.change_seqno stop = time.time() + 10 while idl.change_seqno == seq and not idl.run(): poller = Poller() idl.wait(poller) poller.block() if time.time() >= stop: raise Exception('Retry Timeout') def __init__(self, db_sock): self._db_sock = db_sock self._txn = None schema = self._get_schema() schema.register_all() self._idl_conn = idl.Idl(db_sock, schema) OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB def close_idl(self): self._idl_conn.close() def _get_schema(self): error, strm = Stream.open_block(Stream.open(self._db_sock)) if error: raise Exception("Unable to connect to %s" % self._db_sock) rpc = jsonrpc.Connection(strm) req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch']) error, resp = rpc.transact_block(req) rpc.close() if error or resp.error: raise Exception('Unable to retrieve schema.') return idl.SchemaHelper(None, resp.result) def get_table(self, table_name): return self._idl_conn.tables[table_name] def _start_txn(self): if self._txn is not None: raise OVSDBException("ERROR: A transaction was started already") self._idl_conn.change_seqno += 1 self._txn = idl.Transaction(self._idl_conn) return self._txn def _complete_txn(self, try_again_fn): if self._txn is None: raise OVSDBException("ERROR: Not in a transaction") status = self._txn.commit_block() if status is idl.Transaction.TRY_AGAIN: if self._idl_conn._session.rpc.status != 0: self._idl_conn.force_reconnect() OVSDB.wait_for_db_change(self._idl_conn) return try_again_fn(self) elif status is idl.Transaction.ERROR: return False def _find_row(self, table_name, find): return next( (row for row in self.get_table(table_name).rows.values() if find(row)), None) def _find_row_by_name(self, table_name, value): return self._find_row(table_name, lambda row: row.name == value) def port_exists(self, port_name): return bool(self._find_row_by_name('Port', port_name)) def port_bridge(self, port_name): try: port = self._find_row_by_name('Port', port_name) br = self._find_row('Bridge', lambda x: port in x.ports) return br.name except Exception: raise OVSDBException('Unable to find port %s bridge' % port_name) def interface_mtu(self, intf_name): try: intf = self._find_row_by_name('Interface', intf_name) if intf is None: mtu = 1500 port = self._find_row_by_name('Port', intf_name) for intf in port.interfaces: if mtu < intf.mtu[0]: mtu = intf.mtu[0] return mtu return intf.mtu[0] except Exception: return None def interface_exists(self, intf_name): return bool(self._find_row_by_name('Interface', intf_name)) def mirror_exists(self, mirror_name): return bool(self._find_row_by_name('Mirror', mirror_name)) def interface_uuid(self, intf_name): row = self._find_row_by_name('Interface', intf_name) if bool(row): return row.uuid raise OVSDBException('No such interface: %s' % intf_name) def make_interface(self, intf_name, execute_transaction=True): if self.interface_exists(intf_name): print("INFO: Interface exists.") return self.interface_uuid(intf_name) txn = self._start_txn() tmp_row = txn.insert(self.get_table('Interface')) tmp_row.name = intf_name def try_again(db_entity): db_entity.make_interface(intf_name) if not execute_transaction: return tmp_row txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s" % (username(), intf_name)) status = self._complete_txn(try_again) if status is False: raise OVSDBException('Unable to create Interface %s: %s' % (intf_name, txn.get_error())) result = txn.get_insert_uuid(tmp_row.uuid) self._txn = None return result def destroy_port(self, port_name, bridge_name): if not self.interface_exists(port_name): return txn = self._start_txn() br = self._find_row_by_name('Bridge', bridge_name) ports = [port for port in br.ports if port.name != port_name] br.ports = ports def try_again(db_entity): db_entity.destroy_port(port_name) txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s" % (username(), port_name)) status = self._complete_txn(try_again) if status is False: raise OVSDBException('unable to delete Port %s: %s' % (port_name, txn.get_error())) self._txn = None def destroy_mirror(self, intf_name, bridge_name): mirror_name = 'm_%s' % intf_name if not self.mirror_exists(mirror_name): return txn = self._start_txn() mirror_row = self._find_row_by_name('Mirror', mirror_name) br = self._find_row_by_name('Bridge', bridge_name) mirrors = [mirror for mirror in br.mirrors if mirror.uuid != mirror_row.uuid] br.mirrors = mirrors def try_again(db_entity): db_entity.destroy_mirror(mirror_name, bridge_name) txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s" % (username(), mirror_name)) status = self._complete_txn(try_again) if status is False: raise OVSDBException('Unable to delete Mirror %s: %s' % (mirror_name, txn.get_error())) self._txn = None def make_port(self, port_name, bridge_name): iface_row = self.make_interface(port_name, False) txn = self._txn br = self._find_row_by_name('Bridge', bridge_name) if not br: raise OVSDBException('Bad bridge name %s' % bridge_name) port = txn.insert(self.get_table('Port')) port.name = port_name br.verify('ports') ports = getattr(br, 'ports', []) ports.append(port) br.ports = ports port.verify('interfaces') ifaces = getattr(port, 'interfaces', []) ifaces.append(iface_row) port.interfaces = ifaces def try_again(db_entity): db_entity.make_port(port_name, bridge_name) txn.add_comment("ovs-tcpdump: user=%s,create_port=%s" % (username(), port_name)) status = self._complete_txn(try_again) if status is False: raise OVSDBException('Unable to create Port %s: %s' % (port_name, txn.get_error())) result = txn.get_insert_uuid(port.uuid) self._txn = None return result def bridge_mirror(self, intf_name, mirror_intf_name, br_name, mirror_select_all=False, mirror_filter=None): txn = self._start_txn() mirror = txn.insert(self.get_table('Mirror')) mirror.name = 'm_%s' % intf_name mirror.select_all = mirror_select_all if mirror_filter is not None: mirror.filter = mirror_filter mirrored_port = self._find_row_by_name('Port', intf_name) mirror.verify('select_dst_port') dst_port = getattr(mirror, 'select_dst_port', []) dst_port.append(mirrored_port) mirror.select_dst_port = dst_port mirror.verify('select_src_port') src_port = getattr(mirror, 'select_src_port', []) src_port.append(mirrored_port) mirror.select_src_port = src_port output_port = self._find_row_by_name('Port', mirror_intf_name) mirror.verify('output_port') out_port = getattr(mirror, 'output_port', []) out_port.append(output_port.uuid) mirror.output_port = out_port br = self._find_row_by_name('Bridge', br_name) br.verify('mirrors') mirrors = getattr(br, 'mirrors', []) mirrors.append(mirror.uuid) br.mirrors = mirrors def try_again(db_entity): db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name) txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s" % (username(), mirror.name)) status = self._complete_txn(try_again) if status is False: raise OVSDBException('Unable to create Mirror %s: %s' % (mirror_intf_name, txn.get_error())) result = txn.get_insert_uuid(mirror.uuid) self._txn = None return result def argv_tuples(lst): cur, nxt = iter(lst), iter(lst) next(nxt, None) try: while True: yield next(cur), next(nxt, None) except StopIteration: pass def py_which(executable): return any(os.access(os.path.join(path, executable), os.X_OK) for path in os.environ["PATH"].split(os.pathsep)) def teardown(db_sock, interface, mirror_interface, mirror_created, tap_created): def cleanup_mirror(): try: ovsdb = OVSDB(db_sock) ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface)) if mirror_created: ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface)) if tap_created: _del_taps[sys.platform](mirror_interface) except Exception: print("Unable to tear down the ports and mirrors.") print("Please use ovs-vsctl to remove the ports and mirrors" " created.") print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock, mirror_interface)) add_hook(cleanup_mirror, None, True) def main(): rundir = os.environ.get('OVS_RUNDIR', '@RUNDIR@') db_sock = 'unix:%s' % os.path.join(rundir, "db.sock") interface = None tcpdargs = [] skip_next = False mirror_interface = None mirror_select_all = False dump_cmd = 'tcpdump' mirror_filter = None for cur, nxt in argv_tuples(sys.argv[1:]): if skip_next: skip_next = False continue if cur in ['-h', '--help']: usage() elif cur in ['-V', '--version']: print("ovs-tcpdump (Open vSwitch) @VERSION@@VERSION_SUFFIX@") sys.exit(0) elif cur in ['--db-sock']: db_sock = nxt skip_next = True continue elif cur in ['--dump-cmd']: dump_cmd = nxt skip_next = True continue elif cur in ['-i', '--interface']: interface = nxt skip_next = True continue elif cur in ['--mirror-to']: mirror_interface = nxt skip_next = True continue elif cur in ['--span']: mirror_select_all = True continue elif cur in ['--filter']: mirror_filter = nxt skip_next = True continue tcpdargs.append(cur) if interface is None: print("Error: must at least specify an interface with '-i' option") sys.exit(1) if not py_which(dump_cmd): print("Error: unable to execute '%s' (check PATH)" % dump_cmd) sys.exit(1) if '-l' not in tcpdargs: tcpdargs.insert(0, '-l') if '-vv' in tcpdargs: print("TCPDUMP Args: %s" % ' '.join(tcpdargs)) ovsdb = OVSDB(db_sock) if mirror_interface is None: mirror_interface = "mi%s" % interface if sys.platform in _make_mirror_name: mirror_interface = _make_mirror_name[sys.platform](interface) mirror_created = True if sys.platform in _make_taps and \ mirror_interface not in interfaces() and \ not ovsdb.port_exists(mirror_interface): _make_taps[sys.platform](mirror_interface, ovsdb.interface_mtu(interface)) tap_created = True else: if mirror_interface in interfaces() or \ ovsdb.port_exists(mirror_interface): mirror_created = False tap_created = False if mirror_interface not in interfaces() and \ not ovsdb.port_exists(mirror_interface): print("ERROR: Please create an interface called `%s`" % mirror_interface) print("See your OS guide for how to do this.") print("Ex: ip link add %s type veth peer name %s" % (mirror_interface, mirror_interface + "2")) sys.exit(1) if not ovsdb.port_exists(interface): print("ERROR: Port %s does not exist." % interface) sys.exit(1) teardown(db_sock, interface, mirror_interface, mirror_created, tap_created) try: if mirror_created: ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface)) ovsdb.bridge_mirror(interface, mirror_interface, ovsdb.port_bridge(interface), mirror_select_all, mirror_filter=mirror_filter) except OVSDBException as oe: print("ERROR: Unable to properly setup the mirror: %s." % str(oe)) sys.exit(1) ovsdb.close_idl() pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs)) while pipes.poll() is None: data = pipes.stdout.readline().strip(b'\n') if len(data) == 0: break print(data.decode('utf-8')) try: sys.stdout.close() except IOError: pass if pipes.poll() is None: pipes.terminate() if __name__ == '__main__': main() # Local variables: # mode: python # End: openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-tcpundump.in000077500000000000000000000042371514270232600241600ustar00rootroot00000000000000#! @PYTHON3@ # # Copyright (c) 2010 Nicira, Inc. # # 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. import getopt import re import sys argv0 = sys.argv[0] def usage(): print("""\ %(argv0)s: print "tcpdump -xx" output as hex usage: %(argv0)s < FILE where FILE is output from "tcpdump -xx". The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0}) sys.exit(0) if __name__ == "__main__": try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['help', 'version']) except getopt.GetoptError as geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print("ovs-tcpundump (Open vSwitch) @VERSION@@VERSION_SUFFIX@") sys.exit(0) else: sys.exit(0) if len(args) != 0: sys.stderr.write("%s: non-option argument not supported " "(use --help for help)\n" % argv0) sys.exit(1) packet = '' regex = re.compile(r'^\s+0x([0-9a-fA-F]+): ((?: [0-9a-fA-F]{2,4})+)') while True: line = sys.stdin.readline() if line == "": break m = regex.match(line) if m is None or int(m.group(1), 16) == 0: if packet != '': print(packet) packet = '' if m: packet += re.sub(r'\s', '', m.group(2), 0) if packet != '': print(packet) # Local variables: # mode: python # End: openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-test.in000066400000000000000000000112051514270232600231060ustar00rootroot00000000000000#! @PYTHON3@ # # 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. """ ovs test utility that allows to do tests between remote hosts """ import fcntl import math import os import select import signal import socket import subprocess import sys import time import xmlrpclib import argparse import twisted import ovstest.args as args import ovstest.rpcserver as rpcserver import ovstest.tests as tests import ovstest.util as util DEFAULT_TEST_BRIDGE = "ovstestbr0" DEFAULT_TEST_PORT = "ovstestport0" DEFAULT_TEST_TUN = "ovstestport1" def collect_information(node): """Print information about hosts that will do testing""" print "Node %s:%u " % (node[0], node[1]) server = util.rpc_client(node[0], node[1]) interface_name = server.get_interface(node[0]) phys_iface = None uname = server.uname() mtu = 1500 if not interface_name: print ("Could not find interface that has %s IP address." "Make sure that you specified correct Outer IP." % (node[0])) else: if server.is_ovs_bridge(interface_name): phys_iface = server.get_iface_from_bridge(interface_name) else: phys_iface = interface_name if phys_iface: driver = server.get_driver(phys_iface) mtu = server.get_interface_mtu(phys_iface) print "Will be using %s (%s) with MTU %u" % (phys_iface, node[0], mtu) if not driver: print "Unable to get driver information from ethtool." else: print "On this host %s has %s." % (phys_iface, driver) if not uname: print "Unable to retrieve kernel information. Is this Linux?" else: print "Running kernel %s." % uname print "\n" return mtu if __name__ == '__main__': local_server = None try: ovs_args = args.ovs_initialize_args() if ovs_args.port is not None: # Start in pure server mode rpcserver.start_rpc_server(ovs_args.port) elif ovs_args.servers is not None: # Run in client mode node1 = ovs_args.servers[0] node2 = ovs_args.servers[1] # Verify whether client will need to spawn a local instance of # ovs-test server by looking at the first OuterIP. if it is a # 127.0.0.1 then spawn local ovs-test server. if node1[0] == "127.0.0.1": local_server = util.start_local_server(node1[1]) # We must determine the IP address that local ovs-test server # will use: me = util.rpc_client(node1[0], node1[1]) my_ip = me.get_my_address_from(node2[0], node2[1]) node1 = (my_ip, node1[1], node1[2], node1[3]) mtu_node2 = collect_information(node2) mtu_node1 = collect_information(node1) bandwidth = ovs_args.targetBandwidth interval = ovs_args.testInterval ps = util.get_datagram_sizes(mtu_node1, mtu_node2) direct = ovs_args.direct vlan_tag = ovs_args.vlanTag tunnel_modes = ovs_args.tunnelModes if direct is not None: print "Performing direct tests" tests.do_direct_tests(node2, node1, bandwidth, interval, ps) if vlan_tag is not None: print "Performing VLAN tests" tests.do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag) for tmode in tunnel_modes: print "Performing", tmode, "tests" tests.do_l3_tests(node2, node1, bandwidth, interval, ps, tmode) except KeyboardInterrupt: pass except xmlrpclib.Fault: print "Couldn't establish XMLRPC control channel" except socket.error: print "Couldn't establish XMLRPC control channel" except xmlrpclib.ProtocolError: print "XMLRPC control channel was abruptly terminated" except twisted.internet.error.CannotListenError: print "Couldn't start XMLRPC server on port %u" % ovs_args.port finally: if local_server is not None: local_server.terminate() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-testcontroller.8.in000066400000000000000000000132301514270232600253600ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-testcontroller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-testcontroller . .SH NAME ovs\-testcontroller \- simple OpenFlow controller for testing . .SH SYNOPSIS .B ovs\-testcontroller [\fIoptions\fR] \fImethod\fR \fB[\fImethod\fR]\&... . .SH DESCRIPTION \fBovs\-testcontroller\fR is a simple OpenFlow controller that manages any number of switches over the OpenFlow protocol, causing them to function as L2 MAC-learning switches or hubs. It is suitable for initial testing of OpenFlow networks. It is not a necessary or desirable part of a production OpenFlow deployment. .PP \fBovs\-testcontroller\fR controls one or more OpenFlow switches, specified as one or more of the following OpenFlow connection methods: . .RS .so lib/vconn-passive.man .so lib/vconn-active.man .RE . .SH OPTIONS .IP "\fB\-n\fR" .IQ "\fB\-\-noflow\fR" By default, \fBovs\-testcontroller\fR sets up a flow in each OpenFlow switch whenever it receives a packet whose destination is known due through MAC learning. This option disables flow setup, so that every packet in the network passes through the controller. .IP This option is most useful for debugging. It reduces switching performance, so it should not be used in production. . .TP \fB\-\-max\-idle=\fIsecs\fR|\fBpermanent\fR Sets \fIsecs\fR as the number of seconds that a flow set up by the controller will remain in the switch's flow table without any matching packets being seen. If \fBpermanent\fR is specified, which is not recommended, flows will never expire. The default is 60 seconds. .IP This option has no effect when \fB\-n\fR (or \fB\-\-noflow\fR) is in use (because the controller does not set up flows in that case). . .IP "\fB\-H\fR" .IQ "\fB\-\-hub\fR" By default, the controller acts as an L2 MAC-learning switch. This option changes its behavior to that of a hub that floods packets on all but the incoming port. .IP If \fB\-H\fR (or \fB\-\-hub\fR) and \fB\-n\fR (or \fB\-\-noflow\fR) are used together, then the cumulative effect is that every packet passes through the controller and every packet is flooded. .IP This option is most useful for debugging. It reduces switching performance, so it should not be used in production. . .IP "\fB\-w\fR[\fIwildcard_mask\fR]" .IQ "\fB\-\-wildcards\fR[\fB=\fIwildcard_mask\fR]\fR" By default, \fBovs\-testcontroller\fR sets up exact-match flows. This option allows it to set up wildcarded flows, which may reduce flow setup latency by causing less traffic to be sent up to the controller. .IP The optional \fIwildcard_mask\fR is an OpenFlow wildcard bitmask in hexadecimal that specifies the fields to wildcard. If no \fIwildcard_mask\fR is specified, the default value 0x2820F0 is used which specifies L2-only switching and wildcards L3 and L4 fields. Another interesting value is 0x2000EC, which specifies L3-only switching and wildcards L2 and L4 fields. .IP This option has no effect when \fB\-n\fR (or \fB\-\-noflow\fR) is in use (because the controller does not set up flows in that case). . .IP "\fB\-N\fR" .IQ "\fB\-\-normal\fR" By default, \fBovs\-testcontroller\fR directs packets to a particular port or floods them. This option causes it to direct non-flooded packets to the OpenFlow \fBOFPP_NORMAL\fR port. This allows the switch itself to make decisions about packet destinations. Support for \fBOFPP_NORMAL\fR is optional in OpenFlow, so this option may not well with some non-Open vSwitch switches. . .IP "\fB\-\-mute\fR" Prevents ovs\-testcontroller from replying to any OpenFlow messages sent to it by switches. .IP This option is only for debugging the Open vSwitch implementation of ``fail open'' mode. It must not be used in production. . .IP "\fB\-q \fIid\fR" .IQ "\fB\-\-queue=\fIid\fR" By default, \fBovs\-testcontroller\fR uses the default OpenFlow queue for sending packets and setting up flows. Use one of these options, supplying \fIid\fR as an OpenFlow queue ID as a decimal number, to instead use that specific queue. .IP This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and with \fB\-H\fR or \fB\-\-hub\fR. If more than one is specified then this option takes precedence. .IP This option may be useful for testing or debugging quality of service setups. . .IP "\fB\-Q \fIport-name\fB:\fIqueue-id\fR" .IP "\fB\-\-port\-queue \fIport-name\fB:\fIqueue-id\fR" Configures packets received on the port named \fIport-name\fR (e.g. \fBeth0\fR) to be output on OpenFlow queue ID \fIqueue-id\fR (specified as a decimal number). For the specified port, this option overrides the default specified on \fB\-q\fR or \fB\-\-queue\fR. .IP This option may be specified any number of times with different \fIport-name\fR arguments. .IP This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and with \fB\-H\fR or \fB\-\-hub\fR. If more than one is specified then this option takes precedence. .IP This option may be useful for testing or debugging quality of service setups. . .IP "\fB\-\-with\-flows \fIfile\fR" When a switch connects, push the flow entries as described in \fIfile\fR. Each line in \fIfile\fR is a flow entry in the format described for the \fBadd\-flows\fR command in the \fBFlow Syntax\fR section of the \fBovs\-ofctl\fR(8) man page. .IP Use this option more than once to add flows from multiple files. . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-peer-ca-cert.man .ds DD .SS "Daemon Options" .so lib/daemon.man .so lib/vlog.man .so lib/unixctl.man .so lib/common.man .so lib/ofp-version.man . .SH EXAMPLES .PP To bind locally to port 6653 (the default) and wait for incoming connections from OpenFlow switches: .IP \fB% ovs\-testcontroller ptcp:\fR .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-ofctl (8), .BR ovs\-dpctl (8) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-testcontroller.c000066400000000000000000000302301514270232600250250ustar00rootroot00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2017 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "fatal-signal.h" #include "learning-switch.h" #include "ofp-version-opt.h" #include "openvswitch/ofpbuf.h" #include "openflow/openflow.h" #include "openvswitch/poll-loop.h" #include "openvswitch/rconn.h" #include "simap.h" #include "stream-ssl.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/ofp-flow.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "socket-util.h" VLOG_DEFINE_THIS_MODULE(controller); #define MAX_SWITCHES 16 #define MAX_LISTENERS 16 struct switch_ { struct lswitch *lswitch; }; /* -H, --hub: Learn the ports on which MAC addresses appear? */ static bool learn_macs = true; /* -n, --noflow: Set up flows? (If not, every packet is processed at the * controller.) */ static bool set_up_flows = true; /* -N, --normal: Use "NORMAL" action instead of explicit port? */ static bool action_normal = false; /* -w, --wildcard: 0 to disable wildcard flow entries, an OFPFW10_* bitmask to * enable specific wildcards, or UINT32_MAX to use the default wildcards. */ static uint32_t wildcards = 0; /* --max-idle: Maximum idle time, in seconds, before flows expire. */ static int max_idle = 60; /* --mute: If true, accept connections from switches but do not reply to any * of their messages (for debugging fail-open mode). */ static bool mute = false; /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */ static uint32_t default_queue = UINT32_MAX; /* -Q, --port-queue: map from port name to port number. */ static struct simap port_queues = SIMAP_INITIALIZER(&port_queues); /* --with-flows: Flows to send to switch. */ static struct ofputil_flow_mod *default_flows; static size_t n_default_flows; static enum ofputil_protocol usable_protocols; /* --unixctl: Name of unixctl socket, or null to use the default. */ static char *unixctl_path = NULL; static void new_switch(struct switch_ *, struct vconn *); static void parse_options(int argc, char *argv[]); OVS_NO_RETURN static void usage(void); int main(int argc, char *argv[]) { struct unixctl_server *unixctl; struct switch_ switches[MAX_SWITCHES]; struct pvconn *listeners[MAX_LISTENERS]; int n_switches, n_listeners; int retval; int i; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); fatal_ignore_sigpipe(); daemon_become_new_user(false, false); if (argc - optind < 1) { ovs_fatal(0, "at least one vconn argument required; " "use --help for usage"); } n_switches = n_listeners = 0; for (i = optind; i < argc; i++) { const char *name = argv[i]; struct vconn *vconn; retval = vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, &vconn); if (!retval) { if (n_switches >= MAX_SWITCHES) { ovs_fatal(0, "max %d switch connections", n_switches); } new_switch(&switches[n_switches++], vconn); continue; } else if (retval == EAFNOSUPPORT) { struct pvconn *pvconn; retval = pvconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, &pvconn); if (!retval) { if (n_listeners >= MAX_LISTENERS) { ovs_fatal(0, "max %d passive connections", n_listeners); } listeners[n_listeners++] = pvconn; } } if (retval) { VLOG_ERR("%s: connect: %s", name, ovs_strerror(retval)); } } if (n_switches == 0 && n_listeners == 0) { ovs_fatal(0, "no active or passive switch connections"); } daemonize_start(false, false); retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } daemonize_complete(); while (n_switches > 0 || n_listeners > 0) { /* Accept connections on listening vconns. */ for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) { struct vconn *new_vconn; retval = pvconn_accept(listeners[i], &new_vconn); if (!retval || retval == EAGAIN) { if (!retval) { new_switch(&switches[n_switches++], new_vconn); } i++; } else { pvconn_close(listeners[i]); listeners[i] = listeners[--n_listeners]; } } /* Do some switching work. . */ for (i = 0; i < n_switches; ) { struct switch_ *this = &switches[i]; lswitch_run(this->lswitch); if (lswitch_is_alive(this->lswitch)) { i++; } else { lswitch_destroy(this->lswitch); switches[i] = switches[--n_switches]; } } unixctl_server_run(unixctl); /* Wait for something to happen. */ if (n_switches < MAX_SWITCHES) { for (i = 0; i < n_listeners; i++) { pvconn_wait(listeners[i]); } } for (i = 0; i < n_switches; i++) { struct switch_ *sw = &switches[i]; lswitch_wait(sw->lswitch); } unixctl_server_wait(unixctl); poll_block(); } return 0; } static void new_switch(struct switch_ *sw, struct vconn *vconn) { struct lswitch_config cfg; struct rconn *rconn; rconn = rconn_create(60, 0, DSCP_DEFAULT, get_allowed_ofp_versions()); rconn_connect_unreliably(rconn, vconn, NULL); cfg.mode = (action_normal ? LSW_NORMAL : learn_macs ? LSW_LEARN : LSW_FLOOD); cfg.wildcards = wildcards; cfg.max_idle = set_up_flows ? max_idle : -1; cfg.default_flows = default_flows; cfg.n_default_flows = n_default_flows; cfg.usable_protocols = usable_protocols; cfg.default_queue = default_queue; cfg.port_queues = &port_queues; cfg.mute = mute; sw->lswitch = lswitch_create(rconn, &cfg); } static void add_port_queue(char *s) { char *save_ptr = NULL; char *port_name; char *queue_id; port_name = strtok_r(s, ":", &save_ptr); queue_id = strtok_r(NULL, "", &save_ptr); if (!queue_id) { ovs_fatal(0, "argument to -Q or --port-queue should take the form " "\":\""); } if (!simap_put(&port_queues, port_name, atoi(queue_id))) { ovs_fatal(0, " arguments for -Q or --port-queue must " "be unique"); } } static void parse_options(int argc, char *argv[]) { enum { OPT_MAX_IDLE = UCHAR_MAX + 1, OPT_PEER_CA_CERT, OPT_MUTE, OPT_WITH_FLOWS, OPT_UNIXCTL, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS, OFP_VERSION_OPTION_ENUMS, SSL_OPTION_ENUMS, }; static const struct option long_options[] = { {"hub", no_argument, NULL, 'H'}, {"noflow", no_argument, NULL, 'n'}, {"normal", no_argument, NULL, 'N'}, {"wildcards", optional_argument, NULL, 'w'}, {"max-idle", required_argument, NULL, OPT_MAX_IDLE}, {"mute", no_argument, NULL, OPT_MUTE}, {"queue", required_argument, NULL, 'q'}, {"port-queue", required_argument, NULL, 'Q'}, {"with-flows", required_argument, NULL, OPT_WITH_FLOWS}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, DAEMON_LONG_OPTIONS, OFP_VERSION_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int indexptr; char *error; int c; c = getopt_long(argc, argv, short_options, long_options, &indexptr); if (c == -1) { break; } switch (c) { case 'H': learn_macs = false; break; case 'n': set_up_flows = false; break; case OPT_MUTE: mute = true; break; case 'N': action_normal = true; break; case 'w': wildcards = optarg ? strtol(optarg, NULL, 16) : UINT32_MAX; break; case OPT_MAX_IDLE: if (!strcmp(optarg, "permanent")) { max_idle = OFP_FLOW_PERMANENT; } else { max_idle = atoi(optarg); if (max_idle < 1 || max_idle > 65535) { ovs_fatal(0, "--max-idle argument must be between 1 and " "65535 or the word 'permanent'"); } } break; case 'q': default_queue = atoi(optarg); break; case 'Q': add_port_queue(optarg); break; case OPT_WITH_FLOWS: error = parse_ofp_flow_mod_file(optarg, NULL, NULL, OFPFC_ADD, &default_flows, &n_default_flows, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } break; case OPT_UNIXCTL: unixctl_path = optarg; break; case 'h': usage(); VLOG_OPTION_HANDLERS OFP_VERSION_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!simap_is_empty(&port_queues) || default_queue != UINT32_MAX) { if (action_normal) { ovs_error(0, "queue IDs are incompatible with -N or --normal; " "not using OFPP_NORMAL"); action_normal = false; } if (!learn_macs) { ovs_error(0, "queue IDs are incompatible with -H or --hub; " "not acting as hub"); learn_macs = true; } } } static void usage(void) { printf("%s: OpenFlow controller\n" "usage: %s [OPTIONS] METHOD\n" "where METHOD is any OpenFlow connection method.\n", program_name, program_name); vconn_usage(true, true, false); daemon_usage(); ofp_version_usage(); vlog_usage(); printf("\nOther options:\n" " -H, --hub act as hub instead of learning switch\n" " -n, --noflow pass traffic, but don't add flows\n" " --max-idle=SECS max idle time for new flows\n" " -N, --normal use OFPP_NORMAL action\n" " -w, --wildcards[=MASK] wildcard (specified) bits in flows\n" " -q, --queue=QUEUE-ID OpenFlow queue ID to use for output\n" " -Q PORT-NAME:QUEUE-ID use QUEUE-ID for frames from PORT-NAME\n" " --with-flows FILE use the flows from FILE\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-vlan-test.in000077500000000000000000000275241514270232600240620ustar00rootroot00000000000000#! @PYTHON3@ # # Copyright (c) 2010 Nicira, Inc. # # 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. import getopt import http.client import http.server import os import threading import time import signal #Causes keyboard interrupts to go to the main thread. import socket import sys print_safe_lock = threading.Lock() def print_safe(s): print_safe_lock.acquire() print(s) print_safe_lock.release() def start_thread(target, args): t = threading.Thread(target=target, args=args) t.setDaemon(True) t.start() return t #Caller is responsible for catching socket.error exceptions. def send_packet(key, length, dest_ip, dest_port): length -= 20 + 8 #IP and UDP headers. packet = str(key) packet += chr(0) * (length - len(packet)) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(packet, (dest_ip, dest_port)) sock.close() #UDP Receiver class UDPReceiver: def __init__(self, vlan_ip, vlan_port): self.vlan_ip = vlan_ip self.vlan_port = vlan_port self.recv_callbacks = {} self.udp_run = False def recv_packet(self, key, success_callback, timeout_callback): event = threading.Event() def timeout_cb(): timeout_callback() event.set() timer = threading.Timer(30, timeout_cb) timer.daemon = True def success_cb(): timer.cancel() success_callback() event.set() # Start the timer first to avoid a timer.cancel() race condition. timer.start() self.recv_callbacks[key] = success_cb return event def udp_receiver(self): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(1) try: sock.bind((self.vlan_ip, self.vlan_port)) except socket.error as e: print_safe('Failed to bind to %s:%d with error: %s' % (self.vlan_ip, self.vlan_port, e)) os._exit(1) #sys.exit only exits the current thread. while self.udp_run: try: data, _ = sock.recvfrom(4096) except socket.timeout: continue except socket.error as e: print_safe('Failed to receive from %s:%d with error: %s' % (self.vlan_ip, self.vlan_port, e)) os._exit(1) data_str = data.split(chr(0))[0] if not data_str.isdigit(): continue key = int(data_str) if key in self.recv_callbacks: self.recv_callbacks[key]() del self.recv_callbacks[key] def start(self): self.udp_run = True start_thread(self.udp_receiver, ()) def stop(self): self.udp_run = False #Server vlan_server = None class VlanServer: def __init__(self, server_ip, server_port, vlan_ip, vlan_port): global vlan_server vlan_server = self self.server_ip = server_ip self.server_port = server_port self.recv_response = '%s:%d:' % (vlan_ip, vlan_port) self.result = {} self.result_lock = threading.Lock() self._test_id = 0 self._test_id_lock = threading.Lock() self.udp_recv = UDPReceiver(vlan_ip, vlan_port) def get_test_id(self): self._test_id_lock.acquire() self._test_id += 1 ret = self._test_id self._test_id_lock.release() return ret def set_result(self, key, value): self.result_lock.acquire() if key not in self.result: self.result[key] = value self.result_lock.release() def recv(self, test_id): self.udp_recv.recv_packet(test_id, lambda : self.set_result(test_id, 'Success'), lambda : self.set_result(test_id, 'Timeout')) return self.recv_response + str(test_id) def send(self, test_id, data): try: ip, port, size = data.split(':') port = int(port) size = int(size) except ValueError: self.set_result(test_id, 'Server failed to parse send request: %s' % data) return def send_thread(): send_time = 10 for _ in range(send_time * 2): try: send_packet(test_id, size, ip, port) except socket.error as e: self.set_result(test_id, 'Failure: ' + str(e)) return time.sleep(.5) self.set_result(test_id, 'Success') start_thread(send_thread, ()) return str(test_id) def run(self): self.udp_recv.start() try: http.server.HTTPServer((self.server_ip, self.server_port), VlanServerHandler).serve_forever() except socket.error as e: print_safe('Failed to start control server: %s' % e) self.udp_recv.stop() return 1 class VlanServerHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): #Guarantee three arguments. path = (self.path.lower().lstrip('/') + '//').split('/') resp = 404 body = None if path[0] == 'start': test_id = vlan_server.get_test_id() if path[1] == 'recv': resp = 200 body = vlan_server.recv(test_id) elif path[1] == 'send': resp = 200 body = vlan_server.send(test_id, path[2]) elif (path[0] == 'result' and path[1].isdigit() and int(path[1]) in vlan_server.result): resp = 200 body = vlan_server.result[int(path[1])] elif path[0] == 'ping': resp = 200 body = 'pong' self.send_response(resp) self.end_headers() if body: self.wfile.write(body) #Client class VlanClient: def __init__(self, server_ip, server_port, vlan_ip, vlan_port): self.server_ip_port = '%s:%d' % (server_ip, server_port) self.vlan_ip_port = "%s:%d" % (vlan_ip, vlan_port) self.udp_recv = UDPReceiver(vlan_ip, vlan_port) def request(self, resource): conn = http.client.HTTPConnection(self.server_ip_port) conn.request('GET', resource) return conn def send(self, size): def error_msg(e): print_safe('Send size %d unsuccessful: %s' % (size, e)) try: conn = self.request('/start/recv') data = conn.getresponse().read() except (socket.error, http.client.HTTPException) as e: error_msg(e) return False try: ip, port, test_id = data.split(':') port = int(port) test_id = int(test_id) except ValueError: error_msg("Received invalid response from control server (%s)" % data) return False send_time = 5 for _ in range(send_time * 4): try: send_packet(test_id, size, ip, port) resp = self.request('/result/%d' % test_id).getresponse() data = resp.read() except (socket.error, http.client.HTTPException) as e: error_msg(e) return False if resp.status == 200 and data == 'Success': print_safe('Send size %d successful' % size) return True elif resp.status == 200: error_msg(data) return False time.sleep(.25) error_msg('Timeout') return False def recv(self, size): def error_msg(e): print_safe('Receive size %d unsuccessful: %s' % (size, e)) resource = '/start/send/%s:%d' % (self.vlan_ip_port, size) try: conn = self.request(resource) test_id = conn.getresponse().read() except (socket.error, http.client.HTTPException) as e: error_msg(e) return False if not test_id.isdigit(): error_msg('Invalid response %s' % test_id) return False success = [False] #Primitive datatypes can't be set from closures. def success_cb(): success[0] = True def failure_cb(): success[0] = False self.udp_recv.recv_packet(int(test_id), success_cb, failure_cb).wait() if success[0]: print_safe('Receive size %d successful' % size) else: error_msg('Timeout') return success[0] def server_up(self): def error_msg(e): print_safe('Failed control server connectivity test: %s' % e) try: resp = self.request('/ping').getresponse() data = resp.read() except (socket.error, http.client.HTTPException) as e: error_msg(e) return False if resp.status != 200: error_msg('Invalid status %d' % resp.status) elif data != 'pong': error_msg('Invalid response %s' % data) return True def run(self): if not self.server_up(): return 1 self.udp_recv.start() success = True for size in [50, 500, 1000, 1500]: success = self.send(size) and success success = self.recv(size) and success self.udp_recv.stop() if success: print_safe('OK') return 0 else: print_safe('FAILED') return 1 def usage(): print_safe("""\ %(argv0)s: Test vlan connectivity usage: %(argv0)s server vlan The following options are also available: -s, --server run in server mode -h, --help display this help message -V, --version display version information\ """ % {'argv0': sys.argv[0]}) def main(): try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hVs', ['help', 'version', 'server']) except getopt.GetoptError as geo: print_safe('%s: %s\n' % (sys.argv[0], geo.msg)) return 1 server = False for key, _ in options: if key in ['-h', '--help']: usage() return 0 elif key in ['-V', '--version']: print_safe('ovs-vlan-test (Open vSwitch) @VERSION@@VERSION_SUFFIX@') return 0 elif key in ['-s', '--server']: server = True else: print_safe('Unexpected option %s. (use --help for help)' % key) return 1 if len(args) != 2: print_safe('Expecting two arguments. (use --help for help)') return 1 try: server_ip, server_port = args[0].split(':') server_port = int(server_port) except ValueError: server_ip = args[0] server_port = 80 try: vlan_ip, vlan_port = args[1].split(':') vlan_port = int(vlan_port) except ValueError: vlan_ip = args[1] vlan_port = 15213 if server: return VlanServer(server_ip, server_port, vlan_ip, vlan_port).run() else: return VlanClient(server_ip, server_port, vlan_ip, vlan_port).run() if __name__ == '__main__': main_ret = main() # Python can throw exceptions if threads are running at exit. for th in threading.enumerate(): if th != threading.currentThread(): th.join() sys.exit(main_ret) openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-vsctl-bashcomp.bash000066400000000000000000000705521514270232600253750ustar00rootroot00000000000000SAVE_IFS=$IFS IFS=" " _OVSDB_SERVER_LOCATION="" # Run ovs-vsctl and make sure that ovs-vsctl is always called with # the correct --db argument. _ovs_vsctl () { local _db if [ -n "$_OVSDB_SERVER_LOCATION" ]; then _db="--db=$_OVSDB_SERVER_LOCATION" fi ovs-vsctl ${_db} "$@" } # ovs-vsctl --commands outputs in this format: # # main = ,, # localopts = ([] )* # localopt = --[^]]* # name = [^,]* # arguments = ((!argument|?argument|*argument|+argument) )* # argument = ([^ ]*|argument\|argument) # # The [] characters in local options are just delimiters. The # argument prefixes mean: # !argument :: The argument is required # ?argument :: The argument is optional # *argument :: The argument may appear any number (0 or more) times # +argument :: The argument may appear one or more times # A bar (|) character in an argument means thing before bar OR thing # after bar; for example, del-port can take a port or an interface. _OVS_VSCTL_COMMANDS= _OVS_VSCTL_OPTIONS= if command -v ovs-vsctl > /dev/null; then _OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)" # This doesn't complete on short arguments, so it filters them out. _OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \ | sed -e 's/\(.*\)=ARG/\1=/')" fi IFS=$SAVE_IFS declare -A _OVS_VSCTL_PARSED_ARGS declare -A _OVS_VSCTL_NEW_RECORDS # This is a convenience function to make sure that user input is # looked at as a fixed string when being compared to something. $1 is # the input; this behaves like 'grep "^$1"' but deals with regex # metacharacters in $1. _ovs_vsctl_check_startswith_string () { awk 'thearg == "" || index($0, thearg)==1' thearg="$1" } # $1 = word to complete on. # Complete on global options. _ovs_vsctl_bashcomp_globalopt () { local options result options="" result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \ | _ovs_vsctl_check_startswith_string "${1%=*}") if [[ $result =~ "=" ]]; then options="NOSPACE" fi printf -- "${options}\nEO\n${result}" } # $1 = word to complete on. # Complete on local options. _ovs_vsctl_bashcomp_localopt () { local options result possible_opts possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 -d',') # This finds all options that could go together with the # already-seen ones for prefix_arg in $1; do possible_opts=$(printf "%s\n" "$possible_opts" \ | grep -- "\[${prefix_arg%%=*}=\?\]") done result=$(printf "%s\n" "${possible_opts}" \ | tr ' ' '\n' | tr -s '\n' | sort | uniq) # This removes the already-seen options from the list so that # users aren't completed for the same option twice. for prefix_arg in $1; do result=$(printf "%s\n" "${result}" \ | grep -v -- "\[${prefix_arg%%=*}=\?\]") done result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \ | _ovs_vsctl_check_startswith_string "$2") if [[ $result =~ "=" ]]; then options="NOSPACE" fi printf -- "${options}\nEO\n${result}" } # $1 = given local options. # $2 = word to complete on. # Complete on command that could contain the given local options. _ovs_vsctl_bashcomp_command () { local result possible_cmds possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}") for prefix_arg in $1; do possible_cmds=$(printf "%s\n" "$possible_cmds" \ | grep -- "\[$prefix_arg=\?\]") done result=$(printf "%s\n" "${possible_cmds}" \ | cut -f2 -d',' \ | _ovs_vsctl_check_startswith_string "$2") printf -- "${result}" } # $1 = completion result to check. # Return 0 if the completion result is non-empty, otherwise return 1. _ovs_vsctl_detect_nonzero_completions () { local tmp newarg newarg=${1#*EO} readarray tmp <<< "$newarg" if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then return 1 fi return 0 } # $1 = argument format to expand. # Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'. _ovs_vsctl_expand_command () { result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \ | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \ | awk '/\+.*/ { name=substr($0,2); print "!"name; print "*"name; next; } 1') printf -- "${result}\n!--" } # $1 = word to complete on. # Complete on table. _ovs_vsctl_complete_table () { local result result=$(ovsdb-client --no-heading list-tables $_OVSDB_SERVER_LOCATION Open_vSwitch \ | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on record. Provide both the name and uuid. _ovs_vsctl_complete_record () { local table uuids names new_record table="${_OVS_VSCTL_PARSED_ARGS[TABLE]}" new_record="${_OVS_VSCTL_NEW_RECORDS[${table^^}]}" # Tables should always have an _uuid column uuids=$(_ovs_vsctl --no-heading -f table -d bare --columns=_uuid \ list $table | _ovs_vsctl_check_startswith_string "$1") # Names don't always exist, silently ignore if the name column is # unavailable. names=$(_ovs_vsctl --no-heading -f table -d bare \ --columns=name list $table \ 2>/dev/null \ | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n%s\n%s\n" "${uuids}" "${names}" "${new_record}" } # $1 = word to complete on. # Complete on bridge. _ovs_vsctl_complete_bridge () { local result result=$(_ovs_vsctl list-br | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on port. If a bridge has already been specified, # just complete for that bridge. _ovs_vsctl_complete_port () { local ports result if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then ports=$(_ovs_vsctl list-ports "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") else local all_ports all_ports=$(_ovs_vsctl --format=table \ --no-headings \ --columns=name \ list Port) ports=$(printf "$all_ports" | tr -d '" ' | sort -u) fi result=$(_ovs_vsctl_check_startswith_string "$1" <<< "$ports") printf -- "EO\n%s\n" "${result}" } # $1: Atom to complete (as usual) # $2: Table to complete the key in # $3: Column to find keys in # $4: Prefix for each completion # Complete on key based on given table and column info. _ovs_vsctl_complete_key_given_table_column () { local keys keys=$(_ovs_vsctl --no-heading --columns="$3" list \ "$2" \ | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 \ | xargs printf "$4%s\n" | _ovs_vsctl_check_startswith_string "$4$1") result="${keys}" printf -- "%s\n" "${result}" } # $1 = word to complete on. # Complete on key. __complete_key () { # KEY is used in both br-set-external-id/br-get-external id (in # which case it is implicitly a key in the external-id column) and # in remove, where it is a table key. This checks to see if table # is set (the remove scenario), and then decides what to do. local result if [ -n "${_OVS_VSCTL_PARSED_ARGS[TABLE]}" ]; then local column=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["COLUMN"]}) result=$(_ovs_vsctl_complete_key_given_table_column \ "$1" \ ${_OVS_VSCTL_PARSED_ARGS["TABLE"]} \ $column \ "") else result=$(_ovs_vsctl br-get-external-id \ ${_OVS_VSCTL_PARSED_ARGS["BRIDGE"]} \ | cut -d'=' -f1 | _ovs_vsctl_check_startswith_string "$1") fi printf -- "%s" "${result}" } # $1 = word to complete on. # Complete on key. _ovs_vsctl_complete_key () { # KEY is used in both br-set-external-id/br-get-external id (in # which case it is implicitly a key in the external-id column) and # in remove, where it is a table key. This checks to see if table # is set (the remove scenario), and then decides what to do. local result result="$(__complete_key $1)" # If result is empty, just use user input as result. if [ -z "$result" ]; then result=$1 fi printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on value. _ovs_vsctl_complete_value () { local result # Just use user input as result. result=$1 printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on key=value. _ovs_vsctl_complete_key_value () { local orig_completions new_completions orig_completions=$(__complete_key "$1") for completion in ${orig_completions#*EO}; do new_completions="${new_completions} ${completion}=" done # If 'new_completions' is empty, just use user input as result. if [ -z "$new_completions" ]; then new_completions=$1 fi printf -- "NOSPACE\nEO\n%s" "${new_completions}" } # $1 = word to complete on. # Complete on column. _ovs_vsctl_complete_column () { local columns result columns=$(ovsdb-client --no-headings list-columns $_OVSDB_SERVER_LOCATION \ Open_vSwitch ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) result=$(printf "%s\n" "${columns}" \ | tr -d ':' | cut -d' ' -f1 \ | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) printf -- "EO\n%s\n" "${result}" } # Extract all system interfaces. _ovs_vsctl_get_sys_intf () { local result case "$(uname -o)" in *Linux*) result=$(ip -o link 2>/dev/null | cut -d':' -f2 \ | sed -e 's/^ \(.*\)/\1/') ;; *) result=$(ifconfig -a -s 2>/dev/null | cut -f1 -d' ' | tail -n +2) ;; esac printf "%s\n" "${result}" } # $1 = word to complete on. # Complete on system interface. _ovs_vsctl_complete_sysiface () { local result result=$(_ovs_vsctl_get_sys_intf | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on interface. If a bridge has already been specified, # just complete for that bridge. _ovs_vsctl_complete_iface () { local result if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then result=$(_ovs_vsctl list-ifaces "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") else for bridge in $(_ovs_vsctl list-br); do local ifaces ifaces=$(_ovs_vsctl list-ifaces "${bridge}") result="${result} ${ifaces}" done fi printf "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on COLUMN?:KEY=VALUE. _ovs_vsctl_complete_column_optkey_value () { local result column key value completion column=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -d':' -f1) key=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -s -d':' -f2) # The tr -d '\n' <<< makes sure that there are no leading or # trailing accidental newlines. table=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) # This might also be called after add-port or add-bond; in those # cases, the table should implicitly be assumed to be "Port". # This is done by checking if a NEW- parameter has been # encountered and, if it has, using that type without the NEW- as # the table. if [ -z "$table" ]; then if [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-PORT"]} ] \ || [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-BOND-PORT"]} ]; then table="Port" fi fi if [ -z "$key" ]; then local columns=$(ovsdb-client --no-headings list-columns \ $_OVSDB_SERVER_LOCATION Open_vSwitch $table) result=$(printf "%s\n" "${columns}" \ | awk '/key.*value/ { print $1":"; next } { print $1; next }' \ | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) fi if [[ $1 =~ ":" ]]; then result=$(_ovs_vsctl_complete_key_given_table_column \ "$key" "$table" "$column" "$column:") fi # If result is empty, just use user input as result. if [ -z "$result" ]; then result=$1 fi printf -- "NOSPACE\nEO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on filename. _ovs_vsctl_complete_filename () { local result result=$(compgen -o filenames -A file "$1") printf -- "EO\n%s\n" "${result}" } _ovs_vsctl_complete_bridge_fail_mode () { printf -- "EO\nstandalone\nsecure" } # $1 = word to complete on. # Complete on target. _ovs_vsctl_complete_target () { local result if [[ "$1" =~ ^p?u ]]; then local protocol pathname expansion_base result protocol=$(cut -d':' -f1 <<< "$1") pathname=$(cut -s -d':' -f2 <<< "$1") expansion_base=$(compgen -W "unix punix" "$protocol") expansion_base="$expansion_base:" result=$(compgen -o filenames -A file \ -P $expansion_base "${pathname}") printf -- "NOSPACE\nEO\n%s\n" "${result}" else printf -- "NOSPACE\nEO\nssl:\ntcp:\nunix:\npssl:\nptcp:\npunix:" fi } # Extract PS1 prompt. _ovs_vsctl_get_PS1 () { if [ "$test" = "true" ]; then printf -- "> " return; fi # On Bash 4.4+ just use the @P expansion if ((BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4))); then printf '%s\n' "${PS1@P}" return fi # Original inspiration from # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2, # but changed quite a lot to make it more robust. # Make sure the PS1 used doesn't include any of the special # strings used to identify the prompt myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")" # Export the current environment in case the prompt uses any vars="$(env | cut -d'=' -f1)" for var in $vars; do export $var; done funcs="$(declare -F | cut -d' ' -f3)" for func in $funcs; do export -f $func; done # Get the prompt v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')" v="${v##*# Begin prompt}" printf -- "$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')" } # Request a new value from user. Nothing to complete on. _ovs_vsctl_complete_new () { local two_word_type message result if [ ! "$1" = "--" ]; then two_word_type="${2/-/ }" message="\nEnter a ${two_word_type,,}:\n$(_ovs_vsctl_get_PS1)$COMP_LINE" if [ -n "$1" ]; then result="$1" fi printf -- "NOCOMP\nBM%sEM\nEO\n%s\n" "${message}" "${result}" fi } _ovs_vsctl_complete_dashdash () { printf -- "EO\n%s\n" "--" } # These functions are given two arguments: # # $1 is the word being completed # # $2 is the type of completion --- only currently useful for the # NEW-* functions. # # Note that the NEW-* functions actually are ``completed''; currently # the completions are just used to save the fact that they have # appeared for later use (i.e. implicit table calculation). # # The output is of the form EO, where EO stands # for end options. Currently available options are: # - NOSPACE: Do not add a space at the end of each completion # - NOCOMP: Do not complete, but store the output of the completion # func in _OVS_VSCTL_PARSED_ARGS for later usage. # - BMEM: Print the declare -A _OVS_VSCTL_ARG_COMPLETION_FUNCS=( ["TABLE"]=_ovs_vsctl_complete_table ["RECORD"]=_ovs_vsctl_complete_record ["BRIDGE"]=_ovs_vsctl_complete_bridge ["PARENT"]=_ovs_vsctl_complete_bridge ["PORT"]=_ovs_vsctl_complete_port ["KEY"]=_ovs_vsctl_complete_key ["VALUE"]=_ovs_vsctl_complete_value ["ARG"]=_ovs_vsctl_complete_value ["IFACE"]=_ovs_vsctl_complete_iface ["SYSIFACE"]=_ovs_vsctl_complete_sysiface ["COLUMN"]=_ovs_vsctl_complete_column ["COLUMN?:KEY"]=_ovs_vsctl_complete_column_optkey_value ["COLUMN?:KEY=VALUE"]=_ovs_vsctl_complete_column_optkey_value ["KEY=VALUE"]=_ovs_vsctl_complete_key_value ["?KEY=VALUE"]=_ovs_vsctl_complete_key_value ["PRIVATE-KEY"]=_ovs_vsctl_complete_filename ["CERTIFICATE"]=_ovs_vsctl_complete_filename ["CA-CERT"]=_ovs_vsctl_complete_filename ["MODE"]=_ovs_vsctl_complete_bridge_fail_mode ["TARGET"]=_ovs_vsctl_complete_target ["NEW-BRIDGE"]=_ovs_vsctl_complete_new ["NEW-PORT"]=_ovs_vsctl_complete_new ["NEW-BOND-PORT"]=_ovs_vsctl_complete_new ["NEW-VLAN"]=_ovs_vsctl_complete_new ["--"]=_ovs_vsctl_complete_dashdash ) # $1: Argument type, may include vertical bars to mean OR # $2: Beginning of completion # # Note that this checks for existance in # _OVS_VSCTL_ARG_COMPLETION_FUNCS; if the argument type ($1) is not # there it will fail gracefully. _ovs_vsctl_possible_completions_of_argument () { local possible_types completions tmp completions="EO" possible_types=$(printf "%s\n" "$1" | tr '|' '\n') for type in $possible_types; do if [ ${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} ]; then tmp=$(${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} \ "$2" "${type^^}") tmp_noEO="${tmp#*EO}" tmp_EO="${tmp%%EO*}" completions=$(printf "%s%s\n%s" "${tmp_EO}" \ "${completions}" "${tmp_noEO}") fi done printf "%s\n" "${completions}" } # $1 = List of argument types # $2 = current pointer into said list # $3 = word to complete on # Outputs list of possible completions # The return value is the index in the cmd_args($1) list that should # next be matched, if only one of them did, or 254 if there are no # matches, so it doesn't know what comes next. _ovs_vsctl_complete_argument() { local cmd_args arg expansion index new=$(printf "%s\n" "$1" | grep -- '.\+') readarray -t cmd_args <<< "$new"; arg=${cmd_args[$2]} case ${arg:0:1} in !) expansion=$(_ovs_vsctl_possible_completions_of_argument \ "${arg:1}" $3) index=$(($2+1)) ;; \?|\*) local tmp1 tmp2 arg2_index tmp2_noEO tmp2_EO tmp1=$(_ovs_vsctl_possible_completions_of_argument "${arg:1}" $3) tmp2=$(_ovs_vsctl_complete_argument "$1" "$(($2+1))" "$3") arg2_index=$? if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then if [ "${arg:0:1}" = "*" ]; then index=$2; else index=$(($2+1)); fi fi if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then if [ "${arg:0:1}" = "*" ]; then index=$2; else index=$(($2+1)); fi fi if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then index=$arg2_index fi if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then index=254 fi # Don't allow secondary completions to inhibit primary # completions: if [[ $tmp2 =~ ^([^E]|E[^O])*NOCOMP ]]; then tmp2="" fi tmp2_noEO="${tmp2#*EO}" tmp2_EO="${tmp2%%EO*}" expansion=$(printf "%s%s\n%s" "${tmp2_EO}" \ "${tmp1}" "${tmp2_noEO}") ;; esac printf "%s\n" "$expansion" return $index } _ovs_vsctl_detect_nospace () { if [[ $1 =~ ^([^E]|E[^O])*NOSPACE ]]; then _OVS_VSCTL_COMP_NOSPACE=true fi } _ovs_vsctl_process_messages () { local message message="${1#*BM}" message="${message%%EM*}" if [ "$test" = "true" ]; then printf -- "--- BEGIN MESSAGE" fi printf "${message}" if [ "$test" = "true" ]; then printf -- "--- END MESSAGE" fi } # colon, equal sign will mess up the completion output, just # removes the colon-word and equal-word prefix from COMPREPLY items. # # Implementation of this function refers to the __ltrim_colon_completions # function defined in bash_completion module. # # $1: Current argument # $2: $COMP_WORDBREAKS # $3: ${COMPREPLY[@]} _ovs_vsctl_trim_compreply() { local cur comp_wordbreaks local compreply cur=$1 && shift comp_wordbreaks=$1 && shift compreply=( $@ ) if [[ "$cur" == *:* && "$comp_wordbreaks" == *:* ]]; then local colon_word=${cur%${cur##*:}} local i=${#compreply[*]} cur=${cur##*:} while [ $((--i)) -ge 0 ]; do compreply[$i]=${compreply[$i]#"$colon_word"} done fi if [[ "$cur" == *=* && "$comp_wordbreaks" == *=* ]]; then local equal_word=${cur%${cur##*=}} local i=${#compreply[*]} while [ $((--i)) -ge 0 ]; do compreply[$i]=${compreply[$i]#"$equal_word"} done fi printf "%s " "${compreply[@]}" } # The general strategy here is that the same functions that decide # completions can also capture the necessary context for later # completions. This means that there is no distinction between the # processing for words that are not the current word and words that # are the current word. # # Parsing up until the command word happens starts with everything # valid; as the syntax order of ovs-vsctl is fairly strict, when types # of words that preclude other words from happending can turn them # off; this is controlled by valid_globals, valid_opts, and # valid_commands. given_opts is used to narrow down which commands # are valid based on the previously given options. # # After the command has been detected, the parsing becomes more # complicated. The cmd_pos variable is set to 0 when the command is # detected; it is used as a pointer into an array of the argument # types for that given command. The argument types are stored in both # cmd_args and raw_cmd as the main loop uses properties of arrays to # detect certain conditions, but arrays cannot be passed to functions. # To be able to deal with optional or repeatable arguments, the exit # status of the function _ovs_vsctl_complete_argument represents where # it has determined that the next argument will be. _ovs_vsctl_bashcomp () { local words cword valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts local test="false" # Does not support BASH_VERSION < 4.0 if [ ${BASH_VERSINFO[0]} -lt 4 ]; then return 0 fi # Prepare the COMP_* variables based on input. if [ "$1" = "test" ]; then test="true" export COMP_LINE="ovs-vsctl $2" tmp="ovs-vsctl"$'\n'"$(tr ' ' '\n' <<< "${COMP_LINE}x")" tmp="${tmp%x}" readarray -t COMP_WORDS \ <<< "$tmp" export COMP_WORDS export COMP_CWORD="$((${#COMP_WORDS[@]}-1))" else # If not in test mode, reassembles the COMP_WORDS and COMP_CWORD # using just space as word break. _get_comp_words_by_ref -n "\"'><=;|&(:" -w words -i cword COMP_WORDS=( "${words[@]}" ) COMP_CWORD=${cword} fi # Extract the conf.db path. db=$(sed -n 's/.*--db=\([^ ]*\).*/\1/p' <<< "$COMP_LINE") if [ -n "$db" ]; then _OVSDB_SERVER_LOCATION="$db" fi # If having trouble accessing the database, return. if ! _ovs_vsctl get-manager 1>/dev/null 2>/dev/null; then return 1; fi _OVS_VSCTL_PARSED_ARGS=() _OVS_VSCTL_NEW_RECORDS=() cmd_pos=-1 valid_globals=true valid_opts=true valid_commands=true given_opts="" index=1 for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do _OVS_VSCTL_COMP_NOSPACE=false local completion completion="" if [ $cmd_pos -gt -1 ]; then local tmp tmp_noop arg possible_newindex tmp=$(_ovs_vsctl_complete_argument "$raw_cmd" "$cmd_pos" "$word") possible_newindex=$? # Check for nospace. _ovs_vsctl_detect_nospace $tmp # Remove all options. tmp_noop="${tmp#*EO}" # Allow commands to specify that they should not be # completed if ! [[ $tmp =~ ^([^E]|E[^O])*NOCOMP ]]; then # Directly assignment, since 'completion' is guaranteed to # to be empty. completion="$tmp_noop" # If intermediate completion is empty, it means that the current # argument is invalid. And we should not continue. if [ $index -lt $COMP_CWORD ] \ && (! _ovs_vsctl_detect_nonzero_completions "$completion"); then _ovs_vsctl_process_messages "BM\nCannot complete \'${COMP_WORDS[$index]}\' at index ${index}:\n$(_ovs_vsctl_get_PS1)${COMP_LINE}EM\nEO\n" return 1 fi else # Only allow messages when there is no completion # printout and when on the current word. if [ $index -eq $COMP_CWORD ]; then _ovs_vsctl_process_messages "${tmp}" fi # Append the new record to _OVS_VSCTL_NEW_RECORDS. _OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]="${_OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]} $tmp_noop" fi if [[ $cmd_pos -lt ${#cmd_args} ]]; then _OVS_VSCTL_PARSED_ARGS["${cmd_args[$cmd_pos]:1}"]=$word fi if [ $possible_newindex -lt 254 ]; then cmd_pos=$possible_newindex fi fi if [ $valid_globals == true ]; then tmp=$(_ovs_vsctl_bashcomp_globalopt $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" fi if [ $valid_opts == true ]; then tmp=$(_ovs_vsctl_bashcomp_localopt "$given_opts" $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" if [ $index -lt $COMP_CWORD ] \ && _ovs_vsctl_detect_nonzero_completions "$tmp"; then valid_globals=false given_opts="${given_opts} ${word}" fi fi if [ $valid_commands = true ]; then tmp=$(_ovs_vsctl_bashcomp_command "$given_opts" $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" if [ $index -lt $COMP_CWORD ] \ && _ovs_vsctl_detect_nonzero_completions "$tmp"; then valid_globals=false valid_opts=false valid_commands=false cmd_pos=0 raw_cmd=$(_ovs_vsctl_expand_command "$word") readarray -t cmd_args <<< "$raw_cmd" fi fi if [ "$word" = "--" ] && [ $index -lt $COMP_CWORD ]; then # Empty the parsed args array. _OVS_VSCTL_PARSED_AGS=() cmd_pos=-1 # No longer allow global options after '--'. valid_globals=false valid_opts=true valid_commands=true given_opts="" fi completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")" if [ $index -eq $COMP_CWORD ]; then if [ "$test" = "true" ]; then completion="$(_ovs_vsctl_trim_compreply "$word" ":=" ${completion} | \ tr ' ' '\n')" if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then printf "%s" "$completion" | sed -e '/^$/d' else printf "%s" "$completion" | sed -e '/^$/d; s/$/ /g' fi printf "\n" else if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then compopt -o nospace COMPREPLY=( $(compgen -W "${completion}" -- $word) ) else compopt +o nospace COMPREPLY=( $(compgen -W "${completion}" -- $word) ) fi COMPREPLY=( $(_ovs_vsctl_trim_compreply "$word" \ "${COMP_WORDBREAKS}" ${COMPREPLY[@]}) ) fi fi index=$(($index+1)) done } if [ "$1" = "test" ]; then _ovs_vsctl_bashcomp "$@" else complete -F _ovs_vsctl_bashcomp ovs-vsctl fi openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-vsctl.8.in000066400000000000000000001133401514270232600234330ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-vsctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-vsctl . .SH NAME ovs\-vsctl \- utility for querying and configuring \fBovs\-vswitchd\fR . .SH SYNOPSIS \fBovs\-vsctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]... . .SH DESCRIPTION The \fBovs\-vsctl\fR program configures \fBovs\-vswitchd\fR(8) by providing a high\-level interface to its configuration database. See \fBovs\-vswitchd.conf.db\fR(5) for comprehensive documentation of the database schema. .PP \fBovs\-vsctl\fR connects to an \fBovsdb\-server\fR process that maintains an Open vSwitch configuration database. Using this connection, it queries and possibly applies changes to the database, depending on the supplied commands. Then, if it applied any changes, by default it waits until \fBovs\-vswitchd\fR has finished reconfiguring itself before it exits. (If you use \fBovs\-vsctl\fR when \fBovs\-vswitchd\fR is not running, use \fB\-\-no\-wait\fR.) .PP \fBovs\-vsctl\fR can perform any number of commands in a single run, implemented as a single atomic transaction against the database. .PP The \fBovs\-vsctl\fR command line begins with global options (see \fBOPTIONS\fR below for details). The global options are followed by one or more commands. Each command should begin with \fB\-\-\fR by itself as a command-line argument, to separate it from the following commands. (The \fB\-\-\fR before the first command is optional.) The command itself starts with command-specific options, if any, followed by the command name and any arguments. See \fBEXAMPLES\fR below for syntax examples. . .SS "Linux VLAN Bridging Compatibility" The \fBovs\-vsctl\fR program supports the model of a bridge implemented by Open vSwitch, in which a single bridge supports ports on multiple VLANs. In this model, each port on a bridge is either a trunk port that potentially passes packets tagged with 802.1Q headers that designate VLANs or it is assigned a single implicit VLAN that is never tagged with an 802.1Q header. .PP For compatibility with software designed for the Linux bridge, \fBovs\-vsctl\fR also supports a model in which traffic associated with a given 802.1Q VLAN is segregated into a separate bridge. A special form of the \fBadd\-br\fR command (see below) creates a ``fake bridge'' within an Open vSwitch bridge to simulate this behavior. When such a ``fake bridge'' is active, \fBovs\-vsctl\fR will treat it much like a bridge separate from its ``parent bridge,'' but the actual implementation in Open vSwitch uses only a single bridge, with ports on the fake bridge assigned the implicit VLAN of the fake bridge of which they are members. (A fake bridge for VLAN 0 receives packets that have no 802.1Q tag or a tag with VLAN 0.) . .SH OPTIONS . The following options affect the behavior \fBovs\-vsctl\fR as a whole. Some individual commands also accept their own options, which are given just before the command name. If the first command on the command line has options, then those options must be separated from the global options by \fB\-\-\fR. . .IP "\fB\-\-db=\fIserver\fR" Sets \fIserver\fR as the database server that \fBovs\-vsctl\fR contacts to query or modify configuration. \fIserver\fR may be an OVSDB active or passive connection method, as described in \fBovsdb\fR(7). The default is \fBunix:@RUNDIR@/db.sock\fR. .IP "\fB\-\-no\-wait\fR" Prevents \fBovs\-vsctl\fR from waiting for \fBovs\-vswitchd\fR to reconfigure itself according to the modified database. This option should be used if \fBovs\-vswitchd\fR is not running; otherwise, \fBovs\-vsctl\fR will not exit until \fBovs\-vswitchd\fR starts. .IP This option has no effect if the commands specified do not change the database. . .IP "\fB\-\-no\-syslog\fR" By default, \fBovs\-vsctl\fR logs its arguments and the details of any changes that it makes to the system log. This option disables this logging. .IP This option is equivalent to \fB\-\-verbose=vsctl:syslog:warn\fR. . .IP "\fB\-\-oneline\fR" Modifies the output format so that the output for each command is printed on a single line. New-line characters that would otherwise separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that would otherwise appear in the output are doubled. Prints a blank line for each command that has no output. This option does not affect the formatting of output from the \fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR below. . .IP "\fB\-\-dry\-run\fR" Prevents \fBovs\-vsctl\fR from actually modifying the database. . .IP "\fB\-t \fIsecs\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" By default, or with a \fIsecs\fR of \fB0\fR, \fBovs\-vsctl\fR waits forever for a response from the database. This option limits runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBovs\-vsctl\fR will exit with a \fBSIGALRM\fR signal. (A timeout would normally happen only if the database cannot be contacted, or if the system is overloaded.) . .IP "\fB\-\-retry\fR" Without this option, if \fBovs\-vsctl\fR connects outward to the database server (the default) then \fBovs\-vsctl\fR will try to connect once and exit with an error if the connection fails (which usually means that \fBovsdb\-server\fR is not running). .IP With this option, or if \fB\-\-db\fR specifies that \fBovs\-vsctl\fR should listen for an incoming connection from the database server, then \fBovs\-vsctl\fR will wait for a connection to the database forever. .IP Regardless of this setting, \fB\-\-timeout\fR always limits how long \fBovs\-vsctl\fR will wait. . .SS "Table Formatting Options" These options control the format of output from the \fBlist\fR and \fBfind\fR commands. .so lib/table.man . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .so lib/vlog.man .so lib/common.man . .SH COMMANDS The commands implemented by \fBovs\-vsctl\fR are described in the sections below. .SS "Open vSwitch Commands" These commands work with an Open vSwitch as a whole. . .IP "\fBinit\fR" Initializes the Open vSwitch database, if it is empty. If the database has already been initialized, this command has no effect. .IP Any successful \fBovs\-vsctl\fR command automatically initializes the Open vSwitch database if it is empty. This command is provided to initialize the database without executing any other command. . .IP "[\fB\-\-filter\fR=\fIfilter\-rule\fR[,\fIfilter\-rule\fR]...] \fBshow\fR" Prints a brief overview of the database contents. If \fB\-\-filter\fR is specified, output is filtered according to the rules. Each \fIfilter\-rule\fR has the form \fBtable\-name\fR(\fIfilter\fR[|\fIfilter\fR]...). The row in this table is shown only if the printed version of it, including all the rows referenced from this one, contains one of the \fIfilter\fRs as a sub-string. . .IP "\fBemer\-reset\fR" Reset the configuration into a clean state. It deconfigures OpenFlow controllers, OVSDB servers, and SSL/TLS, and deletes port mirroring, \fBfail_mode\fR, NetFlow, sFlow, and IPFIX configuration. This command also removes all \fBother\-config\fR keys from all database records, except that \fBother\-config:hwaddr\fR is preserved if it is present in a Bridge record. Other networking configuration is left as-is. . .SS "Bridge Commands" These commands examine and manipulate Open vSwitch bridges. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-br \fIbridge\fR" Creates a new bridge named \fIbridge\fR. Initially the bridge will have no ports (other than \fIbridge\fR itself). .IP Without \fB\-\-may\-exist\fR, attempting to create a bridge that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIbridge\fR already exists as a real bridge. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-br \fIbridge parent vlan\fR" Creates a ``fake bridge'' named \fIbridge\fR within the existing Open vSwitch bridge \fIparent\fR, which must already exist and must not itself be a fake bridge. The new fake bridge will be on 802.1Q VLAN \fIvlan\fR, which must be an integer between 0 and 4095. The parent bridge must not already have a fake bridge for \fIvlan\fR. Initially \fIbridge\fR will have no ports (other than \fIbridge\fR itself). .IP Without \fB\-\-may\-exist\fR, attempting to create a bridge that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIbridge\fR already exists as a VLAN bridge under \fIparent\fR for \fIvlan\fR. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-br \fIbridge\fR" Deletes \fIbridge\fR and all of its ports. If \fIbridge\fR is a real bridge, this command also deletes any fake bridges that were created with \fIbridge\fR as parent, including all of their ports. .IP Without \fB\-\-if\-exists\fR, attempting to delete a bridge that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a bridge that does not exist has no effect. . .IP "[\fB\-\-real\fR|\fB\-\-fake\fR] \fBlist\-br\fR" Lists all existing real and fake bridges on standard output, one per line. With \fB\-\-real\fR or \fB\-\-fake\fR, only bridges of that type are returned. . .IP "\fBbr\-exists \fIbridge\fR" Tests whether \fIbridge\fR exists as a real or fake bridge. If so, \fBovs\-vsctl\fR exits successfully with exit code 0. If not, \fBovs\-vsctl\fR exits unsuccessfully with exit code 2. . .IP "\fBbr\-to\-vlan \fIbridge\fR" If \fIbridge\fR is a fake bridge, prints the bridge's 802.1Q VLAN as a decimal integer. If \fIbridge\fR is a real bridge, prints 0. . .IP "\fBbr\-to\-parent \fIbridge\fR" If \fIbridge\fR is a fake bridge, prints the name of its parent bridge. If \fIbridge\fR is a real bridge, print \fIbridge\fR. . .IP "\fBbr\-set\-external\-id \fIbridge key\fR [\fIvalue\fR]" Sets or clears an ``external ID'' value on \fIbridge\fR. These values are intended to identify entities external to Open vSwitch with which \fIbridge\fR is associated, e.g. the bridge's identifier in a virtualization management platform. The Open vSwitch database schema specifies well-known \fIkey\fR values, but \fIkey\fR and \fIvalue\fR are otherwise arbitrary strings. .IP If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for \fIbridge\fR, overwriting any previous value. If \fIvalue\fR is omitted, then \fIkey\fR is removed from \fIbridge\fR's set of external IDs (if it was present). .IP For real bridges, the effect of this command is similar to that of a \fBset\fR or \fBremove\fR command in the \fBexternal\-ids\fR column of the \fBBridge\fR table. For fake bridges, it actually modifies keys with names prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table. . .IP "\fBbr\-get\-external\-id \fIbridge\fR [\fIkey\fR]" Queries the external IDs on \fIbridge\fR. If \fIkey\fR is specified, the output is the value for that \fIkey\fR or the empty string if \fIkey\fR is unset. If \fIkey\fR is omitted, the output is \fIkey\fB=\fIvalue\fR, one per line, for each key-value pair. .IP For real bridges, the effect of this command is similar to that of a \fBget\fR command in the \fBexternal\-ids\fR column of the \fBBridge\fR table. For fake bridges, it queries keys with names prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table. . .SS "Port Commands" . These commands examine and manipulate Open vSwitch ports. These commands treat a bonded port as a single entity. . .IP "\fBlist\-ports \fIbridge\fR" Lists all of the ports within \fIbridge\fR on standard output, one per line. The local port \fIbridge\fR is not included in the list. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-port \fIbridge port \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fR=\fIvalue\fR]\&...\fR" Creates on \fIbridge\fR a new port named \fIport\fR from the network device of the same name. .IP Optional arguments set values of column in the Port record created by the command. For example, \fBtag=9\fR would make the port an access port for VLAN 9. The syntax is the same as that for the \fBset\fR command (see \fBDatabase Commands\fR below). .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIbridge\fR and is not a bonded port. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-port \fR[\fIbridge\fR] \fIport\fR" Deletes \fIport\fR. If \fIbridge\fR is omitted, \fIport\fR is removed from whatever bridge contains it; if \fIbridge\fR is specified, it must be the real or fake bridge that contains \fIport\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a port that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a port that does not exist has no effect. . .IP "[\fB\-\-if\-exists\fR] \fB\-\-with\-iface del\-port \fR[\fIbridge\fR] \fIiface\fR" Deletes the port named \fIiface\fR or that has an interface named \fIiface\fR. If \fIbridge\fR is omitted, the port is removed from whatever bridge contains it; if \fIbridge\fR is specified, it must be the real or fake bridge that contains the port. .IP Without \fB\-\-if\-exists\fR, attempting to delete the port for an interface that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete the port for an interface that does not exist has no effect. . .IP "\fBport\-to\-br \fIport\fR" Prints the name of the bridge that contains \fIport\fR on standard output. . .SS "Bond Commands" . These commands work with ports that have more than one interface, which Open vSwitch calls ``bonds.'' . .IP "[\fB\-\-fake\-iface\fR] \fBadd\-bond \fIbridge port iface\fR\&... [\fIcolumn\fR[\fB:\fIkey\fR]\fR=\fIvalue\fR]\&...\fR" Creates on \fIbridge\fR a new port named \fIport\fR that bonds together the network devices given as each \fIiface\fR. At least two interfaces must be named. If the interfaces are DPDK enabled then the transaction will need to include operations to explicitly set the interface type to 'dpdk'. .IP Optional arguments set values of column in the Port record created by the command. The syntax is the same as that for the \fBset\fR command (see \fBDatabase Commands\fR below). .IP With \fB\-\-fake\-iface\fR, a fake interface with the name \fIport\fR is created. This should only be used for compatibility with legacy software that requires it. .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIbridge\fR and bonds together exactly the specified interfaces. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-bond\-iface \fIbond iface\fR" Adds \fIiface\fR as a new bond interface to the existing port \fIbond\fR. If \fIbond\fR previously had only one port, this transforms it into a bond. .IP Without \fB\-\-may\-exist\fR, attempting to add an \fIiface\fR that is already part of \fIbond\fR is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIiface\fR is already part of \fIbond\fR. (It is still an error if \fIiface\fR is an interface of some other port or bond.) . .IP "[\fB\-\-if\-exists\fR] \fBdel\-bond\-iface\fR [\fIbond\fR] \fIiface\fR" Removes \fIiface\fR from its port. If \fIbond\fR is omitted, \fIiface\fR is removed from whatever port contains it; if \fIbond\fR is specified, it must be the port that contains \fIbond\fR. .IP If removing \fIiface\fR causes its port to have only a single interface, then that port transforms from a bond into an ordinary port. It is an error if \fIiface\fR is the only interface in its port. .IP Without \fB\-\-if\-exists\fR, attempting to delete an interface that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete an interface that does not exist has no effect. . .SS "Interface Commands" . These commands examine the interfaces attached to an Open vSwitch bridge. These commands treat a bonded port as a collection of two or more interfaces, rather than as a single port. . .IP "\fBlist\-ifaces \fIbridge\fR" Lists all of the interfaces within \fIbridge\fR on standard output, one per line. The local port \fIbridge\fR is not included in the list. . .IP "\fBiface\-to\-br \fIiface\fR" Prints the name of the bridge that contains \fIiface\fR on standard output. . .SS "Conntrack Zone Commands" These commands query and modify datapath CT zones, Timeout Policies and Limits. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-zone\-tp \fIdatapath \fBzone=\fIzone_id \fIpolicies\fR" Creates a conntrack zone timeout policy with \fIzone_id\fR in \fIdatapath\fR. The \fIpolicies\fR consist of \fIkey\fB=\fIvalue\fR pairs, separated by spaces. For example, \fBicmp_first=30 icmp_reply=60\fR specifies a 30-second timeout policy for the first ICMP packet and a 60-second policy for ICMP reply packets. See the \fBCT_Timeout_Policy\fR table in \fBovs-vswitchd.conf.db\fR(5) for the supported keys. .IP Without \fB\-\-may\-exist\fR, attempting to add a \fIpolicy\fR for \fIzone_id\fR that already has a policy is an error. With \fB\-\-may\-exist\fR, this command does nothing if policy for \fIzone_id\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-zone\-tp \fIdatapath \fBzone=\fIzone_id\fR" Delete the timeout policy associated with \fIzone_id\fR from \fIdatapath\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a policy for zone that does not exist or doesn't have a policy is an error. With \fB\-\-if\-exists\fR, attempting to delete a a policy that does not exist has no effect. . .IP "\fBlist\-zone\-tp \fIdatapath\fR" Prints the timeout policies of all zones in \fIdatapath\fR. . .IP "\fBset\-zone\-limit \fIdatapath \fIzone_id\fR|\fBdefault \fIzone_limit\fR" Sets a conntrack zone limit with \fIzone_id\fR|\fIdefault\fR in \fIdatapath\fR. The \fIlimit\fR with value \fB0\fR means unlimited. .IP . .IP "[\fB\-\-if\-exists\fR] \fBdel\-zone\-limit \fIdatapath \fIzone_id\fR|\fBdefault\fR" Delete the limit associated with \fIzone_id\fR from \fIdatapath\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a limit for zone that does not exist or doesn't have a limit is an error. With \fB\-\-if\-exists\fR, attempting to delete a limit that does not exist has no effect. . .IP "\fBlist\-zone\-limits \fIdatapath\fR" Prints the limits of all zones in \fIdatapath\fR. . .SS "Datapath Capabilities Command" The command query datapath capabilities. . .IP "\fBlist\-dp\-cap \fIdatapath\fR" Prints the datapath's capabilities. . .SS "OpenFlow Controller Connectivity" . \fBovs\-vswitchd\fR can perform all configured bridging and switching locally, or it can be configured to communicate with one or more external OpenFlow controllers. The switch is typically configured to connect to a primary controller that takes charge of the bridge's flow table to implement a network policy. In addition, the switch can be configured to listen to connections from service controllers. Service controllers are typically used for occasional support and maintenance, e.g. with \fBovs\-ofctl\fR. . .IP "\fBget\-controller\fR \fIbridge\fR" Prints the configured controller target. . .IP "\fBdel\-controller\fR \fIbridge\fR" Deletes the configured controller target. . .IP "\fBset\-controller\fR \fIbridge\fR \fItarget\fR\&..." Sets the configured controller target or targets. Each \fItarget\fR may use any of the following forms: . .RS .so lib/vconn-active.man .so lib/vconn-passive.man .RE . .ST "Controller Failure Settings" .PP When a controller is configured, it is, ordinarily, responsible for setting up all flows on the switch. Thus, if the connection to the controller fails, no new network connections can be set up. If the connection to the controller stays down long enough, no packets can pass through the switch at all. .PP If the value is \fBstandalone\fR, or if neither of these settings is set, \fBovs\-vswitchd\fR will take over responsibility for setting up flows when no message has been received from the controller for three times the inactivity probe interval. In this mode, \fBovs\-vswitchd\fR causes the datapath to act like an ordinary MAC-learning switch. \fBovs\-vswitchd\fR will continue to retry connecting to the controller in the background and, when the connection succeeds, it discontinues its standalone behavior. .PP If this option is set to \fBsecure\fR, \fBovs\-vswitchd\fR will not set up flows on its own when the controller connection fails. . .IP "\fBget\-fail\-mode\fR \fIbridge\fR" Prints the configured failure mode. . .IP "\fBdel\-fail\-mode\fR \fIbridge\fR" Deletes the configured failure mode. . .IP "\fBset\-fail\-mode\fR \fIbridge\fR \fBstandalone\fR|\fBsecure\fR" Sets the configured failure mode. . .SS "Manager Connectivity" . These commands manipulate the \fBmanager_options\fR column in the \fBOpen_vSwitch\fR table and rows in the \fBManagers\fR table. When \fBovsdb\-server\fR is configured to use the \fBmanager_options\fR column for OVSDB connections (as described in the startup scripts provided with Open vSwitch; the corresponding \fBovsdb\-server\fR command option is \fB--remote=db:Open_vSwitch,Open_vSwitch,manager_options\fR), this allows the administrator to use \fBovs\-vsctl\fR to configure database connections. . .IP "\fBget\-manager\fR" Prints the configured manager(s). . .IP "\fBdel\-manager\fR" Deletes the configured manager(s). . .IP "\fBset\-manager\fR \fItarget\fR\&..." Sets the configured manager target or targets. Each \fItarget\fR may be an OVSDB active or passive connection method, e.g. \fBpssl:6640\fR, as described in \fBovsdb\fR(7). . .SS "SSL/TLS Configuration" When \fBovs\-vswitchd\fR is configured to connect over SSL/TLS for management or controller connectivity, the following parameters are required: .TP \fIprivate-key\fR Specifies a PEM file containing the private key used as the virtual switch's identity for SSL/TLS connections to the controller. .TP \fIcertificate\fR Specifies a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the virtual switch's private key, identifying a trustworthy switch. .TP \fIca-cert\fR Specifies a PEM file containing the CA certificate used to verify that the virtual switch is connected to a trustworthy controller. .PP These files are read only once, at \fBovs\-vswitchd\fR startup time. If their contents change, \fBovs\-vswitchd\fR must be killed and restarted. .PP These SSL/TLS settings apply to all SSL/TLS connections made by the virtual switch. . .IP "\fBget\-ssl\fR" Prints the SSL/TLS configuration. . .IP "\fBdel\-ssl\fR" Deletes the current SSL/TLS configuration. . .IP "[\fB\-\-bootstrap\fR] \fBset\-ssl\fR \fIprivate-key\fR \fIcertificate\fR \fIca-cert\fR" Sets the SSL/TLS configuration. The \fB\-\-bootstrap\fR option is described below. . .ST "CA Certificate Bootstrap" .PP Ordinarily, all of the files named in the SSL/TLS configuration must exist when \fBovs\-vswitchd\fR starts. However, if the \fIca-cert\fR file does not exist and the \fB\-\-bootstrap\fR option is given, then \fBovs\-vswitchd\fR will attempt to obtain the CA certificate from the controller on its first SSL/TLS connection and save it to the named PEM file. If it is successful, it will immediately drop the connection and reconnect, and from then on all SSL/TLS connections must be authenticated by a certificate signed by the CA certificate thus obtained. .PP \fBThis option exposes the SSL/TLS connection to a man-in-the-middle attack obtaining the initial CA certificate\fR, but it may be useful for bootstrapping. .PP This option is only useful if the controller sends its CA certificate as part of the SSL/TLS certificate chain. SSL/TLS protocols do not require the controller to send the CA certificate. . .SS "Auto-Attach Commands" . The IETF Auto-Attach SPBM draft standard describes a compact method of using IEEE 802.1AB Link Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq Shortest Path Bridging (SPB) network to automatically attach network devices to individual services in a SPB network. The intent here is to allow network applications and devices using OVS to be able to easily take advantage of features offered by industry standard SPB networks. A fundamental element of the Auto-Attach feature is to map traditional VLANs onto SPB I_SIDs. These commands manage the Auto-Attach I-SID/VLAN mappings. . .IP "\fBadd\-aa\-mapping \fIbridge i-sid vlan\fR" Creates a new Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR and \fIvlan\fR. . .IP "\fBdel\-aa\-mapping \fIbridge i-sid vlan\fR" Deletes an Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR and \fIvlan\fR. .IP "\fBget\-aa\-mapping \fIbridge\fR" Lists all of the Auto-Attach mappings within \fIbridge\fR on standard output. . .SS "Database Commands" . These commands query and modify the contents of \fBovsdb\fR tables. They are a slight abstraction of the \fBovsdb\fR interface and as such they operate at a lower level than other \fBovs\-vsctl\fR commands. .PP .ST "Identifying Tables, Records, and Columns" .PP Each of these commands has a \fItable\fR parameter to identify a table within the database. Many of them also take a \fIrecord\fR parameter that identifies a particular record within a table. The \fIrecord\fR parameter may be the UUID for a record, and many tables offer additional ways to identify records. Some commands also take \fIcolumn\fR parameters that identify a particular field within the records in a table. .PP For a list of tables and their columns, see \fBovs-vswitchd.conf.db\fR(5) or see the table listing from the \fB--help\fR option. .PP Record names must be specified in full and with correct capitalization, except that UUIDs may be abbreviated to their first 4 (or more) hex digits, as long as that is unique within the table. Names of tables and columns are not case-sensitive, and \fB\-\fR and \fB_\fR are treated interchangeably. Unique abbreviations of table and column names are acceptable, e.g. \fBnet\fR or \fBn\fR is sufficient to identify the \fBNetFlow\fR table. . .so lib/db-ctl-base.man .SH "EXAMPLES" Create a new bridge named br0 and add port eth0 to it: .IP .B "ovs\-vsctl add\-br br0" .br .B "ovs\-vsctl add\-port br0 eth0" .PP Alternatively, perform both operations in a single atomic transaction: .IP .B "ovs\-vsctl add\-br br0 \-\- add\-port br0 eth0" .PP Delete bridge \fBbr0\fR, reporting an error if it does not exist: .IP .B "ovs\-vsctl del\-br br0" .PP Delete bridge \fBbr0\fR if it exists: .IP .B "ovs\-vsctl \-\-if\-exists del\-br br0" .PP Set the \fBqos\fR column of the \fBPort\fR record for \fBeth0\fR to point to a new \fBQoS\fR record, which in turn points with its queue 0 to a new \fBQueue\fR record: .IP .B "ovs\-vsctl \-\- set port eth0 qos=@newqos \-\- \-\-id=@newqos create qos type=linux\-htb other\-config:max\-rate=1000000 queues:0=@newqueue \-\- \-\-id=@newqueue create queue other\-config:min\-rate=1000000 other\-config:max\-rate=1000000" .SH "CONFIGURATION COOKBOOK" .SS "Port Configuration" .PP Add an ``internal port'' \fBvlan10\fR to bridge \fBbr0\fR as a VLAN access port for VLAN 10, and configure it with an IP address: .IP .B "ovs\-vsctl add\-port br0 vlan10 tag=10 \-\- set Interface vlan10 type=internal" .IP .B "ip addr add 192.168.0.123/24 dev vlan10" . .PP Add a GRE tunnel port \fBgre0\fR to remote IP address 1.2.3.4 to bridge \fBbr0\fR: .IP .B "ovs\-vsctl add\-port br0 gre0 \-\- set Interface gre0 type=gre options:remote_ip=1.2.3.4" . .SS "Port Mirroring" .PP Mirror all packets received or sent on \fBeth0\fR or \fBeth1\fR onto \fBeth2\fR, assuming that all of those ports exist on bridge \fBbr0\fR (as a side-effect this causes any packets received on \fBeth2\fR to be ignored): .IP .B "ovs\-vsctl \-\- set Bridge br0 mirrors=@m \(rs" .IP .B "\-\- \-\-id=@eth0 get Port eth0 \(rs" .IP .B "\-\- \-\-id=@eth1 get Port eth1 \(rs" .IP .B "\-\- \-\-id=@eth2 get Port eth2 \(rs" .IP .B "\-\- \-\-id=@m create Mirror name=mymirror select-dst-port=@eth0,@eth1 select-src-port=@eth0,@eth1 output-port=@eth2" .PP Remove the mirror created above from \fBbr0\fR, which also destroys the Mirror record (since it is now unreferenced): .IP .B "ovs\-vsctl \-\- \-\-id=@rec get Mirror mymirror \(rs" .IP .B "\-\- remove Bridge br0 mirrors @rec" .PP The following simpler command also works: .IP .B "ovs\-vsctl clear Bridge br0 mirrors" .SS "Quality of Service (QoS)" .PP Create a \fBlinux\-htb\fR QoS record that points to a few queues and use it on \fBeth0\fR and \fBeth1\fR: .IP .B "ovs\-vsctl \-\- set Port eth0 qos=@newqos \(rs" .IP .B "\-\- set Port eth1 qos=@newqos \(rs" .IP .B "\-\- \-\-id=@newqos create QoS type=linux\-htb other\-config:max\-rate=1000000000 queues=0=@q0,1=@q1 \(rs" .IP .B "\-\- \-\-id=@q0 create Queue other\-config:min\-rate=100000000 other\-config:max\-rate=100000000 \(rs" .IP .B "\-\- \-\-id=@q1 create Queue other\-config:min\-rate=500000000" .PP Deconfigure the QoS record above from \fBeth1\fR only: .IP .B "ovs\-vsctl clear Port eth1 qos" .PP To deconfigure the QoS record from both \fBeth0\fR and \fBeth1\fR and then delete the QoS record (which must be done explicitly because unreferenced QoS records are not automatically destroyed): .IP .B "ovs\-vsctl \-\- destroy QoS eth0 \-\- clear Port eth0 qos \-\- clear Port eth1 qos" .PP (This command will leave two unreferenced Queue records in the database. To delete them, use "\fBovs\-vsctl list Queue\fR" to find their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR \fIuuid2\fR" to destroy each of them or use "\fBovs\-vsctl -- --all destroy Queue\fR" to delete all records.) .SS "Connectivity Monitoring" .PP Monitor connectivity to a remote maintenance point on eth0. .IP .B "ovs\-vsctl set Interface eth0 cfm_mpid=1" .PP Deconfigure connectivity monitoring from above: .IP .B "ovs\-vsctl clear Interface eth0 cfm_mpid" .SS "NetFlow" .PP Configure bridge \fBbr0\fR to send NetFlow records to UDP port 5566 on host 192.168.0.34, with an active timeout of 30 seconds: .IP .B "ovs\-vsctl \-\- set Bridge br0 netflow=@nf \(rs" .IP .B "\-\- \-\-id=@nf create NetFlow targets=\(rs\(dq192.168.0.34:5566\(rs\(dq active\-timeout=30" .PP Update the NetFlow configuration created by the previous command to instead use an active timeout of 60 seconds: .IP .B "ovs\-vsctl set NetFlow br0 active_timeout=60" .PP Deconfigure the NetFlow settings from \fBbr0\fR, which also destroys the NetFlow record (since it is now unreferenced): .IP .B "ovs\-vsctl clear Bridge br0 netflow" .SS "sFlow" .PP Configure bridge \fBbr0\fR to send sFlow records to a collector on 10.0.0.1 at port 6343, using \fBeth1\fR's IP address as the source, with specific sampling parameters: .IP .B "ovs\-vsctl \-\- \-\-id=@s create sFlow agent=eth1 target=\(rs\(dq10.0.0.1:6343\(rs\(dq header=128 sampling=64 polling=10 \(rs" .IP .B "\-\- set Bridge br0 sflow=@s" .PP Deconfigure sFlow from \fBbr0\fR, which also destroys the sFlow record (since it is now unreferenced): .IP .B "ovs\-vsctl \-\- clear Bridge br0 sflow" .SS "IPFIX" .PP Configure bridge \fBbr0\fR to send one IPFIX flow record per packet sample to UDP port 4739 on host 192.168.0.34, with Observation Domain ID 123 and Observation Point ID 456, a flow cache active timeout of 1 minute (60 seconds), maximum flow cache size of 13 flows, and flows sampled on output port with tunnel info(sampling on input and output port is enabled by default if not disabled) : .IP .B "ovs\-vsctl \-\- set Bridge br0 ipfix=@i \(rs" .IP .B "\-\- \-\-id=@i create IPFIX targets=\(rs\(dq192.168.0.34:4739\(rs\(dq obs_domain_id=123 obs_point_id=456 cache_active_timeout=60 cache_max_flows=13 \(rs" .IP .B "other_config:enable-input-sampling=false other_config:enable-tunnel-sampling=true" .PP Deconfigure the IPFIX settings from \fBbr0\fR, which also destroys the IPFIX record (since it is now unreferenced): .IP .B "ovs\-vsctl clear Bridge br0 ipfix" .SS "802.1D Spanning Tree Protocol (STP)" .PP Configure bridge \fBbr0\fR to participate in an 802.1D spanning tree: .IP .B "ovs\-vsctl set Bridge br0 stp_enable=true" .PP Set the bridge priority of \fBbr0\fR to 0x7800: .IP .B "ovs\-vsctl set Bridge br0 other_config:stp-priority=0x7800" .PP Set the path cost of port \fBeth0\fR to 10: .IP .B "ovs\-vsctl set Port eth0 other_config:stp-path-cost=10" .PP Deconfigure STP from above: .IP .B "ovs\-vsctl set Bridge br0 stp_enable=false" .PP .SS "Multicast Snooping" .PP Configure bridge \fBbr0\fR to enable multicast snooping: .IP .B "ovs\-vsctl set Bridge br0 mcast_snooping_enable=true" .PP Set the multicast snooping aging time \fBbr0\fR to 300 seconds: .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-aging-time=300" .PP Set the multicast snooping table size \fBbr0\fR to 2048 entries: .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-table-size=2048" .PP Disable flooding of unregistered multicast packets to all ports. When set to \fBtrue\fR, the switch will send unregistered multicast packets only to ports connected to multicast routers. When it is set to \fBfalse\fR, the switch will send them to all ports. This command disables the flood of unregistered packets on bridge \fBbr0\fR. .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-disable-flood-unregistered=true" .PP Enable flooding of multicast packets (except Reports) on a specific port. .IP .B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood=true" .PP Enable flooding of Reports on a specific port. .IP .B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood-reports=true" .PP Deconfigure multicasting snooping from above: .IP .B "ovs\-vsctl set Bridge br0 mcast_snooping_enable=false" .PP .SS "802.1D-2004 Rapid Spanning Tree Protocol (RSTP)" .PP Configure bridge \fBbr0\fR to participate in an 802.1D-2004 Rapid Spanning Tree: .IP .B "ovs\-vsctl set Bridge br0 rstp_enable=true" .PP Set the bridge address of \fBbr0\fR to 00:aa:aa:aa:aa:aa : .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-address=00:aa:aa:aa:aa:aa" .PP Set the bridge priority of \fBbr0\fR to 0x7000. The value must be specified in decimal notation and should be a multiple of 4096 (if not, it is rounded down to the nearest multiple of 4096). The default priority value is 0x800 (32768). .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-priority=28672" .PP Set the bridge ageing time of \fBbr0\fR to 1000 s. The ageing time value should be between 10 s and 1000000 s. The default value is 300 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-ageing-time=1000" .PP Set the bridge force protocol version of \fBbr0\fR to 0. The force protocol version has two acceptable values: 0 (STP compatibility mode) and 2 (normal operation). .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-force-protocol-version=0" .PP Set the bridge max age of \fBbr0\fR to 10 s. The max age value should be between 6 s and 40 s. The default value is 20 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-max-age=10" .PP Set the bridge forward delay of \fBbr0\fR to 15 s. This value should be between 4 s and 30 s. The default value is 15 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-forward-delay=15" .PP Set the bridge transmit hold count of \fBbr0\fR to 7 s. This value should be between 1 s and 10 s. The default value is 6 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-transmit-hold-count=7" .PP Enable RSTP on the Port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-enable=true" .PP Disable RSTP on the Port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-enable=false" .PP Set the priority of port \fBeth0\fR to 32. The value must be specified in decimal notation and should be a multiple of 16 (if not, it is rounded down to the nearest multiple of 16). The default priority value is 0x80 (128). .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-priority=32" .PP Set the port number of port \fBeth0\fR to 3: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-num=3" .PP Set the path cost of port \fBeth0\fR to 150: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-path-cost=150" .PP Set the admin edge value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-admin-edge=true" .PP Set the auto edge value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-auto-edge=true" .PP Set the admin point to point MAC value of port \fBeth0\fR. Acceptable values are \fB0\fR (not point-to-point), \fB1\fR (point-to-point, the default value) or \fB2\fR (automatic detection). The auto-detection mode is not currently implemented, and the value \fB2\fR has the same effect of \fB0\fR (not point-to-point). .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-admin-p2p-mac=1" .PP Set the admin port state value of port \fBeth0\fR. \fBtrue\fR is the default value. .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-admin-port-state=false" .PP Set the mcheck value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-mcheck=true" .PP Deconfigure RSTP from above: .IP .B "ovs\-vsctl set Bridge br0 rstp_enable=false" .PP .SS "OpenFlow Version" .PP Configure bridge \fBbr0\fR to support OpenFlow versions 1.0, 1.2, and 1.3: .IP .B "ovs\-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13" . .SS "Flow Table Configuration" Make flow table 0 on bridge br0 refuse to accept more than 100 flows: .IP .B "ovs\-vsctl \-\- \-\-id=@ft create Flow_Table flow_limit=100 overflow_policy=refuse \-\- set Bridge br0 flow_tables=0=@ft" . .PP Make flow table 0 on bridge br0 evict flows, with fairness based on the matched ingress port, when there are more than 100: . .IP .B "ovs\-vsctl \-\- \-\-id=@ft create Flow_Table flow_limit=100 overflow_policy=evict groups='\(dqNXM_OF_IN_PORT[]\(dq' \-\- set Bridge br0 flow_tables:0=@ft" .SH "EXIT STATUS" .IP "0" Successful program execution. .IP "1" Usage, syntax, or configuration file error. .IP "2" The \fIbridge\fR argument to \fBbr\-exists\fR specified the name of a bridge that does not exist. .IP "65 (Linux and BSD) or 160 (Windows)" The database transaction was committed successfully, but \fBovs\-vsctl\fR failed while waiting for \fBovs\-vswitchd\fR to finish reconfiguring itself to reflect the changes. This typically happens when \fB--no\-wait\fR is not specified and the daemon does not acknowledge the update in time or encounters an error applying it. .br .sp Note that the change remains recorded in the database and is not rolled back. This exit code is intended to signal that while the database was updated, the system configuration may not yet reflect the intended changes. .SH "SEE ALSO" . .BR ovsdb\-server (1), .BR ovs\-vswitchd (8), .BR ovs\-vswitchd.conf.db (5). openvswitch-3.7.0~git20260211.8c6ebf8/utilities/ovs-vsctl.c000066400000000000000000003131511514270232600231030ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "fatal-signal.h" #include "hash.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/json.h" #include "openvswitch/ofp-parse.h" #include "openvswitch/poll-loop.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "process.h" #include "simap.h" #include "stream.h" #include "stream-ssl.h" #include "smap.h" #include "sset.h" #include "svec.h" #include "lib/vswitch-idl.h" #include "table.h" #include "timeval.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(vsctl); /* Post OVSDB reload error reported. */ #ifdef _WIN32 #include #define EXIT_POSTDB_ERROR ERROR_BAD_ARGUMENTS #else #include #define EXIT_POSTDB_ERROR EX_DATAERR #endif struct vsctl_context; /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --no-wait: Wait for ovs-vswitchd to reload its configuration? */ static bool wait_for_reload = true; /* --timeout: Time to wait for a connection to 'db'. */ static unsigned int timeout; /* --retry: If true, ovs-vsctl will retry connecting to the database forever. * If false and --db says to use an active connection method (e.g. "unix:", * "tcp:", "ssl:"), then ovs-vsctl will try to connect once and exit with an * error if the database server cannot be contacted (e.g. ovsdb-server is not * running). * * Regardless of this setting, --timeout always limits how long ovs-vsctl will * wait. */ static bool retry; /* --leader-only, --no-leader-only: Only accept the leader in a cluster. * * In a real Open vSwitch environment, it doesn't make much sense to cluster * the Open vSwitch database. This option exists to enable using ovs-vsctl to * test OVSDB's clustering feature. */ static int leader_only = true; /* --shuffle-remotes, --no-shuffle-remotes: Shuffle the order of remotes that * are specified in the connetion method string. * * In a real Open vSwitch environment, it doesn't make much sense to cluster * the Open vSwitch database. This option exists to enable using ovs-vsctl to * test OVSDB's clustering feature. */ static int shuffle_remotes = true; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; static void vsctl_cmd_init(void); /* The IDL we're using and the current transaction, if any. * This is for use by vsctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void vsctl_exit(int status); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static bool do_vsctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *, bool *postdb_err); /* post_db_reload_check frame work is to allow ovs-vsctl to do additional * checks after OVSDB transactions are successfully recorded and reload by * ovs-vswitchd. * * For example, When a new interface is added to OVSDB, ovs-vswitchd will * either store a positive values on successful implementing the new * interface, or -1 on failure. * * Unless --no-wait command line option is specified, * post_db_reload_do_checks() is called right after any configuration * changes is picked up (i.e. reload) by ovs-vswitchd. Any error detected * post OVSDB reload is reported as ovs-vsctl errors. OVS-vswitchd logs * more detailed messages about those errors. * * Current implementation only check for Post OVSDB reload failures on new * interface additions with 'add-br' and 'add-port' commands. * * post_db_reload_do_checks() returns 'true' if a failure is reported. * * post_db_reload_expect_iface() * * keep track of interfaces to be checked post OVSDB reload. */ static void post_db_reload_check_init(void); static bool post_db_reload_do_checks(const struct vsctl_context *); static void post_db_reload_expect_iface(const struct ovsrec_interface *); static struct uuid *neoteric_ifaces; static size_t n_neoteric_ifaces; static size_t allocated_neoteric_ifaces; int main(int argc, char *argv[]) { struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels_from_string_assert("reconnect:warn"); vsctl_cmd_init(); /* Parse command line. */ char *args = process_escape_args(argv); shash_init(&local_options); parse_options(argc, argv, &local_options); char *error = ctl_parse_commands(argc - optind, argv + optind, &local_options, &commands, &n_commands); if (error) { ctl_fatal("%s", error); } VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, "Called as %s", args); ctl_timeout_setup(timeout); /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create_unconnected(&ovsrec_idl_class, false); ovsdb_idl_set_shuffle_remotes(idl, shuffle_remotes); ovsdb_idl_set_remote(idl, db, retry); ovsdb_idl_set_leader_only(idl, leader_only); ovsdb_idl_set_db_change_aware(idl, false); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { bool postdb_err; seqno = ovsdb_idl_get_seqno(idl); if (do_vsctl(args, commands, n_commands, idl, &postdb_err)) { free(args); if (postdb_err) { exit(EXIT_POSTDB_ERROR); } exit(EXIT_SUCCESS); } } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_ONELINE, OPT_NO_SYSLOG, OPT_NO_WAIT, OPT_DRY_RUN, OPT_BOOTSTRAP_CA_CERT, OPT_PEER_CA_CERT, OPT_LOCAL, OPT_RETRY, OPT_COMMANDS, OPT_OPTIONS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS, SSL_OPTION_ENUMS, }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"no-wait", no_argument, NULL, OPT_NO_WAIT}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"retry", no_argument, NULL, OPT_RETRY}, {"help", no_argument, NULL, 'h'}, {"commands", no_argument, NULL, OPT_COMMANDS}, {"options", no_argument, NULL, OPT_OPTIONS}, {"leader-only", no_argument, &leader_only, true}, {"no-leader-only", no_argument, &leader_only, false}, {"shuffle-remotes", no_argument, &shuffle_remotes, true}, {"no-shuffle-remotes", no_argument, &shuffle_remotes, false}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, TABLE_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); for (;;) { int idx = INT_MAX; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); break; case OPT_NO_WAIT: wait_for_reload = false; break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), nullable_xstrdup(optarg)); break; case 'h': usage(); case OPT_COMMANDS: ctl_print_commands(); /* fall through */ case OPT_OPTIONS: ctl_print_options(global_long_options); /* fall through */ case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", ovsrec_get_db_version()); exit(EXIT_SUCCESS); case 't': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; case OPT_RETRY: retry = true; break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case '?': exit(EXIT_FAILURE); case 0: break; default: abort(); } } free(short_options); if (!db) { db = ctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } static void usage(void) { printf("\ %s: ovs-vswitchd management utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ Open vSwitch commands:\n\ init initialize database, if not yet initialized\n\ show print overview of database contents\n\ emer-reset reset configuration to clean state\n\ \n\ Bridge commands:\n\ add-br BRIDGE create a new bridge named BRIDGE\n\ add-br BRIDGE PARENT VLAN create new fake BRIDGE in PARENT on VLAN\n\ del-br BRIDGE delete BRIDGE and all of its ports\n\ list-br print the names of all the bridges\n\ br-exists BRIDGE exit 2 if BRIDGE does not exist\n\ br-to-vlan BRIDGE print the VLAN which BRIDGE is on\n\ br-to-parent BRIDGE print the parent of BRIDGE\n\ br-set-external-id BRIDGE KEY VALUE set KEY on BRIDGE to VALUE\n\ br-set-external-id BRIDGE KEY unset KEY on BRIDGE\n\ br-get-external-id BRIDGE KEY print value of KEY on BRIDGE\n\ br-get-external-id BRIDGE list key-value pairs on BRIDGE\n\ \n\ Port commands (a bond is considered to be a single port):\n\ list-ports BRIDGE print the names of all the ports on BRIDGE\n\ add-port BRIDGE PORT add network device PORT to BRIDGE\n\ add-bond BRIDGE PORT IFACE... add bonded port PORT in BRIDGE from IFACES\n\ del-port [BRIDGE] PORT delete PORT (which may be bonded) from BRIDGE\n\ port-to-br PORT print name of bridge that contains PORT\n\ \n\ Interface commands (a bond consists of multiple interfaces):\n\ list-ifaces BRIDGE print the names of all interfaces on BRIDGE\n\ iface-to-br IFACE print name of bridge that contains IFACE\n\ \n\ Controller commands:\n\ get-controller BRIDGE print the controllers for BRIDGE\n\ del-controller BRIDGE delete the controllers for BRIDGE\n\ [--inactivity-probe=MSECS]\n\ set-controller BRIDGE TARGET... set the controllers for BRIDGE\n\ get-fail-mode BRIDGE print the fail-mode for BRIDGE\n\ del-fail-mode BRIDGE delete the fail-mode for BRIDGE\n\ set-fail-mode BRIDGE MODE set the fail-mode for BRIDGE to MODE\n\ \n\ Manager commands:\n\ get-manager print the managers\n\ del-manager delete the managers\n\ [--inactivity-probe=MSECS]\n\ set-manager TARGET... set the list of managers to TARGET...\n\ \n\ SSL/TLS commands:\n\ get-ssl print the SSL/TLS configuration\n\ del-ssl delete the SSL/TLS configuration\n\ set-ssl PRIV-KEY CERT CA-CERT set the SSL/TLS configuration\n\ \n\ Auto Attach commands:\n\ add-aa-mapping BRIDGE I-SID VLAN add Auto Attach mapping to BRIDGE\n\ del-aa-mapping BRIDGE I-SID VLAN delete Auto Attach mapping VLAN from BRIDGE\n\ get-aa-mapping BRIDGE get Auto Attach mappings from BRIDGE\n\ \n\ Switch commands:\n\ emer-reset reset switch to known good state\n\ \n\ Connection Tracking commands:\n\ set-zone-limit DATAPATH ZONE|default LIMIT\n\ set CT LIMIT for ZONE|default on DATAPATH\n\ del-zone-limit DATAPATH ZONE|default\n\ delete CT limit for ZONE|default on DATAPATH\n\ list-zone-limits DATAPATH list all limits configured on DATAPATH\n\ \n\ %s\ %s\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ --no-wait do not wait for ovs-vswitchd to reconfigure\n\ --retry keep trying to connect to server forever\n\ -t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, ctl_get_db_cmd_usage(), ctl_list_db_tables_usage(), ctl_default_db()); table_usage(); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=vsctl:syslog:warn\n"); stream_usage("database", true, true, true); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); exit(EXIT_SUCCESS); } /* ovs-vsctl specific context. Inherits the 'struct ctl_context' as base. */ struct vsctl_context { struct ctl_context base; /* Modifiable state. */ const struct ovsrec_open_vswitch *ovs; bool verified_ports; /* A cache of the contents of the database. * * A command that needs to use any of this information must first call * vsctl_context_populate_cache(). A command that changes anything that * could invalidate the cache must either call * vsctl_context_invalidate_cache() or manually update the cache to * maintain its correctness. */ bool cache_valid; struct shash bridges; /* Maps from bridge name to struct vsctl_bridge. */ struct shash ports; /* Maps from port name to struct vsctl_port. */ struct shash ifaces; /* Maps from port name to struct vsctl_iface. */ }; struct vsctl_bridge { struct ovsrec_bridge *br_cfg; char *name; struct ovs_list ports; /* Contains "struct vsctl_port"s. */ /* VLAN ("fake") bridge support. * * Use 'parent != NULL' to detect a fake bridge, because 'vlan' can be 0 * in either case. */ struct hmap children; /* VLAN bridges indexed by 'vlan'. */ struct hmap_node children_node; /* Node in parent's 'children' hmap. */ struct vsctl_bridge *parent; /* Real bridge, or NULL. */ int vlan; /* VLAN VID (0...4095), or 0. */ }; struct vsctl_port { struct ovs_list ports_node; /* In struct vsctl_bridge's 'ports' list. */ struct ovs_list ifaces; /* Contains "struct vsctl_iface"s. */ struct ovsrec_port *port_cfg; struct vsctl_bridge *bridge; }; struct vsctl_iface { struct ovs_list ifaces_node; /* In struct vsctl_port's 'ifaces' list. */ struct ovsrec_interface *iface_cfg; struct vsctl_port *port; }; /* Casts 'base' into 'struct vsctl_context'. */ static struct vsctl_context * vsctl_context_cast(struct ctl_context *base) { return CONTAINER_OF(base, struct vsctl_context, base); } static struct vsctl_bridge *find_vlan_bridge(struct vsctl_bridge *parent, int vlan); static char * vsctl_context_to_string(const struct ctl_context *ctx) { const struct shash_node *node; struct svec words; char *s; int i; svec_init(&words); SHASH_FOR_EACH (node, &ctx->options) { svec_add(&words, node->name); } for (i = 0; i < ctx->argc; i++) { svec_add(&words, ctx->argv[i]); } svec_terminate(&words); s = process_escape_args(words.names); svec_destroy(&words); return s; } static void verify_ports(struct vsctl_context *vsctl_ctx) { if (!vsctl_ctx->verified_ports) { const struct ovsrec_bridge *bridge; const struct ovsrec_port *port; ovsrec_open_vswitch_verify_bridges(vsctl_ctx->ovs); OVSREC_BRIDGE_FOR_EACH (bridge, vsctl_ctx->base.idl) { ovsrec_bridge_verify_ports(bridge); } OVSREC_PORT_FOR_EACH (port, vsctl_ctx->base.idl) { ovsrec_port_verify_interfaces(port); } vsctl_ctx->verified_ports = true; } } static struct vsctl_bridge * add_bridge_to_cache(struct vsctl_context *vsctl_ctx, struct ovsrec_bridge *br_cfg, const char *name, struct vsctl_bridge *parent, int vlan) { struct vsctl_bridge *br = xzalloc(sizeof *br); br->br_cfg = br_cfg; br->name = xstrdup(name); ovs_list_init(&br->ports); br->parent = parent; br->vlan = vlan; hmap_init(&br->children); if (parent) { struct vsctl_bridge *conflict = find_vlan_bridge(parent, vlan); if (conflict) { VLOG_WARN("%s: bridge has multiple VLAN bridges (%s and %s) " "for VLAN %d, but only one is allowed", parent->name, name, conflict->name, vlan); } else { hmap_insert(&parent->children, &br->children_node, hash_int(vlan, 0)); } } shash_add(&vsctl_ctx->bridges, br->name, br); return br; } static void ovs_delete_bridge(const struct ovsrec_open_vswitch *ovs, struct ovsrec_bridge *bridge) { struct ovsrec_bridge **bridges; size_t i, n; bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); for (i = n = 0; i < ovs->n_bridges; i++) { if (ovs->bridges[i] != bridge) { bridges[n++] = ovs->bridges[i]; } } ovsrec_open_vswitch_set_bridges(ovs, bridges, n); free(bridges); } static void del_cached_bridge(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *br) { ovs_assert(ovs_list_is_empty(&br->ports)); ovs_assert(hmap_is_empty(&br->children)); if (br->parent) { hmap_remove(&br->parent->children, &br->children_node); } if (br->br_cfg) { ovsrec_bridge_delete(br->br_cfg); ovs_delete_bridge(vsctl_ctx->ovs, br->br_cfg); } shash_find_and_delete(&vsctl_ctx->bridges, br->name); hmap_destroy(&br->children); free(br->name); free(br); } static bool port_is_fake_bridge(const struct ovsrec_port *port_cfg) { return (port_cfg->fake_bridge && port_cfg->tag && *port_cfg->tag >= 0 && *port_cfg->tag <= 4095); } static struct vsctl_bridge * find_vlan_bridge(struct vsctl_bridge *parent, int vlan) { struct vsctl_bridge *child; HMAP_FOR_EACH_IN_BUCKET (child, children_node, hash_int(vlan, 0), &parent->children) { if (child->vlan == vlan) { return child; } } return NULL; } static struct vsctl_port * add_port_to_cache(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *parent, struct ovsrec_port *port_cfg) { struct vsctl_port *port = xzalloc(sizeof *port); if (port_cfg->tag && *port_cfg->tag >= 0 && *port_cfg->tag <= 4095) { struct vsctl_bridge *vlan_bridge; vlan_bridge = find_vlan_bridge(parent, *port_cfg->tag); if (vlan_bridge) { parent = vlan_bridge; } } ovs_list_push_back(&parent->ports, &port->ports_node); ovs_list_init(&port->ifaces); port->port_cfg = port_cfg; port->bridge = parent; shash_add(&vsctl_ctx->ports, port_cfg->name, port); return port; } static void del_cached_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port) { ovs_assert(ovs_list_is_empty(&port->ifaces)); ovs_list_remove(&port->ports_node); shash_find_and_delete(&vsctl_ctx->ports, port->port_cfg->name); ovsrec_port_delete(port->port_cfg); free(port); } static struct vsctl_iface * add_iface_to_cache(struct vsctl_context *vsctl_ctx, struct vsctl_port *parent, struct ovsrec_interface *iface_cfg) { struct vsctl_iface *iface; iface = xmalloc(sizeof *iface); ovs_list_push_back(&parent->ifaces, &iface->ifaces_node); iface->iface_cfg = iface_cfg; iface->port = parent; shash_add(&vsctl_ctx->ifaces, iface_cfg->name, iface); return iface; } static void del_cached_iface(struct vsctl_context *vsctl_ctx, struct vsctl_iface *iface) { ovs_list_remove(&iface->ifaces_node); shash_find_and_delete(&vsctl_ctx->ifaces, iface->iface_cfg->name); ovsrec_interface_delete(iface->iface_cfg); free(iface); } static void vsctl_context_invalidate_cache(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct shash_node *node; if (!vsctl_ctx->cache_valid) { return; } vsctl_ctx->cache_valid = false; SHASH_FOR_EACH (node, &vsctl_ctx->bridges) { struct vsctl_bridge *bridge = node->data; hmap_destroy(&bridge->children); free(bridge->name); free(bridge); } shash_destroy(&vsctl_ctx->bridges); shash_destroy_free_data(&vsctl_ctx->ports); shash_destroy_free_data(&vsctl_ctx->ifaces); } static void pre_get_info(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_bridges); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_fake_bridge); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_tag); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ofport); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_error); } static void vsctl_context_populate_cache(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; struct sset bridges, ports; size_t i; if (vsctl_ctx->cache_valid) { /* Cache is already populated. */ return; } vsctl_ctx->cache_valid = true; shash_init(&vsctl_ctx->bridges); shash_init(&vsctl_ctx->ports); shash_init(&vsctl_ctx->ifaces); sset_init(&bridges); sset_init(&ports); for (i = 0; i < ovs->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs->bridges[i]; struct vsctl_bridge *br; size_t j; if (!sset_add(&bridges, br_cfg->name)) { VLOG_WARN("%s: database contains duplicate bridge name", br_cfg->name); continue; } br = add_bridge_to_cache(vsctl_ctx, br_cfg, br_cfg->name, NULL, 0); for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; if (!sset_add(&ports, port_cfg->name)) { /* Duplicate port name. (We will warn about that later.) */ continue; } if (port_is_fake_bridge(port_cfg) && sset_add(&bridges, port_cfg->name)) { add_bridge_to_cache(vsctl_ctx, NULL, port_cfg->name, br, *port_cfg->tag); } } } sset_destroy(&bridges); sset_destroy(&ports); sset_init(&bridges); for (i = 0; i < ovs->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs->bridges[i]; struct vsctl_bridge *br; size_t j; if (!sset_add(&bridges, br_cfg->name)) { continue; } br = shash_find_data(&vsctl_ctx->bridges, br_cfg->name); ovs_assert(br); for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; struct vsctl_port *port; size_t k; port = shash_find_data(&vsctl_ctx->ports, port_cfg->name); if (port) { if (port_cfg == port->port_cfg) { VLOG_WARN("%s: port is in multiple bridges (%s and %s)", port_cfg->name, br->name, port->bridge->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server shouldn't * have allowed it. */ VLOG_ERR("%s: database contains duplicate port name", port_cfg->name); } continue; } if (port_is_fake_bridge(port_cfg) && !sset_add(&bridges, port_cfg->name)) { continue; } port = add_port_to_cache(vsctl_ctx, br, port_cfg); for (k = 0; k < port_cfg->n_interfaces; k++) { struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; struct vsctl_iface *iface; iface = shash_find_data(&vsctl_ctx->ifaces, iface_cfg->name); if (iface) { if (iface_cfg == iface->iface_cfg) { VLOG_WARN("%s: interface is in multiple ports " "(%s and %s)", iface_cfg->name, iface->port->port_cfg->name, port->port_cfg->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server * shouldn't have allowed it. */ VLOG_ERR("%s: database contains duplicate interface " "name", iface_cfg->name); } continue; } add_iface_to_cache(vsctl_ctx, port, iface_cfg); } } } sset_destroy(&bridges); } static void check_conflicts(struct vsctl_context *vsctl_ctx, const char *name, char *msg) { struct vsctl_iface *iface; struct vsctl_port *port; verify_ports(vsctl_ctx); if (shash_find(&vsctl_ctx->bridges, name)) { ctl_fatal("%s because a bridge named %s already exists", msg, name); } port = shash_find_data(&vsctl_ctx->ports, name); if (port) { if (port->bridge) { ctl_fatal("%s because a port named %s already exists on " "bridge %s", msg, name, port->bridge->name); } else { ctl_fatal("%s because a port named %s already exists", msg, name); } } iface = shash_find_data(&vsctl_ctx->ifaces, name); if (iface) { if (iface->port->bridge) { ctl_fatal("%s because an interface named %s already exists " "on bridge %s", msg, name, iface->port->bridge->name); } else { ctl_fatal("%s because an interface named %s already exists", msg, name); } } free(msg); } static struct vsctl_bridge * find_bridge(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_bridge *br; ovs_assert(vsctl_ctx->cache_valid); br = shash_find_data(&vsctl_ctx->bridges, name); if (must_exist && !br) { ctl_fatal("no bridge named %s", name); } ovsrec_open_vswitch_verify_bridges(vsctl_ctx->ovs); return br; } static struct vsctl_bridge * find_real_bridge(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_bridge *br = find_bridge(vsctl_ctx, name, must_exist); if (br && br->parent) { ctl_fatal("%s is a fake bridge", name); } return br; } static struct vsctl_port * find_port(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_port *port; ovs_assert(vsctl_ctx->cache_valid); port = shash_find_data(&vsctl_ctx->ports, name); if (port && port->bridge && !strcmp(name, port->bridge->name)) { port = NULL; } if (must_exist && !port) { ctl_fatal("no port named %s", name); } verify_ports(vsctl_ctx); return port; } static struct vsctl_iface * find_iface(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_iface *iface; ovs_assert(vsctl_ctx->cache_valid); iface = shash_find_data(&vsctl_ctx->ifaces, name); if (iface && iface->port->bridge && !strcmp(name, iface->port->bridge->name)) { iface = NULL; } if (must_exist && !iface) { ctl_fatal("no interface named %s", name); } verify_ports(vsctl_ctx); return iface; } static void bridge_insert_port(struct ovsrec_bridge *br, struct ovsrec_port *port) { struct ovsrec_port **ports; size_t i; ports = xmalloc(sizeof *br->ports * (br->n_ports + 1)); for (i = 0; i < br->n_ports; i++) { ports[i] = br->ports[i]; } ports[br->n_ports] = port; ovsrec_bridge_set_ports(br, ports, br->n_ports + 1); free(ports); } static void bridge_delete_port(struct ovsrec_bridge *br, struct ovsrec_port *port) { struct ovsrec_port **ports; size_t i, n; ports = xmalloc(sizeof *br->ports * br->n_ports); for (i = n = 0; i < br->n_ports; i++) { if (br->ports[i] != port) { ports[n++] = br->ports[i]; } } ovsrec_bridge_set_ports(br, ports, n); free(ports); } static void ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs, struct ovsrec_bridge *bridge) { struct ovsrec_bridge **bridges; size_t i; bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1)); for (i = 0; i < ovs->n_bridges; i++) { bridges[i] = ovs->bridges[i]; } bridges[ovs->n_bridges] = bridge; ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1); free(bridges); } static void cmd_init(struct ctl_context *ctx OVS_UNUSED) { } static struct cmd_show_table cmd_show_tables[] = { {&ovsrec_table_open_vswitch, NULL, {&ovsrec_open_vswitch_col_manager_options, &ovsrec_open_vswitch_col_bridges, &ovsrec_open_vswitch_col_ovs_version}, {NULL, NULL, NULL} }, {&ovsrec_table_bridge, &ovsrec_bridge_col_name, {&ovsrec_bridge_col_controller, &ovsrec_bridge_col_fail_mode, &ovsrec_bridge_col_datapath_type, &ovsrec_bridge_col_ports}, {NULL, NULL, NULL} }, {&ovsrec_table_port, &ovsrec_port_col_name, {&ovsrec_port_col_tag, &ovsrec_port_col_trunks, &ovsrec_port_col_interfaces}, {NULL, NULL, NULL} }, {&ovsrec_table_interface, &ovsrec_interface_col_name, {&ovsrec_interface_col_type, &ovsrec_interface_col_options, &ovsrec_interface_col_error, &ovsrec_interface_col_bfd_status}, {NULL, NULL, NULL} }, {&ovsrec_table_controller, &ovsrec_controller_col_target, {&ovsrec_controller_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {&ovsrec_table_manager, &ovsrec_manager_col_target, {&ovsrec_manager_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}} }; static void pre_cmd_emer_reset(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_manager_options); ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_mirrors); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_netflow); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_sflow); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ipfix); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_flood_vlans); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_other_config); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_other_config); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ingress_policing_rate); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ingress_policing_burst); } static void cmd_emer_reset(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsdb_idl *idl = ctx->idl; const struct ovsrec_bridge *br; const struct ovsrec_port *port; const struct ovsrec_interface *iface; const struct ovsrec_mirror *mirror; const struct ovsrec_controller *ctrl; const struct ovsrec_manager *mgr; const struct ovsrec_netflow *nf; const struct ovsrec_ssl *ssl; const struct ovsrec_sflow *sflow; const struct ovsrec_ipfix *ipfix; const struct ovsrec_flow_sample_collector_set *fscset; /* Reset the Open_vSwitch table. */ ovsrec_open_vswitch_set_manager_options(vsctl_ctx->ovs, NULL, 0); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, NULL); OVSREC_BRIDGE_FOR_EACH (br, idl) { const char *hwaddr; ovsrec_bridge_set_controller(br, NULL, 0); ovsrec_bridge_set_fail_mode(br, NULL); ovsrec_bridge_set_mirrors(br, NULL, 0); ovsrec_bridge_set_netflow(br, NULL); ovsrec_bridge_set_sflow(br, NULL); ovsrec_bridge_set_ipfix(br, NULL); ovsrec_bridge_set_flood_vlans(br, NULL, 0); /* We only want to save the "hwaddr" key from other_config. */ hwaddr = smap_get(&br->other_config, "hwaddr"); if (hwaddr) { const struct smap smap = SMAP_CONST1(&smap, "hwaddr", hwaddr); ovsrec_bridge_set_other_config(br, &smap); } else { ovsrec_bridge_set_other_config(br, NULL); } } OVSREC_PORT_FOR_EACH (port, idl) { ovsrec_port_set_other_config(port, NULL); } OVSREC_INTERFACE_FOR_EACH (iface, idl) { /* xxx What do we do about gre/patch devices created by mgr? */ ovsrec_interface_set_ingress_policing_rate(iface, 0); ovsrec_interface_set_ingress_policing_burst(iface, 0); } OVSREC_MIRROR_FOR_EACH_SAFE (mirror, idl) { ovsrec_mirror_delete(mirror); } OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, idl) { ovsrec_controller_delete(ctrl); } OVSREC_MANAGER_FOR_EACH_SAFE (mgr, idl) { ovsrec_manager_delete(mgr); } OVSREC_NETFLOW_FOR_EACH_SAFE (nf, idl) { ovsrec_netflow_delete(nf); } OVSREC_SSL_FOR_EACH_SAFE (ssl, idl) { ovsrec_ssl_delete(ssl); } OVSREC_SFLOW_FOR_EACH_SAFE (sflow, idl) { ovsrec_sflow_delete(sflow); } OVSREC_IPFIX_FOR_EACH_SAFE (ipfix, idl) { ovsrec_ipfix_delete(ipfix); } OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, idl) { ovsrec_flow_sample_collector_set_delete(fscset); } vsctl_context_invalidate_cache(ctx); } static struct ovsrec_datapath * find_datapath(struct vsctl_context *vsctl_ctx, const char *dp_name) { const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; for (int i = 0; i < ovs->n_datapaths; i++) { if (!strcmp(ovs->key_datapaths[i], dp_name)) { return ovs->value_datapaths[i]; } } return NULL; } static struct ovsrec_ct_zone * find_ct_zone(struct ovsrec_datapath *dp, const int64_t zone_id) { for (int i = 0; i < dp->n_ct_zones; i++) { if (dp->key_ct_zones[i] == zone_id) { return dp->value_ct_zones[i]; } } return NULL; } static struct ovsrec_ct_timeout_policy * create_timeout_policy(struct ctl_context *ctx, char **tps, int n_tps) { const struct ovsrec_ct_timeout_policy_table *tp_table; const struct ovsrec_ct_timeout_policy *row; struct ovsrec_ct_timeout_policy *tp = NULL; struct simap new_tp = SIMAP_INITIALIZER(&new_tp); char **policies = xzalloc(sizeof *policies * n_tps); const char **key_timeouts = xmalloc(sizeof *key_timeouts * n_tps); int64_t *value_timeouts = xmalloc(sizeof *value_timeouts * n_tps); /* Parse timeout arguments. */ for (int i = 0; i < n_tps; i++) { policies[i] = xstrdup(tps[i]); char *key, *value; char *policy = policies[i]; if (!ofputil_parse_key_value(&policy, &key, &value)) { goto done; } key_timeouts[i] = key; value_timeouts[i] = atoi(value); simap_put(&new_tp, key, (unsigned int)value_timeouts[i]); } done: tp_table = ovsrec_ct_timeout_policy_table_get(ctx->idl); OVSREC_CT_TIMEOUT_POLICY_TABLE_FOR_EACH (row, tp_table) { struct simap s = SIMAP_INITIALIZER(&s); /* Convert to simap. */ for (int i = 0; i < row->n_timeouts; i++) { simap_put(&s, row->key_timeouts[i], row->value_timeouts[i]); } if (simap_equal(&s, &new_tp)) { tp = CONST_CAST(struct ovsrec_ct_timeout_policy *, row); simap_destroy(&s); break; } simap_destroy(&s); } if (!tp) { tp = ovsrec_ct_timeout_policy_insert(ctx->txn); ovsrec_ct_timeout_policy_set_timeouts(tp, key_timeouts, (const int64_t *)value_timeouts, n_tps); } for (int i = 0; i < n_tps; i++) { free(policies[i]); } free(policies); simap_destroy(&new_tp); free(key_timeouts); free(value_timeouts); return tp; } static void cmd_add_zone_tp(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_ct_timeout_policy *tp; int64_t zone_id; const char *dp_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; if (!ovs_scan(ctx->argv[2], "zone=%"SCNi64, &zone_id)) { ctl_fatal("invalid zone argument, %s", ctx->argv[2]); } struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, dp_name); if (!dp) { ctl_fatal("datapath %s does not exist", dp_name); } int n_tps = ctx->argc - 3; struct ovsrec_ct_zone *zone = find_ct_zone(dp, zone_id); if (n_tps <= 0) { ctl_fatal("No timeout policy"); } if (zone && zone->timeout_policy && !may_exist) { ctl_fatal("zone id %"PRIu64" already has a policy", zone_id); } tp = create_timeout_policy(ctx, &ctx->argv[3], n_tps); if (zone) { ovsrec_ct_zone_set_timeout_policy(zone, tp); } else { zone = ovsrec_ct_zone_insert(ctx->txn); ovsrec_ct_zone_set_timeout_policy(zone, tp); ovsrec_datapath_update_ct_zones_setkey(dp, zone_id, zone); } } static void cmd_del_zone_tp(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); int64_t zone_id; bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *dp_name = ctx->argv[1]; if (!ovs_scan(ctx->argv[2], "zone=%"SCNi64, &zone_id)) { ctl_fatal("invalid zone argument, %s", ctx->argv[2]); } struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, dp_name); if (!dp) { ctl_fatal("datapath %s does not exist", dp_name); } struct ovsrec_ct_zone *zone = find_ct_zone(dp, zone_id); if (must_exist && !(zone && zone->timeout_policy)) { ctl_fatal("zone id %"PRIu64" does not have a policy", zone_id); } if (!zone) { return; } if (zone->limit) { if (zone->timeout_policy) { ovsrec_ct_timeout_policy_delete(zone->timeout_policy); } ovsrec_ct_zone_set_timeout_policy(zone, NULL); } else { ovsrec_datapath_update_ct_zones_delkey(dp, zone_id); } } static void cmd_list_zone_tp(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, ctx->argv[1]); if (!dp) { ctl_fatal("datapath: %s record not found", ctx->argv[1]); } for (int i = 0; i < dp->n_ct_zones; i++) { struct ovsrec_ct_zone *zone = dp->value_ct_zones[i]; ds_put_format(&ctx->output, "Zone:%"PRIu64", Timeout Policies: ", dp->key_ct_zones[i]); struct ovsrec_ct_timeout_policy *tp = zone->timeout_policy; if (tp) { for (int j = 0; j < tp->n_timeouts; j++) { ds_put_format(&ctx->output, "%s=%"PRIu64" ", tp->key_timeouts[j], tp->value_timeouts[j]); } } else { ds_put_cstr(&ctx->output, "system default"); } ds_chomp(&ctx->output, ' '); ds_put_char(&ctx->output, '\n'); } } static void cmd_set_zone_limit(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); int64_t zone_id = -1; int64_t limit = -1; const char *dp_name = ctx->argv[1]; if (!ovs_scan(ctx->argv[2], "%"SCNi64, &zone_id) && strcmp(ctx->argv[2], "default")) { ctl_fatal("invalid zone id, %s", ctx->argv[2]); } if (!ovs_scan(ctx->argv[3], "%"SCNi64, &limit)) { ctl_fatal("invalid limit, %s", ctx->argv[3]); } struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, dp_name); if (!dp) { ctl_fatal("datapath %s does not exist", dp_name); } if (limit < 0 || limit > UINT32_MAX) { ctl_fatal("limit (%"PRIi64") out of range", limit); } if (!strcmp(ctx->argv[2], "default")) { ovsrec_datapath_set_ct_zone_default_limit(dp, &limit, 1); return; } if (zone_id < 0 || zone_id > UINT16_MAX) { ctl_fatal("zone_id (%"PRIi64") out of range", zone_id); } struct ovsrec_ct_zone *zone = find_ct_zone(dp, zone_id); if (!zone) { zone = ovsrec_ct_zone_insert(ctx->txn); ovsrec_datapath_update_ct_zones_setkey(dp, zone_id, zone); } ovsrec_ct_zone_set_limit(zone, &limit, 1); } static void cmd_del_zone_limit(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); int64_t zone_id; bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *dp_name = ctx->argv[1]; if (!ovs_scan(ctx->argv[2], "%"SCNi64, &zone_id) && strcmp(ctx->argv[2], "default")) { ctl_fatal("invalid zone id, %s", ctx->argv[2]); } struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, dp_name); if (!dp) { ctl_fatal("datapath %s does not exist", dp_name); } if (!strcmp(ctx->argv[2], "default")) { if (must_exist && !dp->ct_zone_default_limit) { ctl_fatal("datapath %s does not have a limit", dp_name); } ovsrec_datapath_set_ct_zone_default_limit(dp, NULL, 0); return; } struct ovsrec_ct_zone *zone = find_ct_zone(dp, zone_id); if (must_exist && !(zone && zone->limit)) { ctl_fatal("zone_id %"PRIi64" does not have a limit", zone_id); } if (!zone) { return; } if (zone->timeout_policy) { ovsrec_ct_zone_set_limit(zone, NULL, 0); } else { ovsrec_datapath_update_ct_zones_delkey(dp, zone_id); } } static void cmd_list_zone_limits(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, ctx->argv[1]); if (!dp) { ctl_fatal("datapath: %s record not found", ctx->argv[1]); } if (dp->ct_zone_default_limit) { ds_put_format(&ctx->output, "Default, Limit: %"PRIu64"\n", *dp->ct_zone_default_limit); } for (int i = 0; i < dp->n_ct_zones; i++) { struct ovsrec_ct_zone *zone = dp->value_ct_zones[i]; if (zone->limit) { ds_put_format(&ctx->output, "Zone: %"PRIu64", Limit: %"PRIu64"\n", dp->key_ct_zones[i], *zone->limit); } } } static void pre_get_zone(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_datapaths); ovsdb_idl_add_column(ctx->idl, &ovsrec_datapath_col_ct_zones); ovsdb_idl_add_column(ctx->idl, &ovsrec_datapath_col_ct_zone_default_limit); ovsdb_idl_add_column(ctx->idl, &ovsrec_ct_zone_col_timeout_policy); ovsdb_idl_add_column(ctx->idl, &ovsrec_ct_zone_col_limit); ovsdb_idl_add_column(ctx->idl, &ovsrec_ct_timeout_policy_col_timeouts); } static void pre_get_dp_cap(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_datapaths); ovsdb_idl_add_column(ctx->idl, &ovsrec_datapath_col_capabilities); } static void cmd_list_dp_cap(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct smap_node *node; struct ovsrec_datapath *dp = find_datapath(vsctl_ctx, ctx->argv[1]); if (!dp) { ctl_fatal("datapath \"%s\" record not found", ctx->argv[1]); } SMAP_FOR_EACH (node, &dp->capabilities) { ds_put_format(&ctx->output, "%s=%s ",node->key, node->value); } ds_chomp(&ctx->output, ' '); ds_put_char(&ctx->output, '\n'); } static void cmd_add_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; const char *br_name, *parent_name; struct ovsrec_interface *iface; int vlan; br_name = ctx->argv[1]; if (!br_name[0]) { ctl_fatal("bridge name must not be empty string"); } if (ctx->argc == 2) { parent_name = NULL; vlan = 0; } else if (ctx->argc == 4) { parent_name = ctx->argv[2]; vlan = atoi(ctx->argv[3]); if (vlan < 0 || vlan > 4095) { ctl_fatal("%s: vlan must be between 0 and 4095", ctx->argv[0]); } } else { ctl_fatal("'%s' command takes exactly 1 or 3 arguments", ctx->argv[0]); } vsctl_context_populate_cache(ctx); if (may_exist) { struct vsctl_bridge *br; br = find_bridge(vsctl_ctx, br_name, false); if (br) { if (!parent_name) { if (br->parent) { ctl_fatal("\"--may-exist add-br %s\" but %s is " "a VLAN bridge for VLAN %d", br_name, br_name, br->vlan); } } else { if (!br->parent) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "is not a VLAN bridge", br_name, parent_name, vlan, br_name); } else if (strcmp(br->parent->name, parent_name)) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "has the wrong parent %s", br_name, parent_name, vlan, br_name, br->parent->name); } else if (br->vlan != vlan) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "is a VLAN bridge for the wrong VLAN %d", br_name, parent_name, vlan, br_name, br->vlan); } } return; } } check_conflicts(vsctl_ctx, br_name, xasprintf("cannot create a bridge named %s", br_name)); if (!parent_name) { struct ovsrec_port *port; struct ovsrec_bridge *br; iface = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(iface, br_name); ovsrec_interface_set_type(iface, "internal"); port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, br_name); ovsrec_port_set_interfaces(port, &iface, 1); br = ovsrec_bridge_insert(ctx->txn); ovsrec_bridge_set_name(br, br_name); ovsrec_bridge_set_ports(br, &port, 1); ovs_insert_bridge(vsctl_ctx->ovs, br); } else { struct vsctl_bridge *conflict; struct vsctl_bridge *parent; struct ovsrec_port *port; struct ovsrec_bridge *br; int64_t tag = vlan; parent = find_bridge(vsctl_ctx, parent_name, false); if (parent && parent->parent) { ctl_fatal("cannot create bridge with fake bridge as parent"); } if (!parent) { ctl_fatal("parent bridge %s does not exist", parent_name); } conflict = find_vlan_bridge(parent, vlan); if (conflict) { ctl_fatal("bridge %s already has a child VLAN bridge %s " "on VLAN %d", parent_name, conflict->name, vlan); } br = parent->br_cfg; iface = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(iface, br_name); ovsrec_interface_set_type(iface, "internal"); port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, br_name); ovsrec_port_set_interfaces(port, &iface, 1); ovsrec_port_set_fake_bridge(port, true); ovsrec_port_set_tag(port, &tag, 1); bridge_insert_port(br, port); } post_db_reload_expect_iface(iface); vsctl_context_invalidate_cache(ctx); } static void del_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port) { struct vsctl_iface *iface; bridge_delete_port((port->bridge->parent ? port->bridge->parent->br_cfg : port->bridge->br_cfg), port->port_cfg); LIST_FOR_EACH_SAFE (iface, ifaces_node, &port->ifaces) { del_cached_iface(vsctl_ctx, iface); } del_cached_port(vsctl_ctx, port); } static void del_bridge(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *br) { struct vsctl_bridge *child; struct vsctl_port *port; const struct ovsrec_flow_sample_collector_set *fscset; HMAP_FOR_EACH_SAFE (child, children_node, &br->children) { del_bridge(vsctl_ctx, child); } LIST_FOR_EACH_SAFE (port, ports_node, &br->ports) { del_port(vsctl_ctx, port); } OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, vsctl_ctx->base.idl) { if (fscset->bridge == br->br_cfg) { ovsrec_flow_sample_collector_set_delete(fscset); } } del_cached_bridge(vsctl_ctx, br); } static void cmd_del_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], must_exist); if (bridge) { del_bridge(vsctl_ctx, bridge); } } static void output_sorted(struct svec *svec, struct ds *output) { const char *name; size_t i; svec_sort(svec); SVEC_FOR_EACH (i, name, svec) { ds_put_format(output, "%s\n", name); } } static void cmd_list_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct shash_node *node; struct svec bridges; bool real = shash_find(&ctx->options, "--real"); bool fake = shash_find(&ctx->options, "--fake"); /* If neither fake nor real were requested, return both. */ if (!real && !fake) { real = fake = true; } vsctl_context_populate_cache(ctx); svec_init(&bridges); SHASH_FOR_EACH (node, &vsctl_ctx->bridges) { struct vsctl_bridge *br = node->data; if (br->parent ? fake : real) { svec_add(&bridges, br->name); } } output_sorted(&bridges, &ctx->output); svec_destroy(&bridges); } static void cmd_br_exists(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); vsctl_context_populate_cache(ctx); if (!find_bridge(vsctl_ctx, ctx->argv[1], false)) { vsctl_exit(2); } } static void set_external_id(struct smap *old, struct smap *new, char *key, char *value) { smap_clone(new, old); if (value) { smap_replace(new, key, value); } else { smap_remove(new, key); } } static void pre_cmd_br_set_external_id(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_external_ids); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_external_ids); } static void cmd_br_set_external_id(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; struct smap new; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->br_cfg) { set_external_id(&bridge->br_cfg->external_ids, &new, ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL); ovsrec_bridge_verify_external_ids(bridge->br_cfg); ovsrec_bridge_set_external_ids(bridge->br_cfg, &new); } else { char *key = xasprintf("fake-bridge-%s", ctx->argv[2]); struct vsctl_port *port = shash_find_data(&vsctl_ctx->ports, ctx->argv[1]); set_external_id(&port->port_cfg->external_ids, &new, key, ctx->argc >= 4 ? ctx->argv[3] : NULL); ovsrec_port_verify_external_ids(port->port_cfg); ovsrec_port_set_external_ids(port->port_cfg, &new); free(key); } smap_destroy(&new); } static void get_external_id(struct smap *smap, const char *prefix, const char *key, struct ds *output) { if (key) { char *prefix_key = xasprintf("%s%s", prefix, key); const char *value = smap_get(smap, prefix_key); if (value) { ds_put_format(output, "%s\n", value); } free(prefix_key); } else { const struct smap_node **sorted = smap_sort(smap); size_t prefix_len = strlen(prefix); size_t i; for (i = 0; i < smap_count(smap); i++) { const struct smap_node *node = sorted[i]; if (!strncmp(node->key, prefix, prefix_len)) { ds_put_format(output, "%s=%s\n", node->key + prefix_len, node->value); } } free(sorted); } } static void pre_cmd_br_get_external_id(struct ctl_context *ctx) { pre_cmd_br_set_external_id(ctx); } static void cmd_br_get_external_id(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->br_cfg) { ovsrec_bridge_verify_external_ids(bridge->br_cfg); get_external_id(&bridge->br_cfg->external_ids, "", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output); } else { struct vsctl_port *port = shash_find_data(&vsctl_ctx->ports, ctx->argv[1]); ovsrec_port_verify_external_ids(port->port_cfg); get_external_id(&port->port_cfg->external_ids, "fake-bridge-", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output); } } static void cmd_list_ports(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct vsctl_port *port; struct svec ports; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); ovsrec_bridge_verify_ports(br->br_cfg ? br->br_cfg : br->parent->br_cfg); svec_init(&ports); LIST_FOR_EACH (port, ports_node, &br->ports) { if (strcmp(port->port_cfg->name, br->name)) { svec_add(&ports, port->port_cfg->name); } } output_sorted(&ports, &ctx->output); svec_destroy(&ports); } static void add_port(struct ctl_context *ctx, const char *br_name, const char *port_name, bool may_exist, bool fake_iface, char *iface_names[], int n_ifaces, char *settings[], int n_settings) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; struct ovsrec_interface **ifaces; struct ovsrec_port *port; size_t i; if (!port_name[0]) { ctl_fatal("port name must not be empty string"); } for (i = 0; i < n_ifaces; i++) { if (!iface_names[i][0]) { ctl_fatal("interface name must not be empty string"); } } vsctl_context_populate_cache(ctx); if (may_exist) { struct vsctl_port *vsctl_port; vsctl_port = find_port(vsctl_ctx, port_name, false); if (vsctl_port) { struct svec want_names, have_names; svec_init(&want_names); for (i = 0; i < n_ifaces; i++) { svec_add(&want_names, iface_names[i]); } svec_sort(&want_names); svec_init(&have_names); for (i = 0; i < vsctl_port->port_cfg->n_interfaces; i++) { svec_add(&have_names, vsctl_port->port_cfg->interfaces[i]->name); } svec_sort(&have_names); if (strcmp(vsctl_port->bridge->name, br_name)) { char *command = vsctl_context_to_string(ctx); ctl_fatal("\"%s\" but %s is actually attached to bridge %s", command, port_name, vsctl_port->bridge->name); } if (!svec_equal(&want_names, &have_names)) { char *have_names_string = svec_join(&have_names, ", ", ""); char *command = vsctl_context_to_string(ctx); ctl_fatal("\"%s\" but %s actually has interface(s) %s", command, port_name, have_names_string); } svec_destroy(&want_names); svec_destroy(&have_names); return; } } check_conflicts(vsctl_ctx, port_name, xasprintf("cannot create a port named %s", port_name)); for (i = 0; i < n_ifaces; i++) { check_conflicts(vsctl_ctx, iface_names[i], xasprintf("cannot create an interface named %s", iface_names[i])); } bridge = find_bridge(vsctl_ctx, br_name, true); ifaces = xmalloc(n_ifaces * sizeof *ifaces); for (i = 0; i < n_ifaces; i++) { ifaces[i] = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(ifaces[i], iface_names[i]); post_db_reload_expect_iface(ifaces[i]); } port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, port_name); ovsrec_port_set_interfaces(port, ifaces, n_ifaces); ovsrec_port_set_bond_fake_iface(port, fake_iface); if (bridge->parent) { int64_t tag = bridge->vlan; ovsrec_port_set_tag(port, &tag, 1); } for (i = 0; i < n_settings; i++) { char *error = ctl_set_column("Port", &port->header_, settings[i], ctx->symtab); if (error) { ctl_fatal("%s", error); } } bridge_insert_port((bridge->parent ? bridge->parent->br_cfg : bridge->br_cfg), port); struct vsctl_port *vsctl_port = add_port_to_cache(vsctl_ctx, bridge, port); for (i = 0; i < n_ifaces; i++) { add_iface_to_cache(vsctl_ctx, vsctl_port, ifaces[i]); } free(ifaces); } static void cmd_add_port(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, false, &ctx->argv[2], 1, &ctx->argv[3], ctx->argc - 3); } static void cmd_add_bond(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; bool fake_iface = shash_find(&ctx->options, "--fake-iface"); int n_ifaces; int i; n_ifaces = ctx->argc - 3; for (i = 3; i < ctx->argc; i++) { if (strchr(ctx->argv[i], '=')) { n_ifaces = i - 3; break; } } if (n_ifaces < 2) { ctl_fatal("add-bond requires at least 2 interfaces, but only " "%d were specified", n_ifaces); } add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, fake_iface, &ctx->argv[3], n_ifaces, &ctx->argv[n_ifaces + 3], ctx->argc - 3 - n_ifaces); } static void cmd_add_bond_iface(struct ctl_context *ctx) { vsctl_context_populate_cache(ctx); struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vsctl_port *port = find_port(vsctl_ctx, ctx->argv[1], true); const char *iface_name = ctx->argv[2]; if (may_exist) { struct vsctl_iface *iface = find_iface(vsctl_ctx, iface_name, false); if (iface) { if (iface->port == port) { return; } char *command = vsctl_context_to_string(ctx); ctl_fatal("\"%s\" but %s is actually attached to port %s", command, iface_name, iface->port->port_cfg->name); } } check_conflicts(vsctl_ctx, iface_name, xasprintf("cannot create an interface named %s", iface_name)); struct ovsrec_interface *iface = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(iface, iface_name); ovsrec_port_update_interfaces_addvalue(port->port_cfg, iface); post_db_reload_expect_iface(iface); add_iface_to_cache(vsctl_ctx, port, iface); } static void cmd_del_bond_iface(struct ctl_context *ctx) { vsctl_context_populate_cache(ctx); struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const char *iface_name = ctx->argv[ctx->argc - 1]; bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vsctl_iface *iface = find_iface(vsctl_ctx, iface_name, must_exist); if (!iface) { ovs_assert(!must_exist); return; } const char *port_name = ctx->argc > 2 ? ctx->argv[1] : NULL; if (port_name) { struct vsctl_port *port = find_port(vsctl_ctx, port_name, true); if (iface->port != port) { ctl_fatal("port %s does not have an interface %s", port_name, iface_name); } } if (ovs_list_is_short(&iface->port->ifaces)) { ctl_fatal("cannot delete last interface from port %s", iface->port->port_cfg->name); } ovsrec_port_update_interfaces_delvalue(iface->port->port_cfg, iface->iface_cfg); del_cached_iface(vsctl_ctx, iface); } static void cmd_del_port(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); bool with_iface = shash_find(&ctx->options, "--with-iface") != NULL; const char *target = ctx->argv[ctx->argc - 1]; struct vsctl_port *port; vsctl_context_populate_cache(ctx); if (find_bridge(vsctl_ctx, target, false)) { if (must_exist) { ctl_fatal("cannot delete port %s because it is the local port " "for bridge %s (deleting this port requires deleting " "the entire bridge)", target, target); } port = NULL; } else if (!with_iface) { port = find_port(vsctl_ctx, target, must_exist); } else { struct vsctl_iface *iface; port = find_port(vsctl_ctx, target, false); if (!port) { iface = find_iface(vsctl_ctx, target, false); if (iface) { port = iface->port; } } if (must_exist && !port) { ctl_fatal("no port or interface named %s", target); } } if (port) { if (ctx->argc == 3) { struct vsctl_bridge *bridge; bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (port->bridge != bridge) { if (port->bridge->parent == bridge) { ctl_fatal("bridge %s does not have a port %s (although " "its child bridge %s does)", ctx->argv[1], ctx->argv[2], port->bridge->name); } else { ctl_fatal("bridge %s does not have a port %s", ctx->argv[1], ctx->argv[2]); } } } del_port(vsctl_ctx, port); } } static void cmd_port_to_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_port *port; vsctl_context_populate_cache(ctx); port = find_port(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%s\n", port->bridge->name); } static void cmd_br_to_vlan(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%d\n", bridge->vlan); } static void cmd_br_to_parent(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->parent) { bridge = bridge->parent; } ds_put_format(&ctx->output, "%s\n", bridge->name); } static void cmd_list_ifaces(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct vsctl_port *port; struct svec ifaces; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); verify_ports(vsctl_ctx); svec_init(&ifaces); LIST_FOR_EACH (port, ports_node, &br->ports) { struct vsctl_iface *iface; LIST_FOR_EACH (iface, ifaces_node, &port->ifaces) { if (strcmp(iface->iface_cfg->name, br->name)) { svec_add(&ifaces, iface->iface_cfg->name); } } } output_sorted(&ifaces, &ctx->output); svec_destroy(&ifaces); } static void cmd_iface_to_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_iface *iface; vsctl_context_populate_cache(ctx); iface = find_iface(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%s\n", iface->port->bridge->name); } static void verify_controllers(struct ovsrec_bridge *bridge) { size_t i; ovsrec_bridge_verify_controller(bridge); for (i = 0; i < bridge->n_controller; i++) { ovsrec_controller_verify_target(bridge->controller[i]); } } static void pre_controller(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_controller_col_target); ovsdb_idl_add_column(ctx->idl, &ovsrec_controller_col_inactivity_probe); } static void cmd_get_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct svec targets; size_t i; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } verify_controllers(br->br_cfg); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < br->br_cfg->n_controller; i++) { svec_add(&targets, br->br_cfg->controller[i]->target); } svec_sort(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_controllers(struct ovsrec_controller **controllers, size_t n_controllers) { size_t i; for (i = 0; i < n_controllers; i++) { ovsrec_controller_delete(controllers[i]); } } static void cmd_del_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_bridge *br; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true)->br_cfg; verify_controllers(br); if (br->controller) { delete_controllers(br->controller, br->n_controller); ovsrec_bridge_set_controller(br, NULL, 0); } } static struct ovsrec_controller ** insert_controllers(struct ctl_context *ctx, char *targets[], size_t n) { struct ovsrec_controller **controllers; size_t i; const char *inactivity_probe = shash_find_data(&ctx->options, "--inactivity-probe"); controllers = xmalloc(n * sizeof *controllers); for (i = 0; i < n; i++) { if (vconn_verify_name(targets[i]) && pvconn_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } controllers[i] = ovsrec_controller_insert(ctx->txn); ovsrec_controller_set_target(controllers[i], targets[i]); if (inactivity_probe) { int64_t msecs = atoll(inactivity_probe); ovsrec_controller_set_inactivity_probe(controllers[i], &msecs, 1); } } return controllers; } static void cmd_set_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_controller **controllers; struct ovsrec_bridge *br; size_t n; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true)->br_cfg; verify_controllers(br); delete_controllers(br->controller, br->n_controller); n = ctx->argc - 2; controllers = insert_controllers(ctx, &ctx->argv[2], n); ovsrec_bridge_set_controller(br, controllers, n); free(controllers); } static void cmd_get_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; const char *fail_mode; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } ovsrec_bridge_verify_fail_mode(br->br_cfg); fail_mode = br->br_cfg->fail_mode; if (fail_mode && strlen(fail_mode)) { ds_put_format(&ctx->output, "%s\n", fail_mode); } } static void cmd_del_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true); ovsrec_bridge_set_fail_mode(br->br_cfg, NULL); } static void cmd_set_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; const char *fail_mode = ctx->argv[2]; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true); if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) { ctl_fatal("fail-mode must be \"standalone\" or \"secure\""); } ovsrec_bridge_set_fail_mode(br->br_cfg, fail_mode); } static void verify_managers(const struct ovsrec_open_vswitch *ovs) { size_t i; ovsrec_open_vswitch_verify_manager_options(ovs); for (i = 0; i < ovs->n_manager_options; ++i) { const struct ovsrec_manager *mgr = ovs->manager_options[i]; ovsrec_manager_verify_target(mgr); } } static void pre_manager(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_manager_options); ovsdb_idl_add_column(ctx->idl, &ovsrec_manager_col_target); ovsdb_idl_add_column(ctx->idl, &ovsrec_manager_col_inactivity_probe); } static void cmd_get_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; struct svec targets; size_t i; verify_managers(ovs); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < ovs->n_manager_options; i++) { svec_add(&targets, ovs->manager_options[i]->target); } svec_sort_unique(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_managers(const struct ovsrec_open_vswitch *ovs) { size_t i; /* Delete Manager rows pointed to by 'manager_options' column. */ for (i = 0; i < ovs->n_manager_options; i++) { ovsrec_manager_delete(ovs->manager_options[i]); } /* Delete 'Manager' row refs in 'manager_options' column. */ ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0); } static void cmd_del_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; verify_managers(ovs); delete_managers(ovs); } static void insert_managers(struct vsctl_context *vsctl_ctx, char *targets[], size_t n, struct shash *options) { struct ovsrec_manager **managers; size_t i; const char *inactivity_probe = shash_find_data(options, "--inactivity-probe"); /* Insert each manager in a new row in Manager table. */ managers = xmalloc(n * sizeof *managers); for (i = 0; i < n; i++) { if (stream_verify_name(targets[i]) && pstream_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } managers[i] = ovsrec_manager_insert(vsctl_ctx->base.txn); ovsrec_manager_set_target(managers[i], targets[i]); if (inactivity_probe) { int64_t msecs = atoll(inactivity_probe); ovsrec_manager_set_inactivity_probe(managers[i], &msecs, 1); } } /* Store uuids of new Manager rows in 'manager_options' column. */ ovsrec_open_vswitch_set_manager_options(vsctl_ctx->ovs, managers, n); free(managers); } static void cmd_set_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const size_t n = ctx->argc - 1; verify_managers(vsctl_ctx->ovs); delete_managers(vsctl_ctx->ovs); insert_managers(vsctl_ctx, &ctx->argv[1], n, &ctx->options); } static void pre_cmd_get_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_private_key); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_certificate); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_ca_cert); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_bootstrap_ca_cert); } static void cmd_get_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); if (ssl) { ovsrec_ssl_verify_private_key(ssl); ovsrec_ssl_verify_certificate(ssl); ovsrec_ssl_verify_ca_cert(ssl); ovsrec_ssl_verify_bootstrap_ca_cert(ssl); ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key); ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate); ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert); ds_put_format(&ctx->output, "Bootstrap: %s\n", ssl->bootstrap_ca_cert ? "true" : "false"); } } static void pre_cmd_del_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); } static void cmd_del_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; if (ssl) { ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); ovsrec_ssl_delete(ssl); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, NULL); } } static void pre_cmd_set_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); } static void cmd_set_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool bootstrap = shash_find(&ctx->options, "--bootstrap"); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); if (ssl) { ovsrec_ssl_delete(ssl); } ssl = ovsrec_ssl_insert(ctx->txn); ovsrec_ssl_set_private_key(ssl, ctx->argv[1]); ovsrec_ssl_set_certificate(ssl, ctx->argv[2]); ovsrec_ssl_set_ca_cert(ssl, ctx->argv[3]); ovsrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, ssl); } static void autoattach_insert_mapping(struct ovsrec_autoattach *aa, int64_t isid, int64_t vlan) { int64_t *key_mappings, *value_mappings; size_t i; key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings + 1)); value_mappings = xmalloc(sizeof *aa->value_mappings * (aa->n_mappings + 1)); for (i = 0; i < aa->n_mappings; i++) { key_mappings[i] = aa->key_mappings[i]; value_mappings[i] = aa->value_mappings[i]; } key_mappings[aa->n_mappings] = isid; value_mappings[aa->n_mappings] = vlan; ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, aa->n_mappings + 1); free(key_mappings); free(value_mappings); } static void cmd_add_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; int64_t isid, vlan; char *nptr = NULL; isid = strtoull(ctx->argv[2], &nptr, 10); if (nptr == ctx->argv[2] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[2]); return; } vlan = strtoull(ctx->argv[3], &nptr, 10); if (nptr == ctx->argv[3] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[3]); return; } vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } if (br->br_cfg) { if (!br->br_cfg->auto_attach) { struct ovsrec_autoattach *aa = ovsrec_autoattach_insert(ctx->txn); ovsrec_bridge_set_auto_attach(br->br_cfg, aa); } autoattach_insert_mapping(br->br_cfg->auto_attach, isid, vlan); } } static void del_aa_mapping(struct ovsrec_autoattach *aa, int64_t isid, int64_t vlan) { int64_t *key_mappings, *value_mappings; size_t i, n; key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings)); value_mappings = xmalloc(sizeof *value_mappings * (aa->n_mappings)); for (i = n = 0; i < aa->n_mappings; i++) { if (aa->key_mappings[i] != isid && aa->value_mappings[i] != vlan) { key_mappings[n] = aa->key_mappings[i]; value_mappings[n++] = aa->value_mappings[i]; } } ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, n); free(key_mappings); free(value_mappings); } static void cmd_del_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; int64_t isid, vlan; char *nptr = NULL; isid = strtoull(ctx->argv[2], &nptr, 10); if (nptr == ctx->argv[2] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[2]); return; } vlan = strtoull(ctx->argv[3], &nptr, 10); if (nptr == ctx->argv[3] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[3]); return; } vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } if (br->br_cfg && br->br_cfg->auto_attach && br->br_cfg->auto_attach->key_mappings && br->br_cfg->auto_attach->value_mappings) { size_t i; for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) { if (br->br_cfg->auto_attach->key_mappings[i] == isid && br->br_cfg->auto_attach->value_mappings[i] == vlan) { del_aa_mapping(br->br_cfg->auto_attach, isid, vlan); break; } } } } static void pre_aa_mapping(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_auto_attach); ovsdb_idl_add_column(ctx->idl, &ovsrec_autoattach_col_mappings); } static void verify_auto_attach(struct ovsrec_bridge *bridge) { if (bridge) { ovsrec_bridge_verify_auto_attach(bridge); if (bridge->auto_attach) { ovsrec_autoattach_verify_mappings(bridge->auto_attach); } } } static void cmd_get_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } verify_auto_attach(br->br_cfg); if (br->br_cfg && br->br_cfg->auto_attach && br->br_cfg->auto_attach->key_mappings && br->br_cfg->auto_attach->value_mappings) { size_t i; for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) { ds_put_format(&ctx->output, "%"PRId64" %"PRId64"\n", br->br_cfg->auto_attach->key_mappings[i], br->br_cfg->auto_attach->value_mappings[i]); } } } static const struct ctl_table_class tables[OVSREC_N_TABLES] = { [OVSREC_TABLE_BRIDGE].row_ids[0] = {&ovsrec_bridge_col_name, NULL, NULL}, [OVSREC_TABLE_CONTROLLER].row_ids[0] = {&ovsrec_bridge_col_name, NULL, &ovsrec_bridge_col_controller}, [OVSREC_TABLE_INTERFACE].row_ids[0] = {&ovsrec_interface_col_name, NULL, NULL}, [OVSREC_TABLE_MIRROR].row_ids[0] = {&ovsrec_mirror_col_name, NULL, NULL}, [OVSREC_TABLE_MANAGER].row_ids[0] = {&ovsrec_manager_col_target, NULL, NULL}, [OVSREC_TABLE_NETFLOW].row_ids[0] = {&ovsrec_bridge_col_name, NULL, &ovsrec_bridge_col_netflow}, [OVSREC_TABLE_PORT].row_ids[0] = {&ovsrec_port_col_name, NULL, NULL}, [OVSREC_TABLE_QOS].row_ids[0] = {&ovsrec_port_col_name, NULL, &ovsrec_port_col_qos}, [OVSREC_TABLE_SFLOW].row_ids[0] = {&ovsrec_bridge_col_name, NULL, &ovsrec_bridge_col_sflow}, [OVSREC_TABLE_FLOW_TABLE].row_ids[0] = {&ovsrec_flow_table_col_name, NULL, NULL}, [OVSREC_TABLE_IPFIX].row_ids[0] = {&ovsrec_bridge_col_name, NULL, &ovsrec_bridge_col_ipfix}, [OVSREC_TABLE_AUTOATTACH].row_ids[0] = {&ovsrec_bridge_col_name, NULL, &ovsrec_bridge_col_auto_attach}, [OVSREC_TABLE_FLOW_SAMPLE_COLLECTOR_SET].row_ids[0] = {&ovsrec_flow_sample_collector_set_col_id, NULL, NULL}, }; static void post_db_reload_check_init(void) { n_neoteric_ifaces = 0; } static void post_db_reload_expect_iface(const struct ovsrec_interface *iface) { if (n_neoteric_ifaces >= allocated_neoteric_ifaces) { neoteric_ifaces = x2nrealloc(neoteric_ifaces, &allocated_neoteric_ifaces, sizeof *neoteric_ifaces); } neoteric_ifaces[n_neoteric_ifaces++] = iface->header_.uuid; } static bool post_db_reload_do_checks(const struct vsctl_context *vsctl_ctx) { bool reconfig_failed = false; size_t i; for (i = 0; i < n_neoteric_ifaces; i++) { const struct uuid *uuid; uuid = ovsdb_idl_txn_get_insert_uuid(vsctl_ctx->base.txn, &neoteric_ifaces[i]); if (uuid) { const struct ovsrec_interface *iface; iface = ovsrec_interface_get_for_uuid(vsctl_ctx->base.idl, uuid); if (iface && (!iface->ofport || *iface->ofport == -1)) { if (iface->error && *iface->error) { ovs_error(0, "Error detected while setting up '%s': %s. " "See ovs-vswitchd log for details.", iface->name, iface->error); } else { ovs_error(0, "Error detected while setting up '%s'. " "See ovs-vswitchd log for details.", iface->name); } reconfig_failed = true; } } } if (reconfig_failed) { ovs_error(0, "The default log directory is \"%s\".", ovs_logdir()); } return reconfig_failed; } static void vsctl_context_init_command(struct vsctl_context *vsctl_ctx, struct ctl_command *command, bool last_command) { ctl_context_init_command(&vsctl_ctx->base, command, last_command); vsctl_ctx->verified_ports = false; } static void vsctl_context_init(struct vsctl_context *vsctl_ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, const struct ovsrec_open_vswitch *ovs, struct ovsdb_symbol_table *symtab) { ctl_context_init(&vsctl_ctx->base, command, idl, txn, symtab, vsctl_context_invalidate_cache); if (command) { vsctl_ctx->verified_ports = false; } vsctl_ctx->ovs = ovs; vsctl_ctx->cache_valid = false; } static void vsctl_context_done_command(struct vsctl_context *vsctl_ctx, struct ctl_command *command) { ctl_context_done_command(&vsctl_ctx->base, command); } static void vsctl_context_done(struct vsctl_context *vsctl_ctx, struct ctl_command *command) { ctl_context_done(&vsctl_ctx->base, command); } static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; ovsdb_idl_add_table(idl, &ovsrec_table_open_vswitch); if (wait_for_reload) { ovsdb_idl_add_column(idl, &ovsrec_open_vswitch_col_cur_cfg); } for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct vsctl_context vsctl_ctx; ds_init(&c->output); c->table = NULL; vsctl_context_init(&vsctl_ctx, c, idl, NULL, NULL, NULL); (c->syntax->prerequisites)(&vsctl_ctx.base); if (vsctl_ctx.base.error) { ctl_fatal("%s", vsctl_ctx.base.error); } vsctl_context_done(&vsctl_ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static char * vsctl_parent_process_info(void) { #ifdef __linux__ pid_t parent_pid; struct ds s; parent_pid = getppid(); ds_init(&s); /* Retrive the command line of the parent process, except the init * process since /proc/0 does not exist. */ if (parent_pid) { char *procfile; FILE *f; procfile = xasprintf("/proc/%d/cmdline", parent_pid); f = fopen(procfile, "r"); free(procfile); if (f) { ds_get_line(&s, f); fclose(f); } } else { ds_put_cstr(&s, "init"); } ds_put_format(&s, " (pid %d)", parent_pid); return ds_steal_cstr(&s); #else return NULL; #endif } static bool do_vsctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl, bool *postdb_err) { struct ovsdb_idl_txn *txn; const struct ovsrec_open_vswitch *ovs; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct vsctl_context vsctl_ctx; struct ctl_command *c; struct shash_node *node; int64_t next_cfg = 0; char *ppid_info = NULL; ovs_assert(postdb_err); *postdb_err = false; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ppid_info = vsctl_parent_process_info(); if (ppid_info) { ovsdb_idl_txn_add_comment(txn, "ovs-vsctl (invoked by %s): %s", ppid_info, args); free(ppid_info); } else { ovsdb_idl_txn_add_comment(txn, "ovs-vsctl: %s", args); } ovs = ovsrec_open_vswitch_first(idl); if (!ovs) { /* XXX add verification that table is empty */ ovs = ovsrec_open_vswitch_insert(txn); } if (wait_for_reload) { ovsdb_idl_txn_increment(txn, &ovs->header_, &ovsrec_open_vswitch_col_next_cfg, false); } post_db_reload_check_init(); symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } vsctl_context_init(&vsctl_ctx, NULL, idl, txn, ovs, symtab); for (c = commands; c < &commands[n_commands]; c++) { vsctl_context_init_command(&vsctl_ctx, c, c == &commands[n_commands - 1]); if (c->syntax->run) { (c->syntax->run)(&vsctl_ctx.base); } if (vsctl_ctx.base.error) { ctl_fatal("%s", vsctl_ctx.base.error); } vsctl_context_done_command(&vsctl_ctx, c); if (vsctl_ctx.base.try_again) { vsctl_context_done(&vsctl_ctx, NULL); goto try_again; } } vsctl_context_done(&vsctl_ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created (e.g. " "with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (wait_for_reload && status == TXN_SUCCESS) { next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); } if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { vsctl_context_init(&vsctl_ctx, c, idl, txn, ovs, symtab); (c->syntax->postprocess)(&vsctl_ctx.base); if (vsctl_ctx.base.error) { ctl_fatal("%s", vsctl_ctx.base.error); } vsctl_context_done(&vsctl_ctx, c); } } } switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", ovsdb_idl_txn_get_error(txn)); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); if (wait_for_reload && status != TXN_UNCHANGED) { /* Even, if --retry flag was not specified, ovs-vsctl still * has to retry to establish OVSDB connection, if wait_for_reload * was set. Otherwise, ovs-vsctl would end up waiting forever * until cur_cfg would be updated. */ ovsdb_idl_enable_reconnect(idl); for (;;) { ovsdb_idl_run(idl); OVSREC_OPEN_VSWITCH_FOR_EACH (ovs, idl) { if (ovs->cur_cfg >= next_cfg) { if (post_db_reload_do_checks(&vsctl_ctx)) { *postdb_err = true; } goto done; } } ovsdb_idl_wait(idl); poll_block(); } done: ; } ovsdb_idl_txn_destroy(txn); ovsdb_idl_destroy(idl); return true; try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); the_idl_txn = NULL; ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } return false; } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void vsctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } /* * Developers who add new commands to the 'struct ctl_command_syntax' must * define the 'arguments' member of the struct. The following keywords are * available for composing the argument format: * * TABLE RECORD BRIDGE PARENT PORT * KEY VALUE ARG KEY=VALUE ?KEY=VALUE * IFACE SYSIFACE COLUMN COLUMN?:KEY COLUMN?:KEY=VALUE * MODE CA-CERT CERTIFICATE PRIVATE-KEY * TARGET NEW-* (e.g. NEW-PORT) * * For argument types not listed above, just uses 'ARG' as place holder. * * Encloses the keyword with '[]' if it is optional. Appends '...' to * keyword or enclosed keyword to indicate that the argument can be specified * multiple times. * * */ static const struct ctl_command_syntax vsctl_commands[] = { /* Open vSwitch commands. */ {"init", 0, 0, "", NULL, cmd_init, NULL, "", RW}, /* Bridge commands. */ {"add-br", 1, 3, "NEW-BRIDGE [PARENT] [NEW-VLAN]", pre_get_info, cmd_add_br, NULL, "--may-exist", RW}, {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_del_br, NULL, "--if-exists", RW}, {"list-br", 0, 0, "", pre_get_info, cmd_list_br, NULL, "--real,--fake", RO}, {"br-exists", 1, 1, "BRIDGE", pre_get_info, cmd_br_exists, NULL, "", RO}, {"br-to-vlan", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_vlan, NULL, "", RO}, {"br-to-parent", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_parent, NULL, "", RO}, {"br-set-external-id", 2, 3, "BRIDGE KEY [VALUE]", pre_cmd_br_set_external_id, cmd_br_set_external_id, NULL, "", RW}, {"br-get-external-id", 1, 2, "BRIDGE [KEY]", pre_cmd_br_get_external_id, cmd_br_get_external_id, NULL, "", RO}, /* Port commands. */ {"list-ports", 1, 1, "BRIDGE", pre_get_info, cmd_list_ports, NULL, "", RO}, {"add-port", 2, INT_MAX, "BRIDGE NEW-PORT [COLUMN[:KEY]=VALUE]...", pre_get_info, cmd_add_port, NULL, "--may-exist", RW}, {"del-port", 1, 2, "[BRIDGE] PORT|IFACE", pre_get_info, cmd_del_port, NULL, "--if-exists,--with-iface", RW}, {"port-to-br", 1, 1, "PORT", pre_get_info, cmd_port_to_br, NULL, "", RO}, /* Bond commands. */ {"add-bond", 4, INT_MAX, "BRIDGE BOND IFACE... [COLUMN[:KEY]=VALUE]...", pre_get_info, cmd_add_bond, NULL, "--may-exist,--fake-iface", RW}, {"add-bond-iface", 2, 2, "BOND IFACE", pre_get_info, cmd_add_bond_iface, NULL, "--may-exist", RW}, {"del-bond-iface", 1, 2, "[BOND] IFACE", pre_get_info, cmd_del_bond_iface, NULL, "--if-exists", RW}, /* Interface commands. */ {"list-ifaces", 1, 1, "BRIDGE", pre_get_info, cmd_list_ifaces, NULL, "", RO}, {"iface-to-br", 1, 1, "IFACE", pre_get_info, cmd_iface_to_br, NULL, "", RO}, /* Controller commands. */ {"get-controller", 1, 1, "BRIDGE", pre_controller, cmd_get_controller, NULL, "", RO}, {"del-controller", 1, 1, "BRIDGE", pre_controller, cmd_del_controller, NULL, "", RW}, {"set-controller", 1, INT_MAX, "BRIDGE TARGET...", pre_controller, cmd_set_controller, NULL, "--inactivity-probe=", RW}, {"get-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_get_fail_mode, NULL, "", RO}, {"del-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_del_fail_mode, NULL, "", RW}, {"set-fail-mode", 2, 2, "BRIDGE MODE", pre_get_info, cmd_set_fail_mode, NULL, "", RW}, /* Manager commands. */ {"get-manager", 0, 0, "", pre_manager, cmd_get_manager, NULL, "", RO}, {"del-manager", 0, 0, "", pre_manager, cmd_del_manager, NULL, "", RW}, {"set-manager", 1, INT_MAX, "TARGET...", pre_manager, cmd_set_manager, NULL, "--inactivity-probe=", RW}, /* SSL/TLS commands. */ {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", RW}, /* Auto Attach commands. */ {"add-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_add_aa_mapping, NULL, "", RW}, {"del-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_del_aa_mapping, NULL, "", RW}, {"get-aa-mapping", 1, 1, "BRIDGE", pre_aa_mapping, cmd_get_aa_mapping, NULL, "", RO}, /* Switch commands. */ {"emer-reset", 0, 0, "", pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW}, /* Zone and CT Timeout Policy commands. */ {"add-zone-tp", 3, INT_MAX, "", pre_get_zone, cmd_add_zone_tp, NULL, "--may-exist", RW}, {"del-zone-tp", 2, 2, "", pre_get_zone, cmd_del_zone_tp, NULL, "--if-exists", RW}, {"list-zone-tp", 1, 1, "", pre_get_zone, cmd_list_zone_tp, NULL, "", RO}, /* Datapath capabilities. */ {"list-dp-cap", 1, 1, "", pre_get_dp_cap, cmd_list_dp_cap, NULL, "", RO}, /* CT zone limit. */ {"set-zone-limit", 3, 3, "ARG ARG ARG", pre_get_zone, cmd_set_zone_limit, NULL, "", RW}, {"del-zone-limit", 2, 2, "ARG ARG", pre_get_zone, cmd_del_zone_limit, NULL, "--if-exists", RW}, {"list-zone-limits", 1, 1, "ARG", pre_get_zone, cmd_list_zone_limits, NULL, "", RO}, {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; /* Registers vsctl and common db commands. */ static void vsctl_cmd_init(void) { ctl_init(&ovsrec_idl_class, ovsrec_table_classes, tables, cmd_show_tables, vsctl_exit); ctl_register_commands(vsctl_commands); } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/000077500000000000000000000000001514270232600234375ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/bridge_loop.bt000077500000000000000000000116441514270232600262640ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * Copyright (c) 2021 Red Hat, Inc. * * 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. * * * Script information: * ------------------- * bridge_loop.bt uses the USDTs in the ovs-vswitchd's main bridge loop * to report how long it spends running a single loop, as well as the time * it spends waiting in the poll_loop(). Once done, it will also print a * histogram for the time spent. * * The following is an example of how to use the script on the running * ovs-vswitchd process: * * $ bridge_loop.bt -p `pgrep -n ovs-vswitchd` * Attaching 4 probes... * -------------------------------------------------------------- * Tracing ovs-vswitchd's main() loop... Hit Ctrl-C to end. * -------------------------------------------------------------- * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000230706 * - [886467@ovs-vswitchd] poll_block() wait time: 0:00:00.501854292 * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000266445 * - [886467@ovs-vswitchd] poll_block() wait time: 0:00:00.499750288 * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000254856 * - [886467@ovs-vswitchd] poll_block() wait time: 0:00:00.499944280 * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000267390 * - [886467@ovs-vswitchd] poll_block() wait time: 0:00:00.093566288 * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000316774 * - [886467@ovs-vswitchd] poll_block() wait time: 0:00:00.406697754 * - [886467@ovs-vswitchd] bridge run loop time : 0:00:00.000264505 * ^C * -------------------------------------------------------------- * Showing run time histograms in micro seconds: * -------------------------------------------------------------- * * @bridge_run_time: * [0, 1000) 6 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| * * * @poll_block_wait_time: * [90000, 120000) 1 |@@@@@@@@@@@@@@@@@ | * [120000, 150000) 0 | | * [150000, 180000) 0 | | * [180000, 210000) 0 | | * [210000, 240000) 0 | | * [240000, 270000) 0 | | * [270000, 300000) 0 | | * [300000, 330000) 0 | | * [330000, 360000) 0 | | * [360000, 390000) 0 | | * [390000, 420000) 1 |@@@@@@@@@@@@@@@@@ | * [420000, 450000) 0 | | * [450000, 480000) 0 | | * [480000, 510000) 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| */ BEGIN { printf("--------------------------------------------------------------\n"); printf("Tracing ovs-vswitchd's main() loop... Hit Ctrl-C to end.\n"); printf("--------------------------------------------------------------\n"); } usdt::main:poll_block { @pb_start[tid] = nsecs; if (@rs_start[tid] != 0) { $delta = nsecs - @rs_start[tid]; printf("- [%d@%s] bridge run loop time : %u:%2.2u:%2.2u.%9.9u\n", tid, comm, $delta / 3600 / 1000000000, $delta / 60 / 1000000000 % 60, $delta / 1000000000 % 60, $delta % 1000000000); @bridge_run_time = lhist($delta / 1000, 0, 1000000, 1000); } } usdt::main:run_start { @rs_start[tid] = nsecs; if (@pb_start[tid] != 0) { $delta = nsecs - @pb_start[tid]; printf("- [%d@%s] poll_block() wait time: %u:%2.2u:%2.2u.%9.9u\n", tid, comm, $delta / 3600 / 1000000000, $delta / 60 / 1000000000 % 60, $delta / 1000000000 % 60, $delta % 1000000000); @poll_block_wait_time = lhist($delta / 1000, 0, 30000000, 30000); } } END { clear(@rs_start); clear(@pb_start); printf("\n"); printf("--------------------------------------------------------------\n"); printf("Showing run time histograms in micro seconds:\n"); printf("--------------------------------------------------------------"); } openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/dpif_op_nl_monitor.py000077500000000000000000000637651514270232600277150ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022 Red Hat, Inc. # # 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. # # Script information: # ------------------- # dpif_nl_exec_monitor.py uses the dpif_netlink_operate__:op_flow_execute USDT # probe to receive all DPIF_OP_EXECUTE operations that are queued for # transmission over the netlink socket. It will do some basic decoding, and if # requested a packet dump. Note that there are also options to obtain # additional information for the DPIF_OP_FLOW_* operations. # # Here is an example: # # # ./dpif_op_nl_monitor.py --packet-decode decode --trace-exec-op # Display DPIF_OP_EXECUTE operations being queued for transmission... # TIME CPU COMM PID NL_SIZE # Display DPIF operations being queued for transmission onto the netlink... # TIME CPU COMM PID NL_SIZE DPIF_... # 2162030.641541513 5 handler4 2951007 164 DPIF_... # nlmsghdr : len = 0, type = 36, flags = 1, seq = 0, pid = 0 # genlmsghdr: cmd = DPIF_OP_EXECUTE, version = 1, reserved = 0 # ovs_header: dp_ifindex = 21 # > Decode OVS_PACKET_ATTR_* TLVs: # nla_len 46, nla_type OVS_PACKET_ATTR_PACKET[1], data: 00 00 00... # nla_len 20, nla_type OVS_PACKET_ATTR_KEY[2], data: 08 00 02 00... # > Decode OVS_KEY_ATTR_* TLVs: # nla_len 8, nla_type OVS_KEY_ATTR_PRIORITY[2], data: 00 00... # nla_len 8, nla_type OVS_KEY_ATTR_SKB_MARK[15], data: 00 00... # nla_len 88, nla_type OVS_PACKET_ATTR_ACTIONS[3], data: 4c 00 03... # > Decode OVS_ACTION_ATTR_* TLVs: # nla_len 76, nla_type OVS_ACTION_ATTR_SET[3], data: 48 00... # > Decode OVS_TUNNEL_KEY_ATTR_* TLVs: # nla_len 12, nla_type OVS_TUNNEL_KEY_ATTR_ID[0], data:... # nla_len 20, nla_type OVS_TUNNEL_KEY_ATTR_IPV6_DST[13], ... # nla_len 5, nla_type OVS_TUNNEL_KEY_ATTR_TTL[4], data: 40 # nla_len 4, nla_type OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT... # nla_len 4, nla_type OVS_TUNNEL_KEY_ATTR_CSUM[6], data: # nla_len 6, nla_type OVS_TUNNEL_KEY_ATTR_TP_DST[10],... # nla_len 12, nla_type OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS... # nla_len 8, nla_type OVS_ACTION_ATTR_OUTPUT[1], data: 02 00 00 00 # - Dumping OVS_PACKET_ATR_PACKET data: # ###[ Ethernet ]### # dst = 00:00:00:00:ec:01 # src = 04:f4:bc:28:57:00 # type = IPv4 # ###[ IP ]### # version = 4 # ihl = 5 # tos = 0x0 # len = 50 # id = 0 # flags = # frag = 0 # ttl = 127 # proto = icmp # chksum = 0x2767 # src = 10.0.0.1 # dst = 10.0.0.100 # \options \ # ###[ ICMP ]### # type = echo-request # code = 0 # chksum = 0xf7f3 # id = 0x0 # seq = 0xc # # The example above dumps the full netlink and packet decode. However options # exist to disable this. For a complete list of options, please use the # '--help' or '-h' argument. # from bcc import BPF, USDT, USDTException from os.path import exists from scapy.all import hexdump, wrpcap from scapy.layers.l2 import Ether import argparse import psutil import re import struct import sys # # Actual eBPF source code # ebpf_source = """ #include #define MAX_NLMSG struct event_t { u32 cpu; u32 pid; u64 ts; u32 nl_size; char comm[TASK_COMM_LEN]; u8 nl_msg[MAX_NLMSG]; }; struct ofpbuf { void *base; void *data; uint32_t size; /* The actual structure is longer, but we are only interested in the * first couple of entries. */ }; BPF_RINGBUF_OUTPUT(events, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); static int trace_event(struct ofpbuf *nlbuf) { uint32_t size; struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { uint32_t type = 0; uint64_t *value = dropcnt.lookup(&type); if (value) __sync_fetch_and_add(value, 1); return 1; } event->ts = bpf_ktime_get_ns(); event->cpu = bpf_get_smp_processor_id(); event->pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&event->comm, sizeof(event->comm)); event->nl_size = nlbuf->size; if (event->nl_size > MAX_NLMSG) size = MAX_NLMSG; else size = event->nl_size; bpf_probe_read(&event->nl_msg, size, nlbuf->data); events.ringbuf_submit(event, 0); return 0; } #if int trace__op_execute(struct pt_regs *ctx) { struct ofpbuf nlbuf; bpf_usdt_readarg_p(5, ctx, &nlbuf, sizeof(nlbuf)); return trace_event(&nlbuf); }; #endif #if int trace__op_flow_put(struct pt_regs *ctx) { struct ofpbuf nlbuf; bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); return trace_event(&nlbuf); }; #endif #if int trace__op_flow_del(struct pt_regs *ctx) { struct ofpbuf nlbuf; bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); return trace_event(&nlbuf); }; #endif #if int trace__op_flow_get(struct pt_regs *ctx) { struct ofpbuf nlbuf; bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); return trace_event(&nlbuf); }; #endif """ # # print_event() # def print_event(ctx, data, size): event = b["events"].event(data) if event.nl_size < options.nlmsg_size: nl_size = event.nl_size else: nl_size = options.nlmsg_size print("{:<18.9f} {:<4} {:<16} {:<10} {:<10} {}". format(event.ts / 1000000000, event.cpu, event.comm.decode("utf-8"), event.pid, event.nl_size, get_ovs_dpif_op_str(get_cmd_type_from_nlm( bytes(event.nl_msg)[:nl_size])))) # # Dumping the netlink message data if requested. # if options.nlmsg_decode == "hex": # # Abuse scapy's hex dump to dump flow key # print(re.sub("^", " " * 4, hexdump(Ether(bytes(event.nl_msg)[:nl_size]), dump=True), flags=re.MULTILINE)) if options.nlmsg_decode == "nlraw": decode_result = decode_nlm(bytes(event.nl_msg)[:nl_size], dump=True) else: decode_result = decode_nlm(bytes(event.nl_msg)[:nl_size], dump=False) # # Decode packet only if there is data # if "OVS_PACKET_ATTR_PACKET" not in decode_result: return pkt_data = decode_result["OVS_PACKET_ATTR_PACKET"] indent = 4 if options.nlmsg_decode != "nlraw" else 6 if options.packet_decode != "none": print("{}- Dumping OVS_PACKET_ATR_PACKET data:".format(" " * indent)) if options.packet_decode == "hex": print(re.sub("^", " " * indent, hexdump(pkt_data, dump=True), flags=re.MULTILINE)) packet = Ether(pkt_data) if options.packet_decode == "decode": print(re.sub("^", " " * indent, packet.show(dump=True), flags=re.MULTILINE)) if options.pcap is not None: wrpcap(options.pcap, packet, append=True) # # decode_nlm_tlvs() # def decode_nlm_tlvs(tlvs, header=None, indent=4, dump=True, attr_to_str_func=None, decode_tree=None): bytes_left = len(tlvs) result = {} if dump: print("{}{}".format(" " * indent, header)) while bytes_left: if bytes_left < 4: if dump: print("{}WARN: decode truncated; can't read header".format( " " * indent)) break nla_len, nla_type = struct.unpack("=HH", tlvs[:4]) if nla_len < 4: if dump: print("{}WARN: decode truncated; nla_len < 4".format( " " * indent)) break nla_data = tlvs[4:nla_len] trunc = "" if attr_to_str_func is None: nla_type_name = "type_{}".format(nla_type) else: nla_type_name = attr_to_str_func(nla_type) if nla_len > bytes_left: trunc = "..." nla_data = nla_data[:(bytes_left - 4)] else: result[nla_type_name] = nla_data if dump: print("{}nla_len {}, nla_type {}[{}], data: {}{}".format( " " * indent, nla_len, nla_type_name, nla_type, "".join("{:02x} ".format(b) for b in nla_data), trunc)) # # If we have the full data, try to decode further # if trunc == "" and decode_tree is not None \ and nla_type_name in decode_tree: node = decode_tree[nla_type_name] decode_nlm_tlvs(nla_data, header=node["header"], indent=indent + node["indent"], dump=True, attr_to_str_func=node["attr_str_func"], decode_tree=node["decode_tree"]) if trunc != "": if dump: print("{}WARN: decode truncated; nla_len > msg_len[{}] ". format(" " * indent, bytes_left)) break # update next offset, but make sure it's aligned correctly next_offset = (nla_len + 3) & ~(3) tlvs = tlvs[next_offset:] bytes_left -= next_offset return result # # get_cmd_type_from_nlm() # def get_cmd_type_from_nlm(nlm): # The netlink message consists of at least a 'struct nlmsghdr' (16-bytes) # followed by a 'struct genlmsghdr'. The first byte of the genlmsghdr # structure contains the command. Which is what we will extract here. if len(nlm) < 17: return -1 return nlm[16] # # decode_nlm() # def decode_nlm(msg, indent=4, dump=True): result = {} # # Decode 'struct nlmsghdr' # if dump: print("{}nlmsghdr : len = {}, type = {}, flags = {}, seq = {}, " "pid = {}".format(" " * indent, *struct.unpack("=IHHII", msg[:16]))) msg = msg[16:] # # Decode 'struct genlmsghdr' # cmd, version, reserved = struct.unpack("=BBH", msg[:4]) if dump: print("{}genlmsghdr: cmd = {}, version = {}, reserved = {}".format( " " * indent, get_ovs_dpif_op_str(cmd), version, reserved)) msg = msg[4:] # # Decode 'struct ovs_header' # if dump: print("{}ovs_header: dp_ifindex = {}".format( " " * indent, *struct.unpack("=I", msg[:4]))) msg = msg[4:] # # Decode TLVs # nl_key_attr = { "header": "> Decode OVS_KEY_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_key_attr_str, "decode_tree": { "OVS_KEY_ATTR_ENCAP": { "header": "> Decode OVS_KEY_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_key_attr_str, "decode_tree": { "OVS_KEY_ATTR_ENCAP": { "header": "> Decode OVS_KEY_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_key_attr_str, "decode_tree": None, }, }, }, } } nl_action_attr = { "header": "> Decode OVS_ACTION_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_action_attr_str, "decode_tree": { "OVS_ACTION_ATTR_SET": { "header": "> Decode OVS_KEY_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_key_attr_str, "decode_tree": { "OVS_KEY_ATTR_TUNNEL": { "header": "> Decode OVS_TUNNEL_KEY_ATTR_* TLVs:", "indent": 4, "attr_str_func": get_ovs_tunnel_key_attr_str, "decode_tree": None, }, }, }, }, } nl_attr_tree_exec = { "OVS_PACKET_ATTR_KEY": nl_key_attr, "OVS_PACKET_ATTR_ACTIONS": nl_action_attr, } nl_attr_tree_put = { "OVS_FLOW_ATTR_KEY": nl_key_attr, "OVS_FLOW_ATTR_MASK": nl_key_attr, "OVS_FLOW_ATTR_ACTIONS": nl_action_attr, } if get_ovs_dpif_op_str(cmd) == "DPIF_OP_EXECUTE": result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump, header="> Decode OVS_PACKET_ATTR_* TLVs:", attr_to_str_func=get_ovs_pkt_attr_str, decode_tree=nl_attr_tree_exec) else: result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump, header="> Decode OVS_FLOW_ATTR_* TLVs:", attr_to_str_func=get_ovs_flow_attr_str, decode_tree=nl_attr_tree_put) return result # # get_ovs_flow_attr_str() # def get_ovs_flow_attr_str(attr): ovs_flow_attr = ["OVS_FLOW_ATTR_UNSPEC", "OVS_FLOW_ATTR_KEY", "OVS_FLOW_ATTR_ACTIONS", "OVS_FLOW_ATTR_STATS", "OVS_FLOW_ATTR_TCP_FLAGS", "OVS_FLOW_ATTR_USED", "OVS_FLOW_ATTR_CLEAR", "OVS_FLOW_ATTR_MASK", "OVS_FLOW_ATTR_PROBE", "OVS_FLOW_ATTR_UFID", "OVS_FLOW_ATTR_UFID_FLAGS", "OVS_FLOW_ATTR_PAD"] if attr < 0 or attr >= len(ovs_flow_attr): return "".format(attr) return ovs_flow_attr[attr] # # get_ovs_pkt_attr_str() # def get_ovs_pkt_attr_str(attr): ovs_pkt_attr = ["OVS_PACKET_ATTR_UNSPEC", "OVS_PACKET_ATTR_PACKET", "OVS_PACKET_ATTR_KEY", "OVS_PACKET_ATTR_ACTIONS", "OVS_PACKET_ATTR_USERDATA", "OVS_PACKET_ATTR_EGRESS_TUN_KEY", "OVS_PACKET_ATTR_UNUSED1", "OVS_PACKET_ATTR_UNUSED2", "OVS_PACKET_ATTR_PROBE", "OVS_PACKET_ATTR_MRU", "OVS_PACKET_ATTR_LEN", "OVS_PACKET_ATTR_HASH"] if attr < 0 or attr >= len(ovs_pkt_attr): return "".format(attr) return ovs_pkt_attr[attr] # # get_ovs_key_attr_str() # def get_ovs_key_attr_str(attr): ovs_key_attr = ["OVS_KEY_ATTR_UNSPEC", "OVS_KEY_ATTR_ENCAP", "OVS_KEY_ATTR_PRIORITY", "OVS_KEY_ATTR_IN_PORT", "OVS_KEY_ATTR_ETHERNET", "OVS_KEY_ATTR_VLAN", "OVS_KEY_ATTR_ETHERTYPE", "OVS_KEY_ATTR_IPV4", "OVS_KEY_ATTR_IPV6", "OVS_KEY_ATTR_TCP", "OVS_KEY_ATTR_UDP", "OVS_KEY_ATTR_ICMP", "OVS_KEY_ATTR_ICMPV6", "OVS_KEY_ATTR_ARP", "OVS_KEY_ATTR_ND", "OVS_KEY_ATTR_SKB_MARK", "OVS_KEY_ATTR_TUNNEL", "OVS_KEY_ATTR_SCTP", "OVS_KEY_ATTR_TCP_FLAGS", "OVS_KEY_ATTR_DP_HASH", "OVS_KEY_ATTR_RECIRC_ID", "OVS_KEY_ATTR_MPLS", "OVS_KEY_ATTR_CT_STATE", "OVS_KEY_ATTR_CT_ZONE", "OVS_KEY_ATTR_CT_MARK", "OVS_KEY_ATTR_CT_LABELS", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "OVS_KEY_ATTR_NSH"] if attr < 0 or attr >= len(ovs_key_attr): return "".format(attr) return ovs_key_attr[attr] # # get_ovs_action_attr_str() # def get_ovs_action_attr_str(attr): ovs_action_attr = ["OVS_ACTION_ATTR_UNSPEC", "OVS_ACTION_ATTR_OUTPUT", "OVS_ACTION_ATTR_USERSPACE", "OVS_ACTION_ATTR_SET", "OVS_ACTION_ATTR_PUSH_VLAN", "OVS_ACTION_ATTR_POP_VLAN", "OVS_ACTION_ATTR_SAMPLE", "OVS_ACTION_ATTR_RECIRC", "OVS_ACTION_ATTR_HASH", "OVS_ACTION_ATTR_PUSH_MPLS", "OVS_ACTION_ATTR_POP_MPLS", "OVS_ACTION_ATTR_SET_MASKED", "OVS_ACTION_ATTR_CT", "OVS_ACTION_ATTR_TRUNC", "OVS_ACTION_ATTR_PUSH_ETH", "OVS_ACTION_ATTR_POP_ETH", "OVS_ACTION_ATTR_CT_CLEAR", "OVS_ACTION_ATTR_PUSH_NSH", "OVS_ACTION_ATTR_POP_NSH", "OVS_ACTION_ATTR_METER", "OVS_ACTION_ATTR_CLONE", "OVS_ACTION_ATTR_CHECK_PKT_LEN", "OVS_ACTION_ATTR_ADD_MPLS", "OVS_ACTION_ATTR_DEC_TTL", "OVS_ACTION_ATTR_DROP", "OVS_ACTION_ATTR_PSAMPLE", "OVS_ACTION_ATTR_TUNNEL_PUSH", "OVS_ACTION_ATTR_TUNNEL_POP", "OVS_ACTION_ATTR_LB_OUTPUT"] if attr < 0 or attr >= len(ovs_action_attr): return "".format(attr) return ovs_action_attr[attr] # # get_ovs_tunnel_key_attr_str() # def get_ovs_tunnel_key_attr_str(attr): ovs_tunnel_key_attr = ["OVS_TUNNEL_KEY_ATTR_ID", "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "OVS_TUNNEL_KEY_ATTR_TOS", "OVS_TUNNEL_KEY_ATTR_TTL", "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "OVS_TUNNEL_KEY_ATTR_CSUM", "OVS_TUNNEL_KEY_ATTR_OAM", "OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "OVS_TUNNEL_KEY_ATTR_TP_SRC", "OVS_TUNNEL_KEY_ATTR_TP_DST", "OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "OVS_TUNNEL_KEY_ATTR_IPV6_DST", "OVS_TUNNEL_KEY_ATTR_PAD", "OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "OVS_TUNNEL_KEY_ATTR_GTPU_OPTS"] if attr < 0 or attr >= len(ovs_tunnel_key_attr): return "".format(attr) return ovs_tunnel_key_attr[attr] # # get_ovs_dpif_op_str() # def get_ovs_dpif_op_str(op): ovs_dpif_ops = ["DPIF_OP_UNSPEC", "DPIF_OP_FLOW_PUT", "DPIF_OP_FLOW_DEL", "DPIF_OP_EXECUTE", "DPIF_OP_FLOW_GET"] if op < 0 or op >= len(ovs_dpif_ops): return "".format(op) return ovs_dpif_ops[op] # # buffer_size_type() # def buffer_size_type(astr, min=64, max=2048): value = int(astr) if min <= value <= max: return value else: raise argparse.ArgumentTypeError( "value not in range {}-{}".format(min, max)) # # next_power_of_two() # def next_power_of_two(val): np = 1 while np < val: np *= 2 return np # # main() # def main(): # # Don't like these globals, but ctx passing does not seem to work with the # existing open_ring_buffer() API :( # global b global options # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument("--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=int, default=1024, metavar="NUMBER") parser.add_argument("-D", "--debug", help="Enable eBPF debugging", type=int, const=0x3f, default=0, nargs="?") parser.add_argument("-d", "--packet-decode", help="Display packet content in selected mode, " "default none", choices=["none", "hex", "decode"], default="none") parser.add_argument("-n", "--nlmsg-decode", help="Display netlink message content in selected mode" ", default nlraw", choices=["none", "hex", "nlraw"], default="nlraw") parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitch's PID", type=int, default=None) parser.add_argument("-s", "--nlmsg-size", help="Set maximum netlink message size to capture, " "default 512", type=buffer_size_type, default=512, metavar="[64-2048]") parser.add_argument("--trace-del-op", help="Monitor DPIF_OP_FLOW_DEL messages", action="store_true") parser.add_argument("--trace-exec-op", help="Monitor DPIF_OP_EXECUTE messages", action="store_true") parser.add_argument("--trace-get-op", help="Monitor DPIF_OP_FLOW_GET messages", action="store_true") parser.add_argument("--trace-put-op", help="Monitor DPIF_OP_FLOW_PUT messages", action="store_true") parser.add_argument("-w", "--pcap", metavar="PCAP_FILE", help="Write execute packets to specified pcap file", type=str, default=None) options = parser.parse_args() # # Verify arguments. # if (not options.trace_del_op and not options.trace_exec_op and not options.trace_get_op and not options.trace_put_op): print("ERROR: At least on of the '--trace-*-op' arguments should be " "specified!") sys.exit(-1) # # Find the PID of the ovs-vswitchd daemon if not specified. # if options.pid is None: for proc in psutil.process_iter(): if "ovs-vswitchd" in proc.name(): if options.pid is not None: print("ERROR: Multiple ovs-vswitchd daemons running, " "use the -p option!") sys.exit(-1) options.pid = proc.pid # # Error checking on input parameters # if options.pid is None: print("ERROR: Failed to find ovs-vswitchd's PID!") sys.exit(-1) if options.pcap is not None: if exists(options.pcap): print("ERROR: Destination capture file \"{}\" already exists!". format(options.pcap)) sys.exit(-1) options.buffer_page_count = next_power_of_two(options.buffer_page_count) # # Attach the usdt probe # u = USDT(pid=int(options.pid)) try: if options.trace_exec_op: u.enable_probe(probe="dpif_netlink_operate__:op_flow_execute", fn_name="trace__op_execute") if options.trace_put_op: u.enable_probe(probe="dpif_netlink_operate__:op_flow_put", fn_name="trace__op_flow_put") if options.trace_del_op: u.enable_probe(probe="dpif_netlink_operate__:op_flow_del", fn_name="trace__op_flow_del") if options.trace_get_op: u.enable_probe(probe="dpif_netlink_operate__:op_flow_get", fn_name="trace__op_flow_get") except USDTException as e: print("ERROR: {}".format( (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip(). replace("--with-dtrace or --enable-dtrace", "--enable-usdt-probes"))) sys.exit(-1) # # Uncomment to see how arguments are decoded. # print(u.get_text()) # # # Attach probe to running process # source = ebpf_source.replace("", str(options.nlmsg_size)) source = source.replace("", str(options.buffer_page_count)) source = source.replace("", "1" if options.trace_del_op else "0") source = source.replace("", "1" if options.trace_exec_op else "0") source = source.replace("", "1" if options.trace_get_op else "0") source = source.replace("", "1" if options.trace_put_op else "0") b = BPF(text=source, usdt_contexts=[u], debug=options.debug) # # Print header # print("Display DPIF operations being queued for transmission onto the " "netlink socket.") print("{:<18} {:<4} {:<16} {:<10} {:<10} {}".format( "TIME", "CPU", "COMM", "PID", "NL_SIZE", "DPIF_OPERATION")) # # Dump out all events # b["events"].open_ring_buffer(print_event) while 1: try: b.ring_buffer_poll() except KeyboardInterrupt: break dropcnt = b.get_table("dropcnt") for k in dropcnt.keys(): count = dropcnt.sum(k).value if k.value == 0 and count > 0: print("\nWARNING: Not all upcalls were captured, {} were dropped!" "\n Increase the BPF ring buffer size with the " "--buffer-page-count option.".format(count)) # # Start main() as the default entry point... # if __name__ == "__main__": main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/flow_reval_monitor.py000077500000000000000000000705461514270232600277370ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022-2024 Redhat, Inc. # # 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. # # Script information: # ------------------- # flow_reval_monitor.py uses the dpif_netlink_operate:flow_put and # revalidator:flow_result USDT probes to monitor flow lifetimes and # expiration events. By default, this will show all flow_put and flow # expiration events, along with their reasons. This will look like so: # # TID TIME UFID EVENT/REASON # 71828 1549.119959874 39f0f28f-33... Insert (put) flow to ovs kernel module. # 71828 1549.420877223 850db41c-47... Insert (put) flow to ovs kernel module. # 71828 1550.476923456 5bacfca9-fe... Insert (put) flow to ovs kernel module. # 71832 1559.650192299 850db41c-47... Idle flow timed out # 71832 1561.153332825 39f0f28f-33... Idle flow timed out # 71832 1572.684316304 5bacfca9-fe... Idle flow timed out # # Flow key data can be printed using the --flow-keys option. This will # print the equivalent datapath flow string. # # When filtering flows, the syntax is the same as used by # `ovs-appctl dpctl/add-flow`. # # For a complete list of options, please use the '--help' or '-h' argument. # # Examples: # # To use the script on a running ovs-vswitchd to see flow keys and expiration # events for flows with an ipv4 source of 192.168.10.10: # $ ./flow_reval_monitor.py --flow-keys --filter-flows \ # "ipv4(src=192.168.10.10)" # TIME UFID EVENT/REASON # 105082.457322742 ufid:f76fc899-376d-466b-bc74-0000b933eb97 flow_put # ufid:f76fc899-376d-466b-bc74-0000b933eb97 has the following flow information: # in_port(2), # eth(src=0e:04:47:fc:74:51, dst=da:dc:c5:69:05:d7), \ # eth_type(0x800), \ # ipv4(src=192.168.10.10, dst=192.168.10.30, proto=1, tos=0, ttl=64,[...]), # icmp(type=8, code=0) # 105092.635450202 ufid:f76fc899-376d-466b-bc74-0000b933eb97 Flow timed out # # Notes: # 1) No options are needed to attach when there is a single running instance # of ovs-vswitchd. # 2) If you're using the flow filtering option, it will only track flows that # have been upcalled since the script began running. # 3) When using the flow filtering option, the key size will likely need to # be expanded to match on all the fields in the message. The default is # kept small to keep the buffer copy sizes down when displaying # flows (-k), but is hardcoded to 2048 when an actual filter (-l) is # applied # 4) The flow filtering format is a simplified form of the ODP syntax, and # does not support masked matches, which means you will need to filter # on exact details. The fields present are dependent on how the # classifier and OFP rules form the ODP rules - not all fields may be # present in a particular flow. # 5) The flow_put filtering only happens for flows installed into the ovs # kernel module. This means flows taking the HW offload path (ie: tc), # or on DPDK side won't get matched. try: from bcc import BPF from bcc import USDT from bcc import USDTException except ModuleNotFoundError: print("ERROR: Can't find the BPF Compiler Collection Tools.") print("Please install them before running this script.") exit(1) from enum import IntEnum from ipaddress import IPv4Address, IPv6Address from pathlib import Path import argparse import psutil import re import struct import subprocess import sys # # eBPF source code # bpf_src = """ #include #define MAX_KEY #define FLOW_FILTER enum probe { }; struct event_t { u64 ts; u32 pid; u32 result; u32 reason; u32 ufid[4]; u64 key_size; unsigned char key[MAX_KEY]; enum probe probe; }; BPF_HASH(watchlist, ovs_u128); BPF_RINGBUF_OUTPUT(events, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); /* Hack to make a 'static' like storage object. */ BPF_TABLE("percpu_array", uint32_t, struct udpif_key, udpk, 1); static struct event_t *get_event(enum probe p) { struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { dropcnt.increment(0); return NULL; } event->probe = p; event->ts = bpf_ktime_get_ns(); event->pid = bpf_get_current_pid_tgid(); return event; } static int emit_flow_result(struct udpif_key *ukey, ovs_u128 ufid, u32 result, u32 reason) { struct event_t *event = NULL; u64 *ufid_present = NULL; ufid_present = watchlist.lookup(&ufid); if (FLOW_FILTER && !ufid_present) { return 0; } event = get_event(FLOW_RESULT); if (!event) { /* If we can't reserve the space in the ring buffer, return 1. */ return 1; } event->result = result; event->reason = reason; bpf_probe_read(&event->ufid, sizeof ufid, &ufid); events.ringbuf_submit(event, 0); return 0; } int usdt__flow_result(struct pt_regs *ctx) { struct udpif_key *ukey = NULL; u32 reason = 0; u32 result = 0; ovs_u128 ufid; u32 zero = 0; ukey = udpk.lookup(&zero); if (!ukey) { return 1; } bpf_usdt_readarg_p(2, ctx, ukey, sizeof(struct udpif_key)); bpf_usdt_readarg(3, ctx, &result); bpf_usdt_readarg(4, ctx, &reason); ufid = ukey->ufid; return emit_flow_result(ukey, ufid, result, reason); } int usdt__flow_sweep_result(struct pt_regs *ctx) { struct udpif_key *ukey = NULL; u32 reason = 0; u32 result = 0; ovs_u128 ufid; u32 zero = 0; ukey = udpk.lookup(&zero); if (!ukey) { return 1; } bpf_usdt_readarg_p(2, ctx, ukey, sizeof(struct udpif_key)); bpf_usdt_readarg(3, ctx, &result); bpf_usdt_readarg(4, ctx, &reason); ufid = ukey->ufid; return emit_flow_result(ukey, ufid, result, reason); } int usdt__op_flow_put(struct pt_regs *ctx) { struct dpif_flow_put put; ovs_u128 ufid; struct event_t *event = get_event(OP_FLOW_PUT); if (!event) { /* If we can't reserve the space in the ring buffer, return 1. */ return 1; } bpf_usdt_readarg_p(2, ctx, &put, sizeof put); bpf_probe_read(&event->ufid, sizeof event->ufid, put.ufid); bpf_probe_read(&ufid, sizeof ufid, &event->ufid); if (put.key_len > MAX_KEY) { put.key_len = MAX_KEY; } event->key_size = put.key_len; bpf_probe_read(&event->key, put.key_len, put.key); event->reason = 0; events.ringbuf_submit(event, 0); watchlist.increment(ufid); return 0; } """ Event = IntEnum("Event", ["OP_FLOW_PUT", "FLOW_RESULT"], start=0) RevalResult = IntEnum( "reval_result", [ "UKEY_KEEP", "UKEY_DELETE", "UKEY_MODIFY", ], start=0, ) # # The below FdrReasons and FdrReasonStrings definitions can be found in the # ofproto/ofproto-dpif-upcall.c file. Please keep them in sync. # FdrReasons = IntEnum( "flow_del_reason", [ "FDR_NONE", "FDR_AVOID_CACHING", "FDR_BAD_ODP_FIT", "FDR_FLOW_IDLE", "FDR_FLOW_LIMIT", "FDR_FLOW_WILDCARDED", "FDR_NO_OFPROTO", "FDR_PURGE", "FDR_TOO_EXPENSIVE", "FDR_UPDATE_FAIL", "FDR_XLATION_ERROR", "FDR_FLOW_MISSING_DP" ], start=0, ) FdrReasonStrings = { FdrReasons.FDR_NONE: "No delete reason specified", FdrReasons.FDR_AVOID_CACHING: "Cache avoidance flag set", FdrReasons.FDR_BAD_ODP_FIT: "Bad ODP flow fit", FdrReasons.FDR_FLOW_IDLE: "Flow idle timeout", FdrReasons.FDR_FLOW_LIMIT: "Kill all flows condition reached", FdrReasons.FDR_FLOW_WILDCARDED: "Flow needs a narrower wildcard mask", FdrReasons.FDR_NO_OFPROTO: "Bridge not found", FdrReasons.FDR_PURGE: "User requested flow deletion", FdrReasons.FDR_TOO_EXPENSIVE: "Too expensive to revalidate", FdrReasons.FDR_UPDATE_FAIL: "Datapath update failed", FdrReasons.FDR_XLATION_ERROR: "Flow translation error", FdrReasons.FDR_FLOW_MISSING_DP: "Flow is missing from the datapath" } def err(msg, code=-1): """Prints an error to stderr and exits""" print(msg, file=sys.stderr) sys.exit(code) def run_program(command): """Invokes a new process and returns stdout. Note that this will honor the PATH environment variable, so best to use it sparingly, or with a full path to binary.""" try: process = subprocess.run( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding="utf8", check=True, ) except subprocess.CalledProcessError as perror: return perror.returncode, perror.stdout return 0, process.stdout def get_ovs_definitions(objects, pahole="pahole", pid=None): """Uses `pahole` or similar utility to pull object definitions from a running OVS process. The objects argument can either be a string or can be a list of strings. Optionally, pass a specific `pahole` binary to use rather than the default. PID needs to be set.""" if pid is None: raise ValueError("A valid pid value should be supplied!") if not isinstance(objects, list): objects = [objects] if len(objects) == 0: raise ValueError("Must supply at least one object!") vswitchd = Path(f"/proc/{pid}/exe").resolve() object_str = ",".join(objects) def run_pahole(debug_file): """Helper designed for running pahole, or something with compatible output""" error, result = run_program( [pahole, "-C", object_str, "--compile", debug_file] ) if error: if f"pahole: {debug_file}: Invalid argument" not in result: err( "ERROR: Pahole failed to get ovs-vswitchd data " "structures!\n{}".format( re.sub( "^", " " * 7, result.rstrip(), flags=re.MULTILINE ) ) ) return None if bool(re.search("pahole: type .* not found", result)): return None return result def run_readelf(bin_file): """Helper designed for running readelf or something with compatible output""" error, result = run_program( ["readelf", "-n", "--debug-dump=links", bin_file] ) if error: err( "ERROR: Failed 'readelf' on \"{}\"!\n{}".format( bin_file, re.sub("^", " " * 7, result, flags=re.MULTILINE) ) ) return result def get_debug_file(bin_file): """Runs readelf against the binary, and attempts to find the associated debuginfo file.""" elf_result = run_readelf(bin_file) match = re.search("Build ID: ([0-9a-fA-F]+)", elf_result) if not match: err("ERROR: Can't find build ID to read debug symbols!") dbg_file = "/usr/lib/debug/.build-id/{}/{}.debug".format( match.group(1)[:2], match.group(1)[2:] ) return dbg_file def get_from_shared_library(debug_file): ovs_libs = [ "libofproto", "libopenvswitch", "libovsdb", "libsflow", "libvtep", ] error, ldd_result = run_program(["ldd", debug_file]) if error: err( "ERROR: Failed 'ldd' on \"{}\"!\n{}".format( debug_file, re.sub("^", " " * 7, ldd_result, flags=re.MULTILINE), ) ) for lib in ovs_libs: match = re.search( r"^\s*{}.* => (.*) \(.*\)$".format(lib), ldd_result, flags=re.MULTILINE, ) if match is None: continue result = run_pahole(match.group(1)) if result is None: result = run_pahole(get_debug_file(match.group(1))) if result: return result return None # # First try to find the debug data as part of the executable. # result = run_pahole(vswitchd) if result is None: print(f'INFO: Failed to find debug info in "{vswitchd}"!') # # Get additional .debug information if available. # dbg_file = get_debug_file(vswitchd) result = run_pahole(dbg_file) if result is None: print(f'INFO: Failed to find debug info in "{dbg_file}"!') # # Try to get information from shared libraries if used. # result = get_from_shared_library(vswitchd) if result is None: err(f"ERROR: Failed to find needed data structures through {pahole}") # # We need an empty _Atomic definition to avoid compiler complaints. # result = "#define _Atomic\n" + result # # Remove the uint64_t definition as it conflicts with the kernel one. # result = re.sub("^typedef.*uint64_t;$", "", result, flags=re.MULTILINE) return result def buffer_size_type(astr, min=64, max=2048): """Checks whether a string passed in is a number between min and max.""" value = int(astr) if min <= value <= max: return value else: raise argparse.ArgumentTypeError( "value not in range {}-{}".format(min, max) ) def format_ufid(ufid): """Formats a UFID object into a human readable form. If ufid is None, prints "ufid:none" instead.""" if ufid is None: return "ufid:none" return "{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format( ufid[0], ufid[1] >> 16, ufid[1] & 0xFFFF, ufid[2] >> 16, ufid[2] & 0, ufid[3], ) def find_and_delete_from_watchlist(event): """If the event ufid is in the watchlist, delete it""" for k, _ in b["watchlist"].items(): key_ufid = struct.unpack("=IIII", k) if key_ufid == tuple(event.ufid): key = (b["watchlist"].Key * 1)(k) b["watchlist"].items_delete_batch(key) break def handle_flow_put(event): """Event handler for the `flow_put` action. This function will try to populate the watchlist based on the vswitchd emitting a put event to push an ODP flow key with associated actions into the kernel module""" if args.flow_keys or args.filter_flows is not None: key = decode_key(bytes(event.key)[: event.key_size]) flow_dict, flow_str = parse_flow_dict(key) # For each attribute that we're watching. if args.filter_flows is not None: if not compare_flow_to_target(args.filter_flows, flow_dict): find_and_delete_from_watchlist(event) return print( "{:<10} {:<18.9f} {:<36} {}".format( event.pid, event.ts / 1000000000, format_ufid(event.ufid), "Insert (put) flow to ovs kernel module.", ) ) if args.flow_keys and len(flow_str): flow_str_fields = flow_str.split("), ") flow_str = " " curlen = 4 for field in flow_str_fields: if curlen + len(field) > 79: flow_str += "\n " curlen = 4 if field[-1] != ")": field += ")" flow_str += field + ", " curlen += len(field) + 2 print(" - It holds the following key information:") print(flow_str) def compare_flow_to_target(target, flow): """Routine to compare two flow keys""" for key in target: if key not in flow: return False elif target[key] is True: continue elif target[key] == flow[key]: continue elif isinstance(target[key], dict) and isinstance(flow[key], dict): return compare_flow_to_target(target[key], flow[key]) else: return False return True # # parse_flow_str() # def parse_flow_str(flow_str): """Loosely parses an ODP flow key into a dict for further processing""" f_list = [i.strip(", ") for i in flow_str.split(")")] if f_list[-1] == "": f_list = f_list[:-1] flow_dict = {} for e in f_list: split_list = e.split("(") k = split_list[0] if len(split_list) == 1: flow_dict[k] = True elif split_list[1].count("=") == 0: flow_dict[k] = split_list[1] else: sub_dict = {} sublist = [i.strip() for i in split_list[1].split(",")] for subkey in sublist: brk = subkey.find("=") sub_dict[subkey[:brk]] = subkey[brk + 1 :] flow_dict[k] = sub_dict return flow_dict def print_expiration(event): """Prints a UFID eviction with a reason.""" ufid_str = format_ufid(event.ufid) try: reason = FdrReasonStrings[event.reason] except KeyError: reason = f"Unknown reason '{event.reason}'" print( "{:<10} {:<18.9f} {:<36} {:<17}".format( event.pid, event.ts / 1000000000, ufid_str, reason, ) ) def decode_key(msg): """Decodes netlink OVS key attribute.""" bytes_left = len(msg) result = {} while bytes_left: if bytes_left < 4: break nla_len, nla_type = struct.unpack("=HH", msg[:4]) if nla_len < 4: break nla_data = msg[4:nla_len] if nla_len > bytes_left: nla_data = nla_data[: (bytes_left - 4)] break else: result[get_ovs_key_attr_str(nla_type)] = nla_data next_offset = (nla_len + 3) & (~3) msg = msg[next_offset:] bytes_left -= next_offset if bytes_left: print(f"INFO: Buffer truncated with {bytes_left} bytes left.") return result # # get_ovs_key_attr_str() # def get_ovs_key_attr_str(attr): ovs_key_attr = [ "OVS_KEY_ATTR_UNSPEC", "encap", "skb_priority", "in_port", "eth", "vlan", "eth_type", "ipv4", "ipv6", "tcp", "udp", "icmp", "icmpv6", "arp", "nd", "skb_mark", "tunnel", "sctp", "tcp_flags", "dp_hash", "recirc_id", "mpls", "ct_state", "ct_zone", "ct_mark", "ct_label", "ct_tuple4", "ct_tuple6", "nsh", ] if attr < 0 or attr > len(ovs_key_attr): return ": {}".format(attr) return ovs_key_attr[attr] def parse_flow_dict(key_dict, decode=True): """Processes a flow key dict (see `parse_flow_str` or `decode_key`) and returns a tuple of both the final flow key dict, and a string that represents and ODP-like representation. Attempts to decode the actual data values if `decode` is true. Otherwise, this can be for a loose form of validation. Throws a KeyError when it encounters an unknown flow key.""" ret_str = "" parseable = {} skip = ["nsh", "tunnel", "mpls", "vlan"] need_byte_swap = ["ct_label"] ipv4addrs = ["ct_tuple4", "tunnel", "ipv4", "arp"] ipv6addrs = ["ipv6", "nd", "ct_tuple6"] macs = {"eth": [0, 1], "arp": [3, 4], "nd": [1, 2]} fields = [ ("OVS_KEY_ATTR_UNSPEC"), ("encap",), ("skb_priority", " 1: data = list( struct.unpack( fields[attr][1], v[: struct.calcsize(fields[attr][1])] ) ) if k in ipv4addrs: if data[0].count(0) < 4: data[0] = str(IPv4Address(data[0])) else: data[0] = b"\x00" if data[1].count(0) < 4: data[1] = str(IPv4Address(data[1])) else: data[1] = b"\x00" if k in ipv6addrs: if data[0].count(0) < 16: data[0] = str(IPv6Address(data[0])) else: data[0] = b"\x00" if data[1].count(0) < len(data[1]): data[1] = str(IPv6Address(data[1])) else: data[1] = b"\x00" if k in macs.keys(): for e in macs[k]: if data[e].count(0) == 6: mac_str = b"\x00" else: mac_str = ":".join(["%02x" % i for i in data[e]]) data[e] = mac_str if decode and len(fields[attr]) > 2: field_dict = dict(zip(fields[attr][2:], data)) s = ", ".join(k + "=" + str(v) for k, v in field_dict.items()) elif decode and k != "eth_type": s = str(data[0]) field_dict = s else: if decode: s = hex(data[0]) field_dict = s ret_str += k + "(" + s + "), " parseable[k] = field_dict ret_str = ret_str[:-2] return (parseable, ret_str) def handle_event(ctx, data, size): """Dispatches to the correct event handler based on the event probe type. Once we grab the event, we have three cases. 1. It's a revalidator probe and the reason is nonzero: A flow is expiring 2. It's a revalidator probe and the reason is zero: flow revalidated 3. It's a flow_put probe. We will ignore case 2, and report all others. """ event = b["events"].event(data) if event.probe == Event.OP_FLOW_PUT: handle_flow_put(event) elif ( event.probe == Event.FLOW_RESULT and event.result == RevalResult.UKEY_DELETE ): print_expiration(event) def main(): # # Don't like these globals, but ctx passing does not work with the existing # open_ring_buffer() API :( # global b global args # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument( "--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=int, default=1024, metavar="NUMBER", ) parser.add_argument( "-f", "--flow-key-size", help="Set maximum flow key size to capture, " "default 128 - see notes", type=buffer_size_type, default=128, metavar="[128-2048]", ) parser.add_argument( "-k", "--flow-keys", help="Print flow keys as flow strings", action="store_true", ) parser.add_argument( "-l", "--filter-flows", metavar="FLOW_STRING", help="Filter flows that match the specified " "ODP-like flow", type=str, default=None, nargs="*", ) parser.add_argument( "-P", "--pahole", metavar="PAHOLE", help="Pahole executable to use, default pahole", type=str, default="pahole", ) parser.add_argument( "-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitchd's PID", type=int, default=None, ) parser.add_argument( "-D", "--debug", help="Enable eBPF debugging", type=int, const=0x3F, default=0, nargs="?", ) args = parser.parse_args() # # Find the PID of the ovs-vswitchd daemon if not specified. # if args.pid is None: for proc in psutil.process_iter(): if "ovs-vswitchd" in proc.name(): if args.pid is not None: err( "Error: Multiple ovs-vswitchd daemons running, " "use the -p option!" ) args.pid = proc.pid # # Error checking on input parameters # if args.pid is None: err("ERROR: Failed to find ovs-vswitchd's PID!") # # Attach the USDT probes # try: u = USDT(pid=int(args.pid)) u.enable_probe(probe="op_flow_put", fn_name="usdt__op_flow_put") u.enable_probe(probe="flow_result", fn_name="usdt__flow_result") u.enable_probe( probe="flow_sweep_result", fn_name="usdt__flow_sweep_result" ) except USDTException as e: err("Failed to attach probes due to:\n" + str(e)) # # Attach the probes to the running process # source = bpf_src.replace( "", str(args.buffer_page_count) ) source = source.replace( "", get_ovs_definitions( ["udpif_key", "ovs_u128", "dpif_flow_put"], pid=args.pid, pahole=args.pahole, ), ) if args.filter_flows is None: filter_bool = 0 # Set the key size based on what the user wanted source = source.replace("", str(args.flow_key_size)) else: filter_bool = 1 args.filter_flows = parse_flow_str(args.filter_flows[0]) # Run through the parser to make sure we only filter on fields we # understand parse_flow_dict(args.filter_flows, False) # This is hardcoded here because it doesn't make sense to shrink the # size, since the flow key might be missing fields that are matched in # the flow filter. source = source.replace("", "2048") source = source.replace("", str(filter_bool)) source = source.replace( "", "\n".join([f"{event.name} = {event.value}," for event in Event]), ) b = BPF(text=source, usdt_contexts=[u], debug=args.debug) # # Print header # print( "{:<10} {:<18} {:<36} {:<17}".format( "TID", "TIME", "UFID", "EVENT/REASON" ) ) # # Dump out all events. # b["events"].open_ring_buffer(handle_event) while 1: try: b.ring_buffer_poll() except KeyboardInterrupt: break dropcnt = b.get_table("dropcnt") for k in dropcnt.keys(): count = dropcnt.sum(k).value if k.value == 0 and count > 0: print( "\n# WARNING: Not all flow operations were captured, {} were" " dropped!\n# Increase the BPF ring buffer size " "with the --buffer-page-count option.".format(count) ) # # Start main() as the default entry point # if __name__ == "__main__": main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/kernel_delay.py000077500000000000000000001436411514270232600264630ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022,2023 Red Hat, Inc. # # 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. # # # Script information: # ------------------- # This script allows a developer to quickly identify if the issue at hand # might be related to the kernel running out of resources or if it really is # an Open vSwitch issue. # # For documentation see the kernel_delay.rst file. # # # Dependencies: # ------------- # You need to install the BCC package for your specific platform or build it # yourself using the following instructions: # https://raw.githubusercontent.com/iovisor/bcc/master/INSTALL.md # # Python needs the following additional packages installed: # - pytz # - psutil # # You can either install your distribution specific package or use pip: # pip install pytz psutil # import argparse import datetime import errno import os import pytz import psutil import re import sys import time import ctypes as ct try: from bcc import BPF, USDT, USDTException from bcc.syscall import syscalls, syscall_name except ModuleNotFoundError: print("ERROR: Can't find the BPF Compiler Collection (BCC) tools!") sys.exit(os.EX_OSFILE) from enum import IntEnum # # Actual eBPF source code # EBPF_SOURCE = """ #include #include #define MONITOR_PID enum { }; struct event_t { u64 ts; u32 tid; u32 id; int user_stack_id; int kernel_stack_id; u32 syscall; u64 entry_ts; }; BPF_RINGBUF_OUTPUT(events, ); BPF_STACK_TRACE(stack_traces, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); BPF_TABLE("percpu_array", uint32_t, uint64_t, trigger_miss, 1); BPF_ARRAY(capture_on, u64, 1); static inline bool capture_enabled(u64 pid_tgid) { int key = 0; u64 *ret; if ((pid_tgid >> 32) != MONITOR_PID) return false; ret = capture_on.lookup(&key); return ret && *ret == 1; } static inline bool capture_enabled__() { int key = 0; u64 *ret; ret = capture_on.lookup(&key); return ret && *ret == 1; } static struct event_t *get_event(uint32_t id) { struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { dropcnt.increment(0); return NULL; } event->id = id; event->ts = bpf_ktime_get_ns(); event->tid = bpf_get_current_pid_tgid(); return event; } static int start_trigger() { int key = 0; u64 *val = capture_on.lookup(&key); /* If the value is -1 we can't start as we are still processing the * results in userspace. */ if (!val || *val != 0) { trigger_miss.increment(0); return 0; } struct event_t *event = get_event(EVENT_START_TRIGGER); if (event) { events.ringbuf_submit(event, 0); *val = 1; } else { trigger_miss.increment(0); } return 0; } static int stop_trigger() { int key = 0; u64 *val = capture_on.lookup(&key); if (!val || *val != 1) return 0; struct event_t *event = get_event(EVENT_STOP_TRIGGER); if (event) events.ringbuf_submit(event, 0); if (val) *val = -1; return 0; } /* * For the syscall monitor the following probes get installed. */ struct syscall_data_t { u64 count; u64 total_ns; u64 worst_ns; }; struct syscall_data_key_t { u32 pid; u32 tid; u32 syscall; }; struct long_poll_data_key_t { u32 pid; u32 tid; }; struct long_poll_data_t { u64 count; u64 total_ns; u64 min_ns; u64 max_ns; }; BPF_HASH(syscall_start, u64, u64); BPF_HASH(syscall_data, struct syscall_data_key_t, struct syscall_data_t); BPF_HASH(long_poll_start, u64, u64); BPF_HASH(long_poll_data, struct long_poll_data_key_t, struct long_poll_data_t); TRACEPOINT_PROBE(raw_syscalls, sys_enter) { u64 pid_tgid = bpf_get_current_pid_tgid(); struct long_poll_data_t *val, init_val = {.min_ns = U64_MAX}; struct long_poll_data_key_t key; if (!capture_enabled(pid_tgid)) return 0; u64 t = bpf_ktime_get_ns(); syscall_start.update(&pid_tgid, &t); /* Do long poll handling from here on. */ if (args->id != ) return 0; u64 *start_ns = long_poll_start.lookup(&pid_tgid); if (!start_ns || *start_ns == 0) return 0; key.pid = pid_tgid >> 32; key.tid = (u32)pid_tgid; val = long_poll_data.lookup_or_try_init(&key, &init_val); if (val) { u64 delta = t - *start_ns; val->count++; val->total_ns += delta; if (delta > val->max_ns) val->max_ns = delta; if (delta < val->min_ns) val->min_ns = delta; } return 0; } TRACEPOINT_PROBE(raw_syscalls, sys_exit) { struct syscall_data_t *val, zero = {}; struct syscall_data_key_t key; u64 pid_tgid = bpf_get_current_pid_tgid(); if (!capture_enabled(pid_tgid)) return 0; u64 t = bpf_ktime_get_ns(); if (args->id == ) { long_poll_start.update(&pid_tgid, &t); } key.pid = pid_tgid >> 32; key.tid = (u32)pid_tgid; key.syscall = args->id; u64 *start_ns = syscall_start.lookup(&pid_tgid); if (!start_ns) return 0; val = syscall_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = t - *start_ns; val->count++; val->total_ns += delta; if (delta > val->worst_ns) val->worst_ns = delta; if () { struct event_t *event = get_event(EVENT_SYSCALL); if (event) { event->syscall = args->id; event->entry_ts = *start_ns; if () { event->user_stack_id = stack_traces.get_stackid( args, BPF_F_USER_STACK); event->kernel_stack_id = stack_traces.get_stackid( args, 0); } events.ringbuf_submit(event, 0); } } } return 0; } /* * For measuring the thread stopped time, we need the following. */ struct stop_time_data_t { u64 count; u64 total_ns; u64 worst_ns; }; struct pid_tid_key_t { u32 pid; u32 tid; }; BPF_HASH(stop_start, u64, u64); BPF_HASH(stop_data, struct pid_tid_key_t, struct stop_time_data_t); static inline void thread_handle_stopped_run(u32 pid, u32 tgid, u64 ktime) { u64 pid_tgid = (u64) tgid << 32 | pid; u64 *start_ns = stop_start.lookup(&pid_tgid); if (!start_ns || *start_ns == 0) return; struct stop_time_data_t *val, zero = {}; struct pid_tid_key_t key = { .pid = tgid, .tid = pid }; val = stop_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = ktime - *start_ns; val->count++; val->total_ns += delta; if (delta > val->worst_ns) val->worst_ns = delta; } *start_ns = 0; } /* * For measuring the thread run time, we need the following. */ struct run_time_data_t { u64 count; u64 total_ns; u64 max_ns; u64 min_ns; }; BPF_HASH(run_start, u64, u64); BPF_HASH(run_data, struct pid_tid_key_t, struct run_time_data_t); static inline void thread_start_run(u64 pid_tgid, u64 ktime) { run_start.update(&pid_tgid, &ktime); } static inline void thread_stop_run(u32 pid, u32 tgid, u64 ktime) { u64 pid_tgid = (u64) tgid << 32 | pid; u64 *start_ns = run_start.lookup(&pid_tgid); if (!start_ns || *start_ns == 0) return; struct run_time_data_t *val, zero = {}; struct pid_tid_key_t key = { .pid = tgid, .tid = pid }; val = run_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = ktime - *start_ns; val->count++; val->total_ns += delta; if (delta > val->max_ns) val->max_ns = delta; if (val->min_ns == 0 || delta < val->min_ns) val->min_ns = delta; } *start_ns = 0; } /* * For measuring the thread-ready delay, we need the following. */ struct ready_data_t { u64 count; u64 total_ns; u64 worst_ns; }; BPF_HASH(ready_start, u64, u64); BPF_HASH(ready_data, struct pid_tid_key_t, struct ready_data_t); static inline int sched_wakeup__(u32 pid, u32 tgid) { u64 pid_tgid = (u64) tgid << 32 | pid; if (!capture_enabled(pid_tgid)) return 0; u64 t = bpf_ktime_get_ns(); ready_start.update(&pid_tgid, &t); thread_handle_stopped_run(pid, tgid, t); return 0; } RAW_TRACEPOINT_PROBE(sched_wakeup) { struct task_struct *t = (struct task_struct *)ctx->args[0]; return sched_wakeup__(t->pid, t->tgid); } RAW_TRACEPOINT_PROBE(sched_wakeup_new) { struct task_struct *t = (struct task_struct *)ctx->args[0]; return sched_wakeup__(t->pid, t->tgid); } RAW_TRACEPOINT_PROBE(sched_switch) { struct task_struct *prev = (struct task_struct *)ctx->args[1]; struct task_struct *next= (struct task_struct *)ctx->args[2]; u64 ktime = 0; if (!capture_enabled__()) return 0; if (prev->tgid == MONITOR_PID) { u64 prev_pid_tgid = (u64)next->tgid << 32 | next->pid; ktime = bpf_ktime_get_ns(); if (prev-> == TASK_RUNNING) ready_start.update(&prev_pid_tgid, &ktime); if (prev-> & __TASK_STOPPED) stop_start.update(&prev_pid_tgid, &ktime); thread_stop_run(prev->pid, prev->tgid, ktime); } if (next->tgid != MONITOR_PID) return 0; if (ktime == 0) ktime = bpf_ktime_get_ns(); u64 pid_tgid = (u64)next->tgid << 32 | next->pid; u64 *start_ns = ready_start.lookup(&pid_tgid); if (start_ns && *start_ns != 0) { struct ready_data_t *val, zero = {}; struct pid_tid_key_t key = { .pid = next->tgid, .tid = next->pid }; val = ready_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = ktime - *start_ns; val->count++; val->total_ns += delta; if (delta > val->worst_ns) val->worst_ns = delta; } *start_ns = 0; } thread_start_run(pid_tgid, ktime); return 0; } /* * For measuring the hard irq time, we need the following. */ struct hardirq_start_data_t { u64 start_ns; char irq_name[32]; }; struct hardirq_data_t { u64 count; u64 total_ns; u64 worst_ns; }; struct hardirq_data_key_t { u32 pid; u32 tid; char irq_name[32]; }; BPF_HASH(hardirq_start, u64, struct hardirq_start_data_t); BPF_HASH(hardirq_data, struct hardirq_data_key_t, struct hardirq_data_t); TRACEPOINT_PROBE(irq, irq_handler_entry) { u64 pid_tgid = bpf_get_current_pid_tgid(); if (!capture_enabled(pid_tgid)) return 0; struct hardirq_start_data_t data = {}; data.start_ns = bpf_ktime_get_ns(); TP_DATA_LOC_READ_STR(&data.irq_name, name, sizeof(data.irq_name)); hardirq_start.update(&pid_tgid, &data); return 0; } TRACEPOINT_PROBE(irq, irq_handler_exit) { u64 pid_tgid = bpf_get_current_pid_tgid(); if (!capture_enabled(pid_tgid)) return 0; struct hardirq_start_data_t *data; data = hardirq_start.lookup(&pid_tgid); if (!data || data->start_ns == 0) return 0; if (args->ret != IRQ_NONE) { struct hardirq_data_t *val, zero = {}; struct hardirq_data_key_t key = { .pid = pid_tgid >> 32, .tid = (u32)pid_tgid }; bpf_probe_read_kernel(&key.irq_name, sizeof(key.irq_name), data->irq_name); val = hardirq_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = bpf_ktime_get_ns() - data->start_ns; val->count++; val->total_ns += delta; if (delta > val->worst_ns) val->worst_ns = delta; } } data->start_ns = 0; return 0; } /* * For measuring the soft irq time, we need the following. */ struct softirq_start_data_t { u64 start_ns; u32 vec_nr; }; struct softirq_data_t { u64 count; u64 total_ns; u64 worst_ns; }; struct softirq_data_key_t { u32 pid; u32 tid; u32 vec_nr; }; BPF_HASH(softirq_start, u64, struct softirq_start_data_t); BPF_HASH(softirq_data, struct softirq_data_key_t, struct softirq_data_t); TRACEPOINT_PROBE(irq, softirq_entry) { u64 pid_tgid = bpf_get_current_pid_tgid(); if (!capture_enabled(pid_tgid)) return 0; struct softirq_start_data_t data = {}; data.start_ns = bpf_ktime_get_ns(); data.vec_nr = args->vec; softirq_start.update(&pid_tgid, &data); return 0; } TRACEPOINT_PROBE(irq, softirq_exit) { u64 pid_tgid = bpf_get_current_pid_tgid(); if (!capture_enabled(pid_tgid)) return 0; struct softirq_start_data_t *data; data = softirq_start.lookup(&pid_tgid); if (!data || data->start_ns == 0) return 0; struct softirq_data_t *val, zero = {}; struct softirq_data_key_t key = { .pid = pid_tgid >> 32, .tid = (u32)pid_tgid, .vec_nr = data->vec_nr}; val = softirq_data.lookup_or_try_init(&key, &zero); if (val) { u64 delta = bpf_ktime_get_ns() - data->start_ns; val->count++; val->total_ns += delta; if (delta > val->worst_ns) val->worst_ns = delta; } data->start_ns = 0; return 0; } /* * For measuring upcall statistics (per CPU). */ BPF_PERCPU_HASH(upcall_count); #if int kretprobe__ovs_dp_upcall(struct pt_regs *ctx) { int ret = PT_REGS_RC(ctx); u64 zero = 0; u64 *entry; u64 key; if (!capture_enabled__()) return 0; if (ret >= 0) key = 0; else key = -ret; entry = upcall_count.lookup_or_try_init(&key, &zero); if (entry) *entry += 1; return 0; } #endif /* For measuring upcall statistics/errors. */ """ # # time_ns() # try: from time import time_ns except ImportError: # For compatibility with Python <= v3.6. def time_ns(): now = datetime.datetime.now() return int(now.timestamp() * 1e9) # # Probe class to use for the start/stop triggers # class Probe(object): ''' The goal for this object is to support as many as possible probe/events as supported by BCC. See https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#events--arguments ''' def __init__(self, probe, pid=None): self.pid = pid self.text_probe = probe self._parse_text_probe() def __str__(self): if self.probe_type == "usdt": return "[{}]; {}:{}:{}".format(self.text_probe, self.probe_type, self.usdt_provider, self.usdt_probe) elif self.probe_type == "trace": return "[{}]; {}:{}:{}".format(self.text_probe, self.probe_type, self.trace_system, self.trace_event) elif self.probe_type == "kprobe" or self.probe_type == "kretprobe": return "[{}]; {}:{}".format(self.text_probe, self.probe_type, self.kprobe_function) elif self.probe_type == "uprobe" or self.probe_type == "uretprobe": return "[{}]; {}:{}".format(self.text_probe, self.probe_type, self.uprobe_function) else: return "[{}] <{}:unknown probe>".format(self.text_probe, self.probe_type) def _raise(self, error): raise ValueError("[{}]; {}".format(self.text_probe, error)) def _verify_kprobe_probe(self): # Nothing to verify for now, just return. return def _verify_trace_probe(self): # Nothing to verify for now, just return. return def _verify_uprobe_probe(self): # Nothing to verify for now, just return. return def _verify_usdt_probe(self): if not self.pid: self._raise("USDT probes need a valid PID.") usdt = USDT(pid=self.pid) for probe in usdt.enumerate_probes(): if probe.provider.decode("utf-8") == self.usdt_provider and \ probe.name.decode("utf-8") == self.usdt_probe: return self._raise("Can't find UDST probe '{}:{}'".format(self.usdt_provider, self.usdt_probe)) def _parse_text_probe(self): ''' The text probe format is defined as follows: : Types: USDT: u|usdt:: TRACE: t|trace:: KPROBE: k|kprobe: KRETPROBE: kr|kretprobe: UPROBE: up|uprobe: URETPROBE: ur|uretprobe: ''' args = self.text_probe.split(":") if len(args) <= 1: self._raise("Can't extract probe type.") if args[0] not in ["k", "kprobe", "kr", "kretprobe", "t", "trace", "u", "usdt", "up", "uprobe", "ur", "uretprobe"]: self._raise("Invalid probe type '{}'".format(args[0])) self.probe_type = "kprobe" if args[0] == "k" else args[0] self.probe_type = "kretprobe" if args[0] == "kr" else self.probe_type self.probe_type = "trace" if args[0] == "t" else self.probe_type self.probe_type = "usdt" if args[0] == "u" else self.probe_type self.probe_type = "uprobe" if args[0] == "up" else self.probe_type self.probe_type = "uretprobe" if args[0] == "ur" else self.probe_type if self.probe_type == "usdt": if len(args) != 3: self._raise("Invalid number of arguments for USDT") self.usdt_provider = args[1] self.usdt_probe = args[2] self._verify_usdt_probe() elif self.probe_type == "trace": if len(args) != 3: self._raise("Invalid number of arguments for TRACE") self.trace_system = args[1] self.trace_event = args[2] self._verify_trace_probe() elif self.probe_type == "kprobe" or self.probe_type == "kretprobe": if len(args) != 2: self._raise("Invalid number of arguments for K(RET)PROBE") self.kprobe_function = args[1] self._verify_kprobe_probe() elif self.probe_type == "uprobe" or self.probe_type == "uretprobe": if len(args) != 2: self._raise("Invalid number of arguments for U(RET)PROBE") self.uprobe_function = args[1] self._verify_uprobe_probe() def _get_kprobe_c_code(self, function_name, function_content): # # The kprobe__* do not require a function name, so it's # ignored in the code generation. # return """ int {}__{}(struct pt_regs *ctx) {{ {} }} """.format(self.probe_type, self.kprobe_function, function_content) def _get_trace_c_code(self, function_name, function_content): # # The TRACEPOINT_PROBE() do not require a function name, so it's # ignored in the code generation. # return """ TRACEPOINT_PROBE({},{}) {{ {} }} """.format(self.trace_system, self.trace_event, function_content) def _get_uprobe_c_code(self, function_name, function_content): return """ int {}(struct pt_regs *ctx) {{ {} }} """.format(function_name, function_content) def _get_usdt_c_code(self, function_name, function_content): return """ int {}(struct pt_regs *ctx) {{ {} }} """.format(function_name, function_content) def get_c_code(self, function_name, function_content): if self.probe_type == "kprobe" or self.probe_type == "kretprobe": return self._get_kprobe_c_code(function_name, function_content) elif self.probe_type == "trace": return self._get_trace_c_code(function_name, function_content) elif self.probe_type == "uprobe" or self.probe_type == "uretprobe": return self._get_uprobe_c_code(function_name, function_content) elif self.probe_type == "usdt": return self._get_usdt_c_code(function_name, function_content) return "" def probe_name(self): if self.probe_type == "kprobe" or self.probe_type == "kretprobe": return "{}".format(self.kprobe_function) elif self.probe_type == "trace": return "{}:{}".format(self.trace_system, self.trace_event) elif self.probe_type == "uprobe" or self.probe_type == "uretprobe": return "{}".format(self.uprobe_function) elif self.probe_type == "usdt": return "{}:{}".format(self.usdt_provider, self.usdt_probe) return "" # # event_to_dict() # def event_to_dict(event): return dict([(field, getattr(event, field)) for (field, _) in event._fields_ if isinstance(getattr(event, field), (int, bytes))]) # # Event enum # Event = IntEnum("Event", ["SYSCALL", "START_TRIGGER", "STOP_TRIGGER"], start=0) # # process_event() # def process_event(ctx, data, size): global start_trigger_ts global stop_trigger_ts event = bpf["events"].event(data) if event.id == Event.SYSCALL: syscall_events.append({"tid": event.tid, "ts_entry": event.entry_ts, "ts_exit": event.ts, "syscall": event.syscall, "user_stack_id": event.user_stack_id, "kernel_stack_id": event.kernel_stack_id}) elif event.id == Event.START_TRIGGER: # # This event would have started the trigger already, so all we need to # do is record the start timestamp. # start_trigger_ts = event.ts elif event.id == Event.STOP_TRIGGER: # # This event would have stopped the trigger already, so all we need to # do is record the start timestamp. stop_trigger_ts = event.ts # # next_power_of_two() # def next_power_of_two(val): np = 1 while np < val: np *= 2 return np # # unsigned_int() # def unsigned_int(value): try: value = int(value) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if value < 0: raise argparse.ArgumentTypeError("must be positive") return value # # unsigned_nonzero_int() # def unsigned_nonzero_int(value): value = unsigned_int(value) if value == 0: raise argparse.ArgumentTypeError("must be nonzero") return value # # get_thread_name() # def get_thread_name(pid, tid): try: with open(f"/proc/{pid}/task/{tid}/comm", encoding="utf8") as f: return f.readline().strip("\n") except FileNotFoundError: pass return f"" # # get_vec_nr_name() # def get_vec_nr_name(vec_nr): known_vec_nr = ["hi", "timer", "net_tx", "net_rx", "block", "irq_poll", "tasklet", "sched", "hrtimer", "rcu"] if vec_nr < 0 or vec_nr > len(known_vec_nr): return f"" return known_vec_nr[vec_nr] # # start/stop/reset capture # def start_capture(): bpf["capture_on"][ct.c_int(0)] = ct.c_int(1) def stop_capture(force=False): if force: bpf["capture_on"][ct.c_int(0)] = ct.c_int(0xffff) else: bpf["capture_on"][ct.c_int(0)] = ct.c_int(0) def capture_running(): return bpf["capture_on"][ct.c_int(0)].value == 1 def reset_capture(): bpf["syscall_start"].clear() bpf["syscall_data"].clear() bpf["run_start"].clear() bpf["run_data"].clear() bpf["ready_start"].clear() bpf["ready_data"].clear() bpf["hardirq_start"].clear() bpf["hardirq_data"].clear() bpf["softirq_start"].clear() bpf["softirq_data"].clear() bpf["stack_traces"].clear() bpf["stop_start"].clear() bpf["stop_data"].clear() bpf["upcall_count"].clear() # # Display timestamp # def print_timestamp(msg): ltz = datetime.datetime.now() utc = ltz.astimezone(pytz.utc) time_string = "{} @{} ({} UTC)".format( msg, ltz.isoformat(), utc.strftime("%H:%M:%S")) print(time_string) # # Get errno short string # def get_errno_short(err): try: return errno.errorcode[err] except KeyError: return "_unknown_" # # Format a eBPF per-cpu hash entry (if the count is > 0) # def format_per_cpu_hash(cpu_hash, key=None, skip_key=None): per_cpu = "" if key is not None: total = cpu_hash.sum(key).value if total > 0: for cpu, value in enumerate(cpu_hash.getvalue(key)): if value == 0: continue per_cpu += " {}: {},".format(cpu, value) else: total = 0 total_cpu = None for key in cpu_hash.keys(): if skip_key is not None and skip_key.value == key.value: continue if total_cpu is None: total_cpu = [0] * len(cpu_hash.getvalue(key)) for cpu, value in enumerate(cpu_hash.getvalue(key)): total_cpu[cpu] += value total += value if total >= 0 and total_cpu: for cpu, value in enumerate(total_cpu): if value == 0: continue per_cpu += " {}: {},".format(cpu, value) return total, per_cpu.strip(", ") # # Display kernel upcall statistics # def display_upcall_results(): upcalls = bpf["upcall_count"] have_upcalls = False for k in upcalls: if upcalls.sum(k).value == 0: continue have_upcalls = True break if not have_upcalls: return print("\n\n# UPCALL STATISTICS (TOTAL [CPU_ID: N_UPCALLS_PER_CPU, ...]):\n" " Total upcalls : {} [{}]".format( *format_per_cpu_hash(upcalls))) for k in sorted(upcalls, key=lambda x: int(x.value)): error = k.value total, per_cpu = format_per_cpu_hash(upcalls, key=k) if error != 0 and total == 0: continue if error == 0: total_failed, per_cpu_failed = format_per_cpu_hash(upcalls, skip_key=k) if total_failed == 0: continue print(" Successfull upcalls : {} [{}]".format(total, per_cpu)) print(" Failed upcalls : {} [{}]".format(total_failed, per_cpu_failed)) else: print(" {:3}, {:13}: {} [{}]".format(error, get_errno_short(error), total, per_cpu)) # # process_results() # def process_results(syscall_events=None, trigger_delta=None): if trigger_delta: print_timestamp("# Triggered sample dump, stop-start delta {:,} ns". format(trigger_delta)) else: print_timestamp("# Sample dump") # # First get a list of all threads we need to report on. # threads_syscall = {k.tid for k, _ in bpf["syscall_data"].items() if k.syscall != 0xffffffff} threads_long_poll = {k.tid for k, _ in bpf["long_poll_data"].items() if k.pid != 0xffffffff} threads_run = {k.tid for k, _ in bpf["run_data"].items() if k.pid != 0xffffffff} threads_ready = {k.tid for k, _ in bpf["ready_data"].items() if k.pid != 0xffffffff} threads_stopped = {k.tid for k, _ in bpf["stop_data"].items() if k.pid != 0xffffffff} threads_hardirq = {k.tid for k, _ in bpf["hardirq_data"].items() if k.pid != 0xffffffff} threads_softirq = {k.tid for k, _ in bpf["softirq_data"].items() if k.pid != 0xffffffff} threads = sorted(threads_syscall | threads_run | threads_ready | threads_stopped | threads_hardirq | threads_softirq | threads_long_poll, key=lambda x: get_thread_name(options.pid, x)) # # Print header... # print("{:10} {:16} {}".format("TID", "THREAD", "")) print("{:10} {:16} {}".format("-" * 10, "-" * 16, "-" * 76)) indent = 28 * " " # # Print all events/statistics per threads. # poll_id = [k for k, v in syscalls.items() if v == b"poll"][0] for thread in threads: if thread != threads[0]: print("") # # SYSCALL_STATISTICS # print("{:10} {:16} {}\n{}{:20} {:>6} {:>10} {:>16} {:>16}".format( thread, get_thread_name(options.pid, thread), "[SYSCALL STATISTICS]", indent, "NAME", "NUMBER", "COUNT", "TOTAL ns", "MAX ns")) total_count = 0 total_ns = 0 for k, v in sorted(filter(lambda t: t[0].tid == thread, bpf["syscall_data"].items()), key=lambda kv: -kv[1].total_ns): print("{}{:20.20} {:6} {:10} {:16,} {:16,}".format( indent, syscall_name(k.syscall).decode("utf-8"), k.syscall, v.count, v.total_ns, v.worst_ns)) if k.syscall != poll_id: total_count += v.count total_ns += v.total_ns if total_count > 0: print("{}{:20.20} {:6} {:10} {:16,}".format( indent, "TOTAL( - poll):", "", total_count, total_ns)) # # LONG POLL STATISTICS # for k, v in filter(lambda t: t[0].tid == thread, bpf["long_poll_data"].items()): print("\n{:10} {:16} {}\n{}{:10} {:>16} {:>16} {:>16}".format( "", "", "[LONG POLL STATISTICS]", indent, "COUNT", "AVERAGE ns", "MIN ns", "MAX ns")) print("{}{:10} {:16,} {:16,} {:16,}".format( indent, v.count, int(v.total_ns / max(v.count, 1)), v.min_ns, v.max_ns)) break # # THREAD RUN STATISTICS # for k, v in filter(lambda t: t[0].tid == thread, bpf["run_data"].items()): print("\n{:10} {:16} {}\n{}{:10} {:>16} {:>16} {:>16}".format( "", "", "[THREAD RUN STATISTICS]", indent, "SCHED_CNT", "TOTAL ns", "MIN ns", "MAX ns")) print("{}{:10} {:16,} {:16,} {:16,}".format( indent, v.count, v.total_ns, v.min_ns, v.max_ns)) break # # THREAD READY STATISTICS # for k, v in filter(lambda t: t[0].tid == thread, bpf["ready_data"].items()): print("\n{:10} {:16} {}\n{}{:10} {:>16} {:>16}".format( "", "", "[THREAD READY STATISTICS]", indent, "SCHED_CNT", "TOTAL ns", "MAX ns")) print("{}{:10} {:16,} {:16,}".format( indent, v.count, v.total_ns, v.worst_ns)) break # # THREAD STOPPED STATISTICS # for k, v in filter(lambda t: t[0].tid == thread, bpf["stop_data"].items()): print("\n{:10} {:16} {}\n{}{:10} {:>16} {:>16}".format( "", "", "[THREAD STOPPED STATISTICS]", indent, "STOP_CNT", "TOTAL ns", "MAX ns")) print("{}{:10} {:16,} {:16,}".format( indent, v.count, v.total_ns, v.worst_ns)) break # # HARD IRQ STATISTICS # total_ns = 0 total_count = 0 header_printed = False for k, v in sorted(filter(lambda t: t[0].tid == thread, bpf["hardirq_data"].items()), key=lambda kv: -kv[1].total_ns): if not header_printed: print("\n{:10} {:16} {}\n{}{:20} {:>10} {:>16} {:>16}". format("", "", "[HARD IRQ STATISTICS]", indent, "NAME", "COUNT", "TOTAL ns", "MAX ns")) header_printed = True print("{}{:20.20} {:10} {:16,} {:16,}".format( indent, k.irq_name.decode("utf-8"), v.count, v.total_ns, v.worst_ns)) total_count += v.count total_ns += v.total_ns if total_count > 0: print("{}{:20.20} {:10} {:16,}".format( indent, "TOTAL:", total_count, total_ns)) # # SOFT IRQ STATISTICS # total_ns = 0 total_count = 0 header_printed = False for k, v in sorted(filter(lambda t: t[0].tid == thread, bpf["softirq_data"].items()), key=lambda kv: -kv[1].total_ns): if not header_printed: print("\n{:10} {:16} {}\n" "{}{:20} {:>7} {:>10} {:>16} {:>16}". format("", "", "[SOFT IRQ STATISTICS]", indent, "NAME", "VECT_NR", "COUNT", "TOTAL ns", "MAX ns")) header_printed = True print("{}{:20.20} {:>7} {:10} {:16,} {:16,}".format( indent, get_vec_nr_name(k.vec_nr), k.vec_nr, v.count, v.total_ns, v.worst_ns)) total_count += v.count total_ns += v.total_ns if total_count > 0: print("{}{:20.20} {:7} {:10} {:16,}".format( indent, "TOTAL:", "", total_count, total_ns)) # # Print upcall statistics # display_upcall_results() # # Print syscall events # lost_stack_traces = 0 if syscall_events: stack_traces = bpf.get_table("stack_traces") print("\n\n# SYSCALL EVENTS:" "\n{}{:>19} {:>19} {:>10} {:16} {:>10} {}".format( 2 * " ", "ENTRY (ns)", "EXIT (ns)", "TID", "COMM", "DELTA (us)", "SYSCALL")) print("{}{:19} {:19} {:10} {:16} {:10} {}".format( 2 * " ", "-" * 19, "-" * 19, "-" * 10, "-" * 16, "-" * 10, "-" * 16)) for event in syscall_events: print("{}{:19} {:19} {:10} {:16} {:10,} {}".format( " " * 2, event["ts_entry"], event["ts_exit"], event["tid"], get_thread_name(options.pid, event["tid"]), int((event["ts_exit"] - event["ts_entry"]) / 1000), syscall_name(event["syscall"]).decode("utf-8"))) # # Not sure where to put this, but I'll add some info on stack # traces here... Userspace stack traces are very limited due to # the fact that bcc does not support dwarf backtraces. As OVS # gets compiled without frame pointers we will not see much. # If however, OVS does get built with frame pointers, we should not # use the BPF_STACK_TRACE_BUILDID as it does not seem to handle # the debug symbols correctly. Also, note that for kernel # traces you should not use BPF_STACK_TRACE_BUILDID, so two # buffers are needed. # # Some info on manual dwarf walk support: # https://github.com/iovisor/bcc/issues/3515 # https://github.com/iovisor/bcc/pull/4463 # if options.stack_trace_size == 0: continue if event["kernel_stack_id"] < 0 or event["user_stack_id"] < 0: lost_stack_traces += 1 kernel_stack = stack_traces.walk(event["kernel_stack_id"]) \ if event["kernel_stack_id"] >= 0 else [] user_stack = stack_traces.walk(event["user_stack_id"]) \ if event["user_stack_id"] >= 0 else [] for addr in kernel_stack: print("{}{}".format( " " * 10, bpf.ksym(addr, show_module=True, show_offset=True).decode("utf-8", "replace"))) for addr in user_stack: addr_str = bpf.sym(addr, options.pid, show_module=True, show_offset=True).decode("utf-8", "replace") if addr_str == "[unknown]": addr_str += " 0x{:x}".format(addr) print("{}{}".format(" " * 10, addr_str)) # # Print any footer messages. # if lost_stack_traces > 0: print("\n#WARNING: We where not able to display {} stack traces!\n" "# Consider increasing the stack trace size using\n" "# the '--stack-trace-size' option.\n" "# Note that this can also happen due to a stack id\n" "# collision.".format(lost_stack_traces)) # # main() # def main(): # # Don't like these globals, but ctx passing does not seem to work with the # existing open_ring_buffer() API :( # global bpf global options global syscall_events global start_trigger_ts global stop_trigger_ts start_trigger_ts = 0 stop_trigger_ts = 0 # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument("-D", "--debug", help="Enable eBPF debugging", type=int, const=0x3f, default=0, nargs="?") parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitch's PID", type=unsigned_int, default=None) parser.add_argument("-s", "--syscall-events", metavar="DURATION_NS", help="Record syscall events that take longer than " "DURATION_NS. Omit the duration value to record all " "syscall events", type=unsigned_int, const=0, default=None, nargs="?") parser.add_argument("--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=unsigned_int, default=1024, metavar="NUMBER") parser.add_argument("--sample-count", help="Number of sample runs, default 1", type=unsigned_nonzero_int, default=1, metavar="RUNS") parser.add_argument("--sample-interval", help="Delay between sample runs, default 0", type=float, default=0, metavar="SECONDS") parser.add_argument("--sample-time", help="Sample time, default 0.5 seconds", type=float, default=0.5, metavar="SECONDS") parser.add_argument("--skip-syscall-poll-events", help="Skip poll() syscalls with --syscall-events", action="store_true") parser.add_argument("--skip-upcall-stats", help="Skip the collection of upcall statistics", action="store_true") parser.add_argument("--stack-trace-size", help="Number of unique stack traces that can be " "recorded, default 4096. 0 to disable", type=unsigned_int, default=4096) parser.add_argument("--start-trigger", metavar="TRIGGER", help="Start trigger, see documentation for details", type=str, default=None) parser.add_argument("--stop-trigger", metavar="TRIGGER", help="Stop trigger, see documentation for details", type=str, default=None) parser.add_argument("--trigger-delta", metavar="DURATION_NS", help="Only report event when the trigger duration > " "DURATION_NS, default 0 (all events)", type=unsigned_int, const=0, default=0, nargs="?") options = parser.parse_args() # # Find the PID of the ovs-vswitchd daemon if not specified. # if not options.pid: for proc in psutil.process_iter(): if "ovs-vswitchd" in proc.name(): if options.pid: print("ERROR: Multiple ovs-vswitchd daemons running, " "use the -p option!") sys.exit(os.EX_NOINPUT) options.pid = proc.pid # # Error checking on input parameters. # if not options.pid: print("ERROR: Failed to find ovs-vswitchd's PID!") sys.exit(os.EX_UNAVAILABLE) options.buffer_page_count = next_power_of_two(options.buffer_page_count) # # Make sure we are running as root, or else we can not attach the probes. # if os.geteuid() != 0: print("ERROR: We need to run as root to attached probes!") sys.exit(os.EX_NOPERM) # # Setup any of the start stop triggers # if options.start_trigger is not None: try: start_trigger = Probe(options.start_trigger, pid=options.pid) except ValueError as e: print(f"ERROR: Invalid start trigger {str(e)}") sys.exit(os.EX_CONFIG) else: start_trigger = None if options.stop_trigger is not None: try: stop_trigger = Probe(options.stop_trigger, pid=options.pid) except ValueError as e: print(f"ERROR: Invalid stop trigger {str(e)}") sys.exit(os.EX_CONFIG) else: stop_trigger = None # # Attach probe to running process. # source = EBPF_SOURCE.replace("", "\n".join( [" EVENT_{} = {},".format( event.name, event.value) for event in Event])) source = source.replace("", str(options.buffer_page_count)) source = source.replace("", str(options.pid)) if BPF.kernel_struct_has_field(b"task_struct", b"state") == 1: source = source.replace("", "state") else: source = source.replace("", "__state") poll_id = [k for k, v in syscalls.items() if v == b"poll"][0] if options.syscall_events is None: syscall_trace_events = "false" elif options.syscall_events == 0: if not options.skip_syscall_poll_events: syscall_trace_events = "true" else: syscall_trace_events = f"args->id != {poll_id}" else: syscall_trace_events = "delta > {}".format(options.syscall_events) if options.skip_syscall_poll_events: syscall_trace_events += f" && args->id != {poll_id}" source = source.replace("", syscall_trace_events) source = source.replace("", str(options.stack_trace_size)) source = source.replace("", "true" if options.stack_trace_size > 0 else "false") source = source.replace("", str(poll_id)) source = source.replace("", "0" if options.skip_upcall_stats else "1") # # Handle start/stop probes # if start_trigger: source = source.replace("", start_trigger.get_c_code( "start_trigger_probe", "return start_trigger();")) else: source = source.replace("", "") if stop_trigger: source = source.replace("", stop_trigger.get_c_code( "stop_trigger_probe", "return stop_trigger();")) else: source = source.replace("", "") # # Setup usdt or other probes that need handling trough the BFP class. # usdt = USDT(pid=int(options.pid)) try: if start_trigger and start_trigger.probe_type == "usdt": usdt.enable_probe(probe=start_trigger.probe_name(), fn_name="start_trigger_probe") if stop_trigger and stop_trigger.probe_type == "usdt": usdt.enable_probe(probe=stop_trigger.probe_name(), fn_name="stop_trigger_probe") except USDTException as e: print("ERROR: {}".format( (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip(). replace("--with-dtrace or --enable-dtrace", "--enable-usdt-probes"))) sys.exit(os.EX_OSERR) bpf = BPF(text=source, usdt_contexts=[usdt], debug=options.debug) if start_trigger: try: if start_trigger.probe_type == "uprobe": bpf.attach_uprobe(name=f"/proc/{options.pid}/exe", sym=start_trigger.probe_name(), fn_name="start_trigger_probe", pid=options.pid) if start_trigger.probe_type == "uretprobe": bpf.attach_uretprobe(name=f"/proc/{options.pid}/exe", sym=start_trigger.probe_name(), fn_name="start_trigger_probe", pid=options.pid) except Exception as e: print("ERROR: Failed attaching uprobe start trigger " f"'{start_trigger.probe_name()}';\n {str(e)}") sys.exit(os.EX_OSERR) if stop_trigger: try: if stop_trigger.probe_type == "uprobe": bpf.attach_uprobe(name=f"/proc/{options.pid}/exe", sym=stop_trigger.probe_name(), fn_name="stop_trigger_probe", pid=options.pid) if stop_trigger.probe_type == "uretprobe": bpf.attach_uretprobe(name=f"/proc/{options.pid}/exe", sym=stop_trigger.probe_name(), fn_name="stop_trigger_probe", pid=options.pid) except Exception as e: print("ERROR: Failed attaching uprobe stop trigger" f"'{stop_trigger.probe_name()}';\n {str(e)}") sys.exit(os.EX_OSERR) # # If no triggers are configured use the delay configuration # bpf["events"].open_ring_buffer(process_event) sample_count = 0 while sample_count < options.sample_count: sample_count += 1 syscall_events = [] if not options.start_trigger: print_timestamp("# Start sampling") start_capture() stop_time = -1 if options.stop_trigger else \ time_ns() + options.sample_time * 1000000000 else: # For start triggers the stop time depends on the start trigger # time, or depends on the stop trigger if configured. stop_time = -1 if options.stop_trigger else 0 while True: keyboard_interrupt = False try: last_start_ts = start_trigger_ts last_stop_ts = stop_trigger_ts if stop_time > 0: delay = int((stop_time - time_ns()) / 1000000) if delay <= 0: break else: delay = -1 bpf.ring_buffer_poll(timeout=delay) if stop_time <= 0 and last_start_ts != start_trigger_ts: print_timestamp( "# Start sampling (trigger@{})".format( start_trigger_ts)) if not options.stop_trigger: stop_time = time_ns() + \ options.sample_time * 1000000000 if last_stop_ts != stop_trigger_ts: break except KeyboardInterrupt: keyboard_interrupt = True break if options.stop_trigger and not capture_running(): print_timestamp("# Stop sampling (trigger@{})".format( stop_trigger_ts)) else: print_timestamp("# Stop sampling") if stop_trigger_ts != 0 and start_trigger_ts != 0: trigger_delta = stop_trigger_ts - start_trigger_ts else: trigger_delta = None if not trigger_delta or trigger_delta >= options.trigger_delta: stop_capture(force=True) # Prevent a new trigger to start. process_results(syscall_events=syscall_events, trigger_delta=trigger_delta) elif trigger_delta: sample_count -= 1 print_timestamp("# Sample dump skipped, delta {:,} ns".format( trigger_delta)) reset_capture() stop_capture() if keyboard_interrupt: break if options.sample_interval > 0: time.sleep(options.sample_interval) # # Report lost events. # dropcnt = bpf.get_table("dropcnt") for k in dropcnt.keys(): count = dropcnt.sum(k).value if k.value == 0 and count > 0: print("\n# WARNING: Not all events were captured, {} were " "dropped!\n# Increase the BPF ring buffer size " "with the --buffer-page-count option.".format(count)) if options.sample_count > 1: trigger_miss = bpf.get_table("trigger_miss") for k in trigger_miss.keys(): count = trigger_miss.sum(k).value if k.value == 0 and count > 0: print("\n# WARNING: Not all start triggers were successful. " "{} were missed due to\n# slow userspace " "processing!".format(count)) # # Start main() as the default entry point... # if __name__ == "__main__": main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/kernel_delay.rst000066400000000000000000000764451514270232600266470ustar00rootroot00000000000000Troubleshooting Open vSwitch: Is the kernel to blame? ===================================================== Often, when troubleshooting Open vSwitch (OVS) in the field, you might be left wondering if the issue is really OVS-related, or if it's a problem with the kernel being overloaded. Messages in the log like ``Unreasonably long XXXXms poll interval`` might suggest it's OVS, but from experience, these are mostly related to an overloaded Linux Kernel. The kernel_delay.py tool can help you quickly identify if the focus of your investigation should be OVS or the Linux kernel. Introduction ------------ ``kernel_delay.py`` consists of a Python script that uses the BCC [#BCC]_ framework to install eBPF probes. The data the eBPF probes collect will be analyzed and presented to the user by the Python script. Some of the presented data can also be captured by the individual scripts included in the BBC [#BCC]_ framework. kernel_delay.py has two modes of operation: - In **time mode**, the tool runs for a specific time and collects the information. - In **trigger mode**, event collection can be started and/or stopped based on a specific eBPF probe. Currently, the following probes are supported: - USDT probes - Kernel tracepoints - kprobe - kretprobe - uprobe - uretprobe In addition, the option, ``--sample-count``, exists to specify how many iterations you would like to do. When using triggers, you can also ignore samples if they are less than a number of nanoseconds with the ``--trigger-delta`` option. The latter might be useful when debugging Linux syscalls which take a long time to complete. More on this later. Finally, you can configure the delay between two sample runs with the ``--sample-interval`` option. Before getting into more details, you can run the tool without any options to see what the output looks like. Notice that it will try to automatically get the process ID of the running ``ovs-vswitchd``. You can overwrite this with the ``--pid`` option. .. code-block:: console $ sudo ./kernel_delay.py # Start sampling @2023-06-08T12:17:22.725127 (10:17:22 UTC) # Stop sampling @2023-06-08T12:17:23.224781 (10:17:23 UTC) # Sample dump @2023-06-08T12:17:23.224855 (10:17:23 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 27090 ovs-vswitchd [SYSCALL STATISTICS] 31741 revalidator122 [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns poll 7 5 184,193,176 184,191,520 recvmsg 47 494 125,208,756 310,331 futex 202 8 18,768,758 4,023,039 sendto 44 10 375,861 266,867 sendmsg 46 4 43,294 11,213 write 1 1 5,949 5,949 getrusage 98 1 1,424 1,424 read 0 1 1,292 1,292 TOTAL( - poll): 519 144,405,334 [LONG POLL STATISTICS] COUNT AVERAGE ns MIN ns MAX ns 58 76,773 7,388 234,129 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns 6 136,764,071 1,480 115,146,424 [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns 7 11,334 6,636 [THREAD STOPPED STATISTICS] STOP_CNT TOTAL ns MAX ns 3 3,045,728,323 1,015,739,474 [HARD IRQ STATISTICS] NAME COUNT TOTAL ns MAX ns eno8303-rx-1 1 3,586 3,586 TOTAL: 1 3,586 [SOFT IRQ STATISTICS] NAME VECT_NR COUNT TOTAL ns MAX ns net_rx 3 1 17,699 17,699 sched 7 6 13,820 3,226 rcu 9 16 13,586 1,554 timer 1 3 10,259 3,815 TOTAL: 26 55,364 By default, the tool will run for half a second in `time mode`. To extend this you can use the ``--sample-time`` option. What will it report ------------------- The above sample output separates the captured data on a per-thread basis. For this, it displays the thread's id (``TID``) and name (``THREAD``), followed by resource-specific data. Which are: - ``SYSCALL STATISTICS`` - ``LONG POLL STATISTICS`` - ``THREAD RUN STATISTICS`` - ``THREAD READY STATISTICS`` - ``THREAD STOPPED STATISTICS`` - ``HARD IRQ STATISTICS`` - ``SOFT IRQ STATISTICS`` - ``UPCALL STATISTICS`` The following sections will describe in detail what statistics they report. ``SYSCALL STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~ ``SYSCALL STATISTICS`` tell you which Linux system calls got executed during the measurement interval. This includes the number of times the syscall was called (``COUNT``), the total time spent in the system calls (``TOTAL ns``), and the worst-case duration of a single call (``MAX ns``). It also shows the total of all system calls, but it excludes the poll system call, as the purpose of this call is to wait for activity on a set of sockets, and usually, the thread gets swapped out. Note that it only counts calls that started and stopped during the measurement interval! ``LONG POLL STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~ ``LONG POLL STATISTICS`` tell you how long the thread was running between two poll system calls. This relates to the 'Unreasonably long ... ms poll interval' message reported by ovs-vswitchd. More details about this message can be found in the example section. ``THREAD RUN STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~~~~ ``THREAD RUN STATISTICS`` tell you how long the thread was running on a CPU during the measurement interval. Note that these statistics only count events where the thread started and stopped running on a CPU during the measurement interval. For example, if this was a PMD thread, you should see zero ``SCHED_CNT`` and ``TOTAL_ns``. If not, there might be a misconfiguration. ``THREAD READY STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``THREAD READY STATISTICS`` tell you the time between the thread being ready to run and it actually running on the CPU. Note that these statistics only count events where the thread was getting ready to run and started running during the measurement interval. ``THREAD STOPPED STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``THREAD STOPPED STATISTICS`` reveal the number of instances where the thread has been scheduled out while in the running state due to its transition to the TASK_STOPPED state. This behavior can be replicated by manually placing the thread in the stopped state and subsequently resuming it. For instance: .. code-block:: console # kill -STOP $(pidof ovs-vswitchd); \ sleep 1; \ kill -CONT $(pidof ovs-vswitchd); Note that these statistics only count events where the thread was running at the time it was put to stopped state. ``HARD IRQ STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~~ ``HARD IRQ STATISTICS`` tell you how much time was spent servicing hard interrupts during the threads run time. It shows the interrupt name (``NAME``), the number of interrupts (``COUNT``), the total time spent in the interrupt handler (``TOTAL ns``), and the worst-case duration (``MAX ns``). ``SOFT IRQ STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~~~ ``SOFT IRQ STATISTICS`` tell you how much time was spent servicing soft interrupts during the threads run time. It shows the interrupt name (``NAME``), vector number (``VECT_NR``), the number of interrupts (``COUNT``), the total time spent in the interrupt handler (``TOTAL ns``), and the worst-case duration (``MAX ns``). ``UPCALL STATISTICS`` ~~~~~~~~~~~~~~~~~~~~~ The ``UPCALL STATISTICS`` section provides information on the number of upcalls sent by the kernel to userspace. If any upcalls fail to be sent, the specific return codes are recorded. Statistics are presented both as a total and on a per-CPU basis. The ``--syscall-events`` option ------------------------------- In addition to reporting global syscall statistics in ``SYSCALL_STATISTICS``, the tool can also report each individual syscall. This can be a usefull second step if the ``SYSCALL_STATISTICS`` show high latency numbers. All you need to do is add the ``--syscall-events`` option, with or without the additional ``DURATION_NS`` parameter. The ``DUTATION_NS`` parameter allows you to exclude events that take less than the supplied time. The ``--skip-syscall-poll-events`` option allows you to exclude poll syscalls from the report. Below is an example run, note that the resource-specific data is removed to highlight the syscall events: .. code-block:: console $ sudo ./kernel_delay.py --syscall-events 50000 --skip-syscall-poll-events # Start sampling @2023-06-13T17:10:46.460874 (15:10:46 UTC) # Stop sampling @2023-06-13T17:10:46.960727 (15:10:46 UTC) # Sample dump @2023-06-13T17:10:46.961033 (15:10:46 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 3359686 ipf_clean2 [SYSCALL STATISTICS] ... 3359635 ovs-vswitchd [SYSCALL STATISTICS] ... 3359697 revalidator12 [SYSCALL STATISTICS] ... 3359698 revalidator13 [SYSCALL STATISTICS] ... 3359699 revalidator14 [SYSCALL STATISTICS] ... 3359700 revalidator15 [SYSCALL STATISTICS] ... # SYSCALL EVENTS: ENTRY (ns) EXIT (ns) TID COMM DELTA (us) SYSCALL ------------------- ------------------- ---------- ---------------- ---------- ---------------- 2161821694935486 2161821695031201 3359699 revalidator14 95 futex syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode+0x9 [kernel] do_syscall_64+0x68 [kernel] entry_SYSCALL_64_after_hwframe+0x72 [kernel] __GI___lll_lock_wait+0x30 [libc.so.6] ovs_mutex_lock_at+0x18 [ovs-vswitchd] [unknown] 0x696c003936313a63 2161821695276882 2161821695333687 3359698 revalidator13 56 futex syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode+0x9 [kernel] do_syscall_64+0x68 [kernel] entry_SYSCALL_64_after_hwframe+0x72 [kernel] __GI___lll_lock_wait+0x30 [libc.so.6] ovs_mutex_lock_at+0x18 [ovs-vswitchd] [unknown] 0x696c003134313a63 2161821695275820 2161821695405733 3359700 revalidator15 129 futex syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode+0x9 [kernel] do_syscall_64+0x68 [kernel] entry_SYSCALL_64_after_hwframe+0x72 [kernel] __GI___lll_lock_wait+0x30 [libc.so.6] ovs_mutex_lock_at+0x18 [ovs-vswitchd] [unknown] 0x696c003936313a63 2161821695964969 2161821696052021 3359635 ovs-vswitchd 87 accept syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode_prepare+0x161 [kernel] syscall_exit_to_user_mode+0x9 [kernel] do_syscall_64+0x68 [kernel] entry_SYSCALL_64_after_hwframe+0x72 [kernel] __GI_accept+0x4d [libc.so.6] pfd_accept+0x3a [ovs-vswitchd] [unknown] 0x7fff19f2bd00 [unknown] 0xe4b8001f0f As you can see above, the output also shows the stackback trace. You can disable this using the ``--stack-trace-size 0`` option. As you can see above, the backtrace does not show a lot of useful information due to the BCC [#BCC]_ toolkit not supporting dwarf decoding. So to further analyze system call backtraces, you could use perf. The following perf script can do this for you (refer to the embedded instructions): https://github.com/chaudron/perf_scripts/blob/master/analyze_perf_pmd_syscall.py Using triggers -------------- The tool supports start and, or stop triggers. This will allow you to capture statistics triggered by a specific event. The following combinations of stop-and-start triggers can be used. If you only use ``--start-trigger``, the inspection start when the trigger happens and runs until the ``--sample-time`` number of seconds has passed. The example below shows all the supported options in this scenario. .. code-block:: console $ sudo ./kernel_delay.py --start-trigger up:bridge_run --sample-time 4 \ --sample-count 4 --sample-interval 1 If you only use ``--stop-trigger``, the inspection starts immediately and stops when the trigger happens. The example below shows all the supported options in this scenario. .. code-block:: console $ sudo ./kernel_delay.py --stop-trigger upr:bridge_run \ --sample-count 4 --sample-interval 1 If you use both ``--start-trigger`` and ``--stop-trigger`` triggers, the statistics are captured between the two first occurrences of these events. The example below shows all the supported options in this scenario. .. code-block:: console $ sudo ./kernel_delay.py --start-trigger up:bridge_run \ --stop-trigger upr:bridge_run \ --sample-count 4 --sample-interval 1 \ --trigger-delta 50000 What triggers are supported? Note that what ``kernel_delay.py`` calls triggers, BCC [#BCC]_, calls events; these are eBPF tracepoints you can attach to. For more details on the supported tracepoints, check out the BCC documentation [#BCC_EVENT]_. The list below shows the supported triggers and their argument format: **USDT probes:** [u|usdt]:{provider}:{probe} **Kernel tracepoint:** [t:trace]:{system}:{event} **kprobe:** [k:kprobe]:{kernel_function} **kretprobe:** [kr:kretprobe]:{kernel_function} **uprobe:** [up:uprobe]:{function} **uretprobe:** [upr:uretprobe]:{function} Here are a couple of trigger examples, more use-case-specific examples can be found in the *Examples* section. .. code-block:: console --start|stop-trigger u:udpif_revalidator:start_dump --start|stop-trigger t:openvswitch:ovs_dp_upcall --start|stop-trigger k:ovs_dp_process_packet --start|stop-trigger kr:ovs_dp_process_packet --start|stop-trigger up:bridge_run --start|stop-trigger upr:bridge_run Examples -------- This section will give some examples of how to use this tool in real-world scenarios. Let's start with the issue where Open vSwitch reports ``Unreasonably long XXXXms poll interval`` on your revalidator threads. Note that there is a blog available explaining how the revalidator process works in OVS [#REVAL_BLOG]_. First, let me explain this log message. It gets logged if the time delta between two ``poll_block()`` calls is more than 1 second. In other words, the process was spending a lot of time processing stuff that was made available by the return of the ``poll_block()`` function. Do a run with the tool using the existing USDT revalidator probes as a start and stop trigger (Note that the resource-specific data is removed from the none revalidator threads): .. code-block:: console $ sudo ./kernel_delay.py --start-trigger u:udpif_revalidator:start_dump --stop-trigger u:udpif_revalidator:sweep_done # Start sampling (trigger@791777093512008) @2023-06-14T14:52:00.110303 (12:52:00 UTC) # Stop sampling (trigger@791778281498462) @2023-06-14T14:52:01.297975 (12:52:01 UTC) # Triggered sample dump, stop-start delta 1,187,986,454 ns @2023-06-14T14:52:01.298021 (12:52:01 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 1457761 handler24 [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns sendmsg 46 6110 123,274,761 41,776 recvmsg 47 136299 99,397,508 49,896 futex 202 51 7,655,832 7,536,776 poll 7 4068 1,202,883 2,907 getrusage 98 2034 586,602 1,398 sendto 44 9 213,682 27,417 TOTAL( - poll): 144503 231,128,385 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns 1 1,438 1,438 [SOFT IRQ STATISTICS] NAME VECT_NR COUNT TOTAL ns MAX ns sched 7 21 59,145 3,769 rcu 9 50 42,917 2,234 TOTAL: 71 102,062 1457733 ovs-vswitchd [SYSCALL STATISTICS] ... 1457792 revalidator55 [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns futex 202 73 572,576,329 19,621,600 recvmsg 47 815 296,697,618 405,338 sendto 44 3 78,302 26,837 sendmsg 46 3 38,712 13,250 write 1 1 5,073 5,073 TOTAL( - poll): 895 869,396,034 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns 48 394,350,393 1,729 140,455,796 [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns 49 23,650 1,559 [SOFT IRQ STATISTICS] NAME VECT_NR COUNT TOTAL ns MAX ns sched 7 14 26,889 3,041 rcu 9 28 23,024 1,600 TOTAL: 42 49,913 Above you see from the start of the output that the trigger took more than a second (1,187,986,454 ns), which is already know, by looking at the output of the ``ovs-vsctl upcall/show`` command. From the *revalidator55*'s ``SYSCALL STATISTICS`` statistics you can see it spent almost 870ms handling syscalls, and there were no poll() calls being executed. The ``THREAD RUN STATISTICS`` statistics here are a bit misleading, as it looks like OVS only spent 394ms on the CPU. But earlier, it was mentioned that this time does not include the time being on the CPU at the start or stop of an event. What is exactly the case here, because USDT probes were used. From the above data and maybe some ``top`` output, it can be determined that the *revalidator55* thread is taking a lot of CPU time, probably because it has to do a lot of revalidator work by itself. The solution here is to increase the number of revalidator threads, so more work could be done in parallel. Here is another run of the same command in another scenario: .. code-block:: console $ sudo ./kernel_delay.py --start-trigger u:udpif_revalidator:start_dump --stop-trigger u:udpif_revalidator:sweep_done # Start sampling (trigger@795160501758971) @2023-06-14T15:48:23.518512 (13:48:23 UTC) # Stop sampling (trigger@795160764940201) @2023-06-14T15:48:23.781381 (13:48:23 UTC) # Triggered sample dump, stop-start delta 263,181,230 ns @2023-06-14T15:48:23.781414 (13:48:23 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 1457733 ovs-vswitchd [SYSCALL STATISTICS] ... 1457792 revalidator55 [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns recvmsg 47 284 193,422,110 46,248,418 sendto 44 2 46,685 23,665 sendmsg 46 2 24,916 12,703 write 1 1 6,534 6,534 TOTAL( - poll): 289 193,500,245 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns 2 47,333,558 331,516 47,002,042 [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns 3 87,000,403 45,999,712 [SOFT IRQ STATISTICS] NAME VECT_NR COUNT TOTAL ns MAX ns sched 7 2 9,504 5,109 TOTAL: 2 9,504 Here you can see the revalidator run took about 263ms, which does not look odd, however, the ``THREAD READY STATISTICS`` information shows that OVS was waiting 87ms for a CPU to be run on. This means the revalidator process could have finished 87ms faster. Looking at the ``MAX ns`` value, a worst-case delay of almost 46ms can be seen, which hints at an overloaded system. One final example that uses a ``uprobe`` to get some statistics on a ``bridge_run()`` execution that takes more than 1ms. .. code-block:: console $ sudo ./kernel_delay.py --start-trigger up:bridge_run --stop-trigger ur:bridge_run --trigger-delta 1000000 # Start sampling (trigger@2245245432101270) @2023-06-14T16:21:10.467919 (14:21:10 UTC) # Stop sampling (trigger@2245245432414656) @2023-06-14T16:21:10.468296 (14:21:10 UTC) # Sample dump skipped, delta 313,386 ns @2023-06-14T16:21:10.468419 (14:21:10 UTC) # Start sampling (trigger@2245245505301745) @2023-06-14T16:21:10.540970 (14:21:10 UTC) # Stop sampling (trigger@2245245506911119) @2023-06-14T16:21:10.542499 (14:21:10 UTC) # Triggered sample dump, stop-start delta 1,609,374 ns @2023-06-14T16:21:10.542565 (14:21:10 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 3371035 [SYSCALL STATISTICS] ... 3371102 handler66 [SYSCALL STATISTICS] ... 3366258 ovs-vswitchd [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns futex 202 43 403,469 199,312 clone3 435 13 174,394 30,731 munmap 11 8 115,774 21,861 poll 7 5 92,969 38,307 unlink 87 2 49,918 35,741 mprotect 10 8 47,618 13,201 accept 43 10 31,360 6,976 mmap 9 8 30,279 5,776 write 1 6 27,720 11,774 rt_sigprocmask 14 28 12,281 970 read 0 6 9,478 2,318 recvfrom 45 3 7,024 4,024 sendto 44 1 4,684 4,684 getrusage 98 5 4,594 1,342 close 3 2 2,918 1,627 recvmsg 47 1 2,722 2,722 TOTAL( - poll): 144 924,233 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns 13 817,605 5,433 524,376 [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns 14 28,646 11,566 [SOFT IRQ STATISTICS] NAME VECT_NR COUNT TOTAL ns MAX ns rcu 9 1 2,838 2,838 TOTAL: 1 2,838 3371110 revalidator74 [SYSCALL STATISTICS] ... 3366311 urcu3 [SYSCALL STATISTICS] ... OVS removed some of the threads and their resource-specific data, but based on the ```` thread name, you can determine that some threads no longer exist. In the ``ovs-vswitchd`` thread, you can see some ``clone3`` syscalls, indicating threads were created. In this example, it was due to the deletion of a bridge, which resulted in the recreation of the revalidator and handler threads. Use with Openshift ------------------ This section describes how you would use the tool on a node in an OpenShift cluster. It assumes you have console access to the node, either directly or through a debug container. A base fedora38 container will be used through podman, as this will allow the use of some additional tools and packages needed. First the containers need to be started: .. code-block:: console [core@localhost ~]$ sudo podman run -it --rm \ -e PS1='[(DEBUG)\u@\h \W]\$ ' \ --privileged --network=host --pid=host \ -v /lib/modules:/lib/modules:ro \ -v /sys/kernel/debug:/sys/kernel/debug \ -v /proc:/proc \ -v /:/mnt/rootdir \ quay.io/fedora/fedora:38-x86_64 [(DEBUG)root@localhost /]# Next add the ``linux_delay.py`` dependencies: .. code-block:: console [(DEBUG)root@localhost /]# dnf install -y bcc-tools perl-interpreter \ python3-pytz python3-psutil You need to install the devel, debug and source RPMs for your OVS and kernel version: .. code-block:: console [(DEBUG)root@localhost home]# rpm -i \ openvswitch2.17-debuginfo-2.17.0-67.el8fdp.x86_64.rpm \ openvswitch2.17-debugsource-2.17.0-67.el8fdp.x86_64.rpm \ kernel-devel-4.18.0-372.41.1.el8_6.x86_64.rpm Now the tool can be started. Here the above ``bridge_run()`` example is used: .. code-block:: console [(DEBUG)root@localhost home]# ./kernel_delay.py --start-trigger up:bridge_run --stop-trigger ur:bridge_run # Start sampling (trigger@75279117343513) @2023-06-15T11:44:07.628372 (11:44:07 UTC) # Stop sampling (trigger@75279117443980) @2023-06-15T11:44:07.628529 (11:44:07 UTC) # Triggered sample dump, stop-start delta 100,467 ns @2023-06-15T11:44:07.628569 (11:44:07 UTC) TID THREAD ---------- ---------------- ---------------------------------------------------------------------------- 1246 ovs-vswitchd [SYSCALL STATISTICS] NAME NUMBER COUNT TOTAL ns MAX ns getdents64 217 2 8,560 8,162 openat 257 1 6,951 6,951 accept 43 4 6,942 3,763 recvfrom 45 1 3,726 3,726 recvmsg 47 2 2,880 2,188 stat 4 2 1,946 1,384 close 3 1 1,393 1,393 fstat 5 1 1,324 1,324 TOTAL( - poll): 14 33,722 [THREAD RUN STATISTICS] SCHED_CNT TOTAL ns MIN ns MAX ns [THREAD READY STATISTICS] SCHED_CNT TOTAL ns MAX ns .. rubric:: Footnotes .. [#BCC] https://github.com/iovisor/bcc .. [#BCC_EVENT] https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#events--arguments .. [#REVAL_BLOG] https://developers.redhat.com/articles/2022/10/19/open-vswitch-revalidator-process-explained openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/reval_monitor.py000077500000000000000000000726601514270232600267070ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2022 Red Hat, Inc. # # 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. # # Script information: # ------------------- # reval_monitor.py uses various user-defined tracepoints to get all the # revalidator-process related variables and will display them in a (dynamic) # graph. In addition, it will also dump the data to the console # in a CSV format. Note that all the graphical output can be disabled. # # All the USDT events can be saved to a file and than can be used to # replay the trace offline and still get the plots. # # The script can simple be invoked without any options, and it will try # to find the running ovs-vswitchd instance: # # # ./reval_monitor.py # # Starting trace @2022-09-20T04:07:43.588749 (08:07:43 UTC) # ts_start, ts_complete, n_flows, n_reval_flows, avg_n_flows, max_n_flows, # flow_limit, dump_duration, poll_wait, actual_wait # 1741367714251645, 1741367714532545, 0, 0, 0, 10000, 69000, 1, 500, 500.52 # 1741368215056961, 1741368215318223, 0, 0, 0, 10000, 69000, 1, 500, 500.55 # 1741368715865871, 1741368716107089, 0, 0, 0, 10000, 69000, 1, 500, 499.48 # ^C# Stopping trace @2022-09-20T04:07:49.893827 (08:07:49 UTC) # # IMPORTANT NOTE: This script only works when a single datapath is configured! # 2nd IMPORTANT NOTE: ovs-vswitchd either needs to be built with debug info # or the debug info package needs to be installed! # # The following are the available options: # # usage: reval_monitor.py [-h] [-c] [--buffer-page-count NUMBER] # [-D [DEBUG]] [-g] [--no-ukey-count] # [-p VSWITCHD_PID] [-P PAHOLE] [-r FILE] [-R] # [-u SECONDS] [-w FILE] [-W FILE] # # options: # -h, --help show this help message and exit # -c, --compress-output # Compress output, i.e. only dump changes in # the dataset # --buffer-page-count NUMBER # Number of BPF ring buffer pages, default # 1024 # -D [DEBUG], --debug [DEBUG] # Enable eBPF debugging # -g, --no-gui Do not use the gui to display plots # --no-ukey-count No revalidate_ukey() counting # -p VSWITCHD_PID, --pid VSWITCHD_PID # ovs-vswitch's PID # -P PAHOLE, --pahole PAHOLE # Pahole executable to use, default pahole # -r FILE, --read-events FILE # Read events from instead of # installing tracepoints # -R, --no-realtime-plots # Do not show realtime plot while tracing # -u SECONDS, --update-interval SECONDS # Seconds to wait between real time update, # default 1 # -w FILE, --write-events FILE # Write events to # -W FILE, --write-charts FILE # Write overall charts to .png # [-D [DEBUG]] [-g] [--no-ukey-count] # [-p VSWITCHD_PID] [-r FILE] [-R] # [-u SECONDS] [-w FILE] [-W FILE] # # The -g option disabled all GUI output of matplotlib, -R only disables the # real-time plots. As real-time plots are rather slow, the -u option can be # used to only update the graph every x seconds, which might speed up the # processing. # # The --no-ukey-count option disables counting of the number of flows actually # being revalidated against the current OpenFlow ruleset. This will not install # the specific tracepoint which would be called for each flow being # revalidated. # # What is plotted in the graphs (and dumped in the CSV output)? # - n_flows: Number of flows active in the system. # - n_reval_flows: Number of flows that where revalidated against the OpenFlow # ruleset. # - dump_duration: Time it took to dump and process all flows. # - avg_n_flows: Average number of flows in the system. # - max_n_flows: Maximum number of flows in the system. # - flow_limit: Dynamic flow limit. # - poll_wait: Time requested for the poll wait. # - actual_wait: Time it took to be woken up. # # Dependencies: # This script needs the 'readelf' binary to be available. In addition, it also # needs pahole to be installed, and it needs a version that is equal or newer # than the following commit on the next branch: # # https://git.kernel.org/pub/scm/devel/pahole/pahole.git/?h=next # c55b13b9d785 ("WIP: Remove DW_TAG_atomic_type when encoding BTF") # # To use a locally compiled pahole the --pahole option can be used. # For example: # # ./reval_monitor.py --pahole ~/pahole/build/pahole -g # Starting trace @2022-12-20T14:57:26.077815 (13:57:26 UTC) # ts_start, ts_complete, n_flows, n_reval_flows, avg_n_flows, max_n_flows, \ # flow_limit, dump_duration, poll_wait, actual_wait # 4202771850983494, 4202771851472838, 0, 0, 0, 0, 10000, 1, 500, 15.06 # 4202771866531996, 4202771867713366, 0, 0, 0, 0, 10000, 1, 500, 4.23 # 4202771871941979, 4202771872749915, 0, 0, 0, 0, 10000, 1, 500, 500.02 # 4202772372770361, 4202772373531820, 0, 0, 0, 0, 10000, 1, 500, 499.96 # 4202772873487942, 4202772874514753, 0, 0, 0, 0, 10000, 1, 500, 500.01 # 4202773374528435, 4202773375695054, 0, 0, 0, 0, 10000, 1, 500, 500.01 # 4202773875701559, 4202773876880763, 0, 0, 0, 0, 10000, 1, 500, 500.04 # 4202774376925058, 4202774377905799, 0, 0, 0, 0, 10000, 1, 500, 500.03 # ^C# Stopping trace @2022-12-20T14:57:40.391730 (13:57:40 UTC) # try: from bcc import BPF, USDT, USDTException except ModuleNotFoundError: print("WARNING: Can't find the BPF Compiler Collection (BCC) tools!") print(" This is NOT problem if you analyzing previously collected" " data.\n") from collections import namedtuple from enum import IntEnum from pathlib import Path import argparse import ast import datetime import re import subprocess import sys import pytz import psutil import matplotlib.pyplot as plt # # Actual eBPF source code # EBPF_SOURCE = """ #include enum { }; struct event_t { u64 ts; u32 pid; u32 id; u64 n_flows; u32 avg_n_flows; u32 max_n_flows; u32 flow_limit; u32 dump_duration; u32 poll_wait; }; BPF_RINGBUF_OUTPUT(events, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); static struct event_t *get_event(uint32_t id) { struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { dropcnt.increment(0); return NULL; } event->id = id; event->ts = bpf_ktime_get_ns(); event->pid = bpf_get_current_pid_tgid(); return event; } int probe__start_dump(struct pt_regs *ctx) { struct event_t *event = get_event(EVENT_START_DUMP); if (!event) return 1; events.ringbuf_submit(event, 0); return 0; }; int probe__sweep_done(struct pt_regs *ctx) { struct udpif udpif; bpf_usdt_readarg_p(1, ctx, &udpif, sizeof(udpif)); struct event_t *event = get_event(EVENT_SWEEP_DONE); if (!event) return 1; event->avg_n_flows = udpif.avg_n_flows; event->max_n_flows = udpif.max_n_flows; event->flow_limit = udpif.flow_limit; event->dump_duration = udpif.dump_duration; bpf_usdt_readarg(2, ctx, &event->n_flows); bpf_usdt_readarg(3, ctx, &event->poll_wait); events.ringbuf_submit(event, 0); return 0; }; int probe__reval_entry(struct pt_regs *ctx) { struct event_t *event = get_event(EVENT_REVAL_ENTRY); if (!event) return 1; events.ringbuf_submit(event, 0); return 0; }; """ # # event_to_dict() # def event_to_dict(event): return dict([(field, getattr(event, field)) for (field, _) in event._fields_ if isinstance(getattr(event, field), (int, bytes))]) # # print_csv_header() # def print_csv_header(): print("ts_start, ts_complete, n_flows, n_reval_flows, avg_n_flows, " "max_n_flows, flow_limit, dump_duration, poll_wait, actual_wait") # # Event enum # Event = IntEnum("Event", ["START_DUMP", "SWEEP_DONE", "REVAL_ENTRY"], start=0) # # process_event() # def process_event(ctx, data, size): event = b['events'].event(data) _process_event(event) def _process_event(event): global graph if export_file is not None: export_file.write("event = {}\n".format(event_to_dict(event))) if event.id == Event.START_DUMP and not state['running']: start = state["last_start"] done = state["last_done"] if done and start: actual_wait = (event.ts - done.ts) / 1000000 csv = "{}, {}, {}, {}, {}, {}, {}, {}, {}, {:.2f}".format( start.ts, done.ts, done.n_flows, graph.ukey_count, done.avg_n_flows, done.max_n_flows, done.flow_limit, done.dump_duration, done.poll_wait, actual_wait) if graph.base_time == 0: graph = graph._replace(base_time=done.ts) graph.time.append((done.ts - graph.base_time) / 1000000000) graph.n_flows.append(done.n_flows) graph.n_reval_flows.append(graph.ukey_count) graph.avg_n_flows.append(done.avg_n_flows) graph.max_n_flows.append(done.max_n_flows) graph.flow_limit.append(done.flow_limit) graph.dump_duration.append(done.dump_duration) graph.poll_wait.append(done.poll_wait) graph.actual_wait.append(actual_wait) if not options.no_gui and not options.no_realtime_plots: updated_graph = dynamic_plot_update( graph, refresh=options.update_interval) if updated_graph is None: raise KeyboardInterrupt graph = updated_graph if options.compress_output: last_csv = state["last_csv"] if not last_csv or \ csv.split(",")[2:-1] != last_csv.split(",")[2:-1] or \ abs((event.ts - done.ts) / 1000000 - done.poll_wait) > 100: print(csv) else: state["last_not_printed_csv"] = csv state["last_csv"] = csv else: print(csv) state["last_start"] = event state['running'] = True graph = graph._replace(ukey_count=0) elif event.id == Event.SWEEP_DONE and state['running']: state["last_done"] = event state['running'] = False elif event.id == Event.REVAL_ENTRY and state['running']: graph = graph._replace(ukey_count=graph.ukey_count + 1) # # run_program() # def run_program(command): try: process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8', check=True) except subprocess.CalledProcessError as perror: return perror.returncode, perror.stdout return 0, process.stdout # # get_ovs_definitions() # def get_ovs_definitions(objects, pahole="pahole", pid=None): if pid is None: raise ValueError("A valid pid value should be supplied!") if not isinstance(objects, list): objects = [objects] if len(objects) == 0: raise ValueError("Must supply at least one object!") vswitchd = Path("/proc/{}/exe".format(str(pid))).resolve() object_str = ','.join(objects) def run_pahole(debug_file): error, result = run_program([pahole, "-C", object_str, "--compile", debug_file]) if error: if "pahole: {}: Invalid argument".format(debug_file) not in result: print("ERROR: Pahole failed to get ovs-vswitchd data " "structures!\n{}".format(re.sub('^', ' ' * 7, result.rstrip(), flags=re.MULTILINE))) sys.exit(-1) return None if bool(re.search("pahole: type .* not found", result)): return None return result def run_readelf(bin_file): error, result = run_program(['readelf', "-n", "--debug-dump=links", bin_file]) if error: print("ERROR: Failed 'readelf' on \"{}\"!\n{}". format(bin_file, re.sub('^', ' ' * 7, result, flags=re.MULTILINE))) sys.exit(-1) return result def get_debug_file(bin_file): elf_result = run_readelf(bin_file) match = re.search("Build ID: ([0-9a-fA-F]+)", elf_result) if not match: print("ERROR: Can't find build ID to read debug symbols!") sys.exit(-1) dbg_file = "/usr/lib/debug/.build-id/{}/{}.debug".format( match.group(1)[:2], match.group(1)[2:]) return dbg_file def get_from_shared_library(debug_file): ovs_libs = ['libofproto', 'libopenvswitch', 'libovsdb', 'libsflow', 'libvtep'] error, ldd_result = run_program(['ldd', debug_file]) if error: print("ERROR: Failed 'ldd' on \"{}\"!\n{}". format(debug_file, re.sub('^', ' ' * 7, ldd_result, flags=re.MULTILINE))) sys.exit(-1) for lib in ovs_libs: match = re.search(r"^\s*{}.* => (.*) \(.*\)$".format(lib), ldd_result, flags=re.MULTILINE) if match is None: continue result = run_pahole(match.group(1)) if result is None: result = run_pahole(get_debug_file(match.group(1))) if result: return result return None # # First try to find the debug data as part of the executable. # result = run_pahole(vswitchd) if result is None: print("INFO: Failed to find debug info in \"{}\"!".format(vswitchd)) # # Get additional .debug information if available. # dbg_file = get_debug_file(vswitchd) result = run_pahole(dbg_file) if result is None: print("INFO: Failed to find debug info in \"{}\"!".format( dbg_file)) # # Try to get information from shared libraries if used. # result = get_from_shared_library(vswitchd) if result is None: print("ERROR: Failed to find needed data structures through pahole!") sys.exit(-1) # # We need an empty _Atomic definition to avoid compiler complaints. # result = "#define _Atomic\n" + result # # Remove the uint64_t definition as it conflicts with the kernel one. # result = re.sub("^typedef.*uint64_t;$", "", result, flags=re.MULTILINE) return result # # next_power_of_two() # def next_power_of_two(val): np = 1 while np < val: np *= 2 return np # # dynamic_plot_init() # def dynamic_plot_init(real_time=True): if real_time: lines = [] fig, axs = plt.subplots(4, figsize=(19, 10)) fig.suptitle('Revalidator Handling') for ax in axs: ax.grid() axs[0].set_ylabel("Numer of flows", weight='bold') axs[1].set_ylabel("Time spend (ms)", weight='bold') axs[2].set_ylabel("Numer of flows", weight='bold') axs[3].set_ylabel("Time spend (ms)", weight='bold') axs[3].set_xlabel("Time (seconds since start)", weight='bold') lines.append(axs[0].plot([], [], label="n_flows", marker='o')[0]) lines.append(axs[0].plot([], [], label="n_reval_flows")[0]) axs[0].legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) axs[0].set_xlim(0, 30) axs[0].set_ylim(-4, 104) lines.append(axs[1].plot([], [], color="orange", label="dump_duration")[0]) axs[1].legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) axs[1].set_xlim(0, 30) axs[1].set_ylim(-0.4, 10.4) lines.append(axs[2].plot([], [], label="avg_n_flows")[0]) lines.append(axs[2].plot([], [], label="max_n_flows")[0]) lines.append(axs[2].plot([], [], label="flow_limit")[0]) axs[2].legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) axs[2].set_xlim(0, 30) axs[2].set_ylim(-600, 15600) lines.append(axs[3].plot([], [], label="poll_wait")[0]) lines.append(axs[3].plot([], [], label="actual_wait")[0]) axs[3].legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) axs[3].set_xlim(0, 30) axs[3].set_ylim(-20, 520) fig.tight_layout() plt.ion() plt.show() else: fig = None axs = None lines = None graph_data = {"base_time": 0, "l_index": 0, "fig": fig, "axs": axs, "lines": lines, "last_update": 0, "ukey_count": 0, "time": [], "n_flows": [], "n_reval_flows": [], "avg_n_flows": [], "max_n_flows": [], "flow_limit": [], "dump_duration": [], "poll_wait": [], "actual_wait": []} return namedtuple("GraphData", graph_data.keys())(*graph_data.values()) # # dynamic_plot_update() # def dynamic_plot_update(graph_data, refresh=1): if graph_data.last_update != 0 and \ (graph_data.time[-1] - graph_data.last_update) < refresh: return graph_data graph_data = graph_data._replace(last_update=graph_data.time[-1]) if (graph_data.time[-1] - graph_data.time[graph_data.l_index]) > 30: for i in range(graph_data.l_index + 1, len(graph_data.time)): if (graph_data.time[-1] - graph_data.time[i]) <= 30: graph_data = graph_data._replace(l_index=i) break for line in graph_data.lines: line.set_xdata(graph_data.time[graph_data.l_index:]) graph_data.lines[0].set_ydata(graph_data.n_flows[graph_data.l_index:]) graph_data.lines[1].set_ydata( graph_data.n_reval_flows[graph_data.l_index:]) graph_data.lines[2].set_ydata( graph_data.dump_duration[graph_data.l_index:]) graph_data.lines[3].set_ydata(graph_data.avg_n_flows[graph_data.l_index:]) graph_data.lines[4].set_ydata(graph_data.max_n_flows[graph_data.l_index:]) graph_data.lines[5].set_ydata(graph_data.flow_limit[graph_data.l_index:]) graph_data.lines[6].set_ydata(graph_data.poll_wait[graph_data.l_index:]) graph_data.lines[7].set_ydata(graph_data.actual_wait[graph_data.l_index:]) for ax in graph_data.axs: if graph_data.l_index == 0: ax.autoscale(enable=True, axis='y') else: ax.autoscale(enable=True) ax.relim(visible_only=True) ax.autoscale_view(tight=True, scalex=True, scaley=True) try: graph_data.fig.canvas.draw() graph_data.fig.canvas.flush_events() except KeyboardInterrupt: return None return graph_data # # show_graph() # def show_graph(graph_data, gui=False, file_name=None): if len(graph_data.time) == 0 or (not gui and file_name is None): return plt.ioff() fig, (nf_ax, dd_ax, f_ax, t_ax) = plt.subplots(4, figsize=(19, 10)) fig.suptitle('Revalidator Handling') nf_ax.grid() f_ax.grid() dd_ax.grid() t_ax.grid() nf_ax.set_ylabel("Numer of flows", weight='bold') f_ax.set_ylabel("Numer of flows", weight='bold') dd_ax.set_ylabel("Time spend (ms)", weight='bold') t_ax.set_ylabel("Time spend (ms)", weight='bold') t_ax.set_xlabel("Time (seconds since start)", weight='bold') nf_ax.plot(graph_data.time, graph_data.n_flows, label="n_flows") nf_ax.plot(graph_data.time, graph_data.n_reval_flows, label="n_reval_flows") nf_ax.legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) dd_ax.plot(graph_data.time, graph_data.dump_duration, color="orange", label="dump_duration") dd_ax.legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) f_ax.plot(graph_data.time, graph_data.avg_n_flows, label="avg_n_flows") f_ax.plot(graph_data.time, graph_data.max_n_flows, label="max_n_flows") f_ax.plot(graph_data.time, graph_data.flow_limit, label="flow_limit") f_ax.legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) t_ax.plot(graph_data.time, graph_data.poll_wait, label="poll_wait") t_ax.plot(graph_data.time, graph_data.actual_wait, label="actual_wait") t_ax.legend(bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0.5) fig.tight_layout() if file_name is not None and file_name != "": fig.savefig(file_name + '.png') if gui: try: plt.show() except KeyboardInterrupt: pass plt.close(fig) # # process_events_from_file() # def process_events_from_file(file_name): try: with open(file_name, 'r') as fd: print("- Reading events from \"{}\"...".format(file_name)) print_csv_header() for entry in fd: entry.rstrip() if entry.startswith('event = {'): event = ast.literal_eval(entry[8:]) event = namedtuple("EventObject", event.keys())(*event.values()) try: _process_event(event) except KeyboardInterrupt: break except (FileNotFoundError, PermissionError): print("ERROR: Can't open file \"{}\" for reading!".format(file_name)) sys.exit(-1) show_graph(graph, gui=not options.no_gui, file_name=options.write_charts) # # main() # def main(): # # Don't like these globals, but ctx passing does not seem to work with the # existing open_ring_buffer() API :( # global b global export_file global options global state global graph # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument("-c", "--compress-output", action="store_true", help="Compress output, i.e. only dump changes in " "the dataset") parser.add_argument("--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=int, default=1024, metavar="NUMBER") parser.add_argument("-D", "--debug", help="Enable eBPF debugging", type=int, const=0x3f, default=0, nargs='?') parser.add_argument("-g", "--no-gui", action="store_true", help="Do not use the gui to display plots") parser.add_argument("--no-ukey-count", action="store_true", help="No revalidate_ukey() counting") parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitch's PID", type=int, default=None) parser.add_argument("-P", "--pahole", metavar="PAHOLE", help="Pahole executable to use, default pahole", type=str, default="pahole") parser.add_argument("-r", "--read-events", help="Read events from instead of installing " "tracepoints", type=str, default=None, metavar="FILE") parser.add_argument("-R", "--no-realtime-plots", action="store_true", help="Do not show realtime plot while tracing") parser.add_argument("-u", "--update-interval", help="Seconds to wait between real time update, " "default 1", type=float, default=1, metavar="SECONDS") parser.add_argument("-w", "--write-events", help="Write events to ", type=str, default=None, metavar="FILE") parser.add_argument("-W", "--write-charts", help="Write overall charts to .png", type=str, default=None, metavar="FILE") options = parser.parse_args() # # Find the PID of the ovs-vswitchd daemon if not specified. # if options.pid is None and options.read_events is None: for proc in psutil.process_iter(): if 'ovs-vswitchd' in proc.name(): if options.pid is not None: print("ERROR: Multiple ovs-vswitchd daemons running, " "use the -p option!") sys.exit(-1) options.pid = proc.pid # # Error checking on input parameters. # if options.pid is None and options.read_events is None: print("ERROR: Failed to find ovs-vswitchd's PID!") sys.exit(-1) if options.read_events is not None and options.write_events is not None: print("ERROR: Either supply the read or write events option, " "not both!") sys.exit(-1) options.buffer_page_count = next_power_of_two(options.buffer_page_count) # # Define the state and graph. # state = {"last_start": None, "last_done": None, "running": False, "last_csv": None, "last_not_printed_csv": None} export_file = None graph = dynamic_plot_init(real_time=(not options.no_gui and not options.no_realtime_plots)) # # Process events from file if required. # if options.read_events is not None: process_events_from_file(options.read_events) sys.exit(0) # # Open write handle if needed. # if options.write_events is not None: try: export_file = open(options.write_events, "w") except (FileNotFoundError, IOError, PermissionError) as e: print("ERROR: Can't create export file \"{}\": {}".format( options.write_events, e.strerror)) sys.exit(-1) # # Attach the usdt probe. # u = USDT(pid=int(options.pid)) try: u.enable_probe(probe="start_dump", fn_name="probe__start_dump") u.enable_probe(probe="sweep_done", fn_name="probe__sweep_done") if not options.no_ukey_count: u.enable_probe(probe="revalidate_ukey__:entry", fn_name="probe__reval_entry") except USDTException as e: print("ERROR: {}".format( (re.sub('^', ' ' * 7, str(e), flags=re.MULTILINE)).strip(). replace("--with-dtrace or --enable-dtrace", "--enable-usdt-probes"))) sys.exit(-1) # # Attach probe to running process. # source = EBPF_SOURCE.replace("", "\n".join( [" EVENT_{} = {},".format( event.name, event.value) for event in Event])) source = source.replace("", str(options.buffer_page_count)) source = source.replace("", get_ovs_definitions("udpif", pid=options.pid, pahole=options.pahole)) b = BPF(text=source, usdt_contexts=[u], debug=options.debug) # # Print header. # ltz = datetime.datetime.now() utc = ltz.astimezone(pytz.utc) time_string = "# Starting trace @{} ({} UTC)".format( ltz.isoformat(), utc.strftime("%H:%M:%S")) if export_file is not None: export_file.write(time_string + "\n") print(time_string) print_csv_header() # # Process all events. b['events'].open_ring_buffer(process_event) while 1: try: b.ring_buffer_poll() except KeyboardInterrupt: break dropcnt = b.get_table("dropcnt") for k in dropcnt.keys(): count = dropcnt.sum(k).value if k.value == 0 and count > 0: print("\n# WARNING: Not all upcalls were captured, {} were " "dropped!\n# Increase the BPF ring buffer size " "with the --buffer-page-count option.".format(count)) # # Display footer. # if state["last_not_printed_csv"] is not None: print(state["last_not_printed_csv"]) ltz = datetime.datetime.now() utc = ltz.astimezone(pytz.utc) time_string = "# Stopping trace @{} ({} UTC)".format( ltz.isoformat(), utc.strftime("%H:%M:%S")) if export_file is not None: export_file.write(time_string + "\n") print(time_string) # # Close event file is used. # if options.write_events is not None: export_file.close() # # Do final graph if requested. # show_graph(graph, gui=not options.no_gui, file_name=options.write_charts) # # Start main() as the default entry point... # if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/upcall_cost.py000077500000000000000000001752151514270232600263370ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2021 Red Hat, Inc. # # 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. # # Script information: # ------------------- # upcall_cost.py uses various user space and kernel space probes to determine # the costs (in time) for handling the first packet in user space. It # calculates the following costs: # # - Time it takes from the kernel sending the upcall till it's received by the # ovs-vswitchd process. # - Time it takes from ovs-vswitchd sending the execute actions command till # the kernel receives it. # - The total time it takes from the kernel to sent the upcall until it # receives the packet execute command. # - The total time of the above, minus the time it takes for the actual lookup. # # In addition, it will also report the number of packets batched, as OVS will # first try to read UPCALL_MAX_BATCH(64) packets from kernel space and then # does the flow lookups and execution. So the smaller the batch size, the more # realistic are the cost estimates. # # The script does not need any options to attach to a running instance of # ovs-vswitchd. However, it's recommended always run the script with the # --write-events option. This way, if something does go wrong, the collected # data is saved. Use the --help option to see all the available options. # # Note: In addition to the bcc tools for your specific setup, you need the # following Python packages: # pip install alive-progress halo psutil scapy strenum text_histogram3 # try: from bcc import BPF, USDT, USDTException except ModuleNotFoundError: print("WARNING: Can't find the BPF Compiler Collection (BCC) tools!") print(" This is NOT problem if you analyzing previously collected" " data.\n") from alive_progress import alive_bar from collections import namedtuple from halo import Halo from scapy.all import TCP, UDP from scapy.layers.l2 import Ether from strenum import StrEnum from text_histogram3 import histogram from time import process_time from usdt_lib import DpPortMapping import argparse import ast import psutil import re import struct import sys # # Global definitions # DP_TUNNEL_PORT = -1 # # Actual eBPF source code # ebpf_source = """ #include #include #include #define MAX_PACKET #define MAX_KEY enum { EVENT_RECV_UPCALL = 0, EVENT_DP_UPCALL, EVENT_OP_FLOW_PUT, EVENT_OP_FLOW_EXECUTE, EVENT_OVS_PKT_EXEC, _EVENT_MAX_EVENT }; #define barrier_var(var) asm volatile("" : "=r"(var) : "0"(var)) struct event_t { u32 event; u32 cpu; u32 pid; u32 upcall_type; u64 ts; u32 pkt_frag_size; u32 pkt_size; u64 key_size; char comm[TASK_COMM_LEN]; char dpif_name[32]; char dev_name[16]; unsigned char pkt[MAX_PACKET]; unsigned char key[MAX_KEY]; }; BPF_RINGBUF_OUTPUT(events, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, _EVENT_MAX_EVENT); static struct event_t *init_event(u32 type) { struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { uint64_t *value = dropcnt.lookup(&type); if (value) __sync_fetch_and_add(value, 1); return NULL; } event->event = type; event->ts = bpf_ktime_get_ns(); event->cpu = bpf_get_smp_processor_id(); event->pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&event->comm, sizeof(event->comm)); return event; } int trace__recv_upcall(struct pt_regs *ctx) { uint32_t upcall_type; uint64_t addr; uint64_t size; bpf_usdt_readarg(2, ctx, &upcall_type); if (upcall_type != 0) return 0; struct event_t *event = init_event(EVENT_RECV_UPCALL); if (!event) return 1; bpf_usdt_readarg(1, ctx, &addr); bpf_probe_read_str(&event->dpif_name, sizeof(event->dpif_name), (void *)addr); event->upcall_type = upcall_type; bpf_usdt_readarg(4, ctx, &event->pkt_size); bpf_usdt_readarg(6, ctx, &event->key_size); if (event->pkt_size > MAX_PACKET) size = MAX_PACKET; else size = event->pkt_size; bpf_usdt_readarg(3, ctx, &addr); bpf_probe_read(&event->pkt, size, (void *)addr); if (event->key_size > MAX_KEY) size = MAX_KEY; else size = event->key_size; bpf_usdt_readarg(5, ctx, &addr); bpf_probe_read(&event->key, size, (void *)addr); events.ringbuf_submit(event, 0); return 0; }; int trace__op_flow_put(struct pt_regs *ctx) { uint64_t addr; uint64_t size; struct event_t *event = init_event(EVENT_OP_FLOW_PUT); if (!event) { return 1; } events.ringbuf_submit(event, 0); return 0; }; int trace__op_flow_execute(struct pt_regs *ctx) { uint64_t addr; uint64_t size; struct event_t *event = init_event(EVENT_OP_FLOW_EXECUTE); if (!event) { return 1; } bpf_usdt_readarg(4, ctx, &event->pkt_size); if (event->pkt_size > MAX_PACKET) size = MAX_PACKET; else size = event->pkt_size; bpf_usdt_readarg(3, ctx, &addr); bpf_probe_read(&event->pkt, size, (void *)addr); events.ringbuf_submit(event, 0); return 0; }; TRACEPOINT_PROBE(openvswitch, ovs_dp_upcall) { uint64_t size; struct sk_buff *skb = args->skbaddr; if (args->upcall_cmd != 1 || skb == NULL || skb->data == NULL) return 0; struct event_t *event = init_event(EVENT_DP_UPCALL); if (!event) { return 1; } event->upcall_type = args->upcall_cmd; event->pkt_size = args->len; TP_DATA_LOC_READ_CONST(&event->dpif_name, dp_name, sizeof(event->dpif_name)); TP_DATA_LOC_READ_CONST(&event->dev_name, dev_name, sizeof(event->dev_name)); if (skb->data_len != 0) { event->pkt_frag_size = (skb->len - skb->data_len) & 0xfffffff; size = event->pkt_frag_size; } else { event->pkt_frag_size = 0; size = event->pkt_size; } /* Prevent clang from using register mirroring (or any optimization) on * the 'size' variable. */ barrier_var(size); if (size > MAX_PACKET) size = MAX_PACKET; bpf_probe_read_kernel(event->pkt, size, skb->data); events.ringbuf_submit(event, 0); return 0; } int kprobe__ovs_packet_cmd_execute(struct pt_regs *ctx, struct sk_buff *skb) { uint64_t size; if (skb == NULL || skb->data == NULL) return 0; struct event_t *event = init_event(EVENT_OVS_PKT_EXEC); if (!event) { return 1; } events.ringbuf_submit(event, 0); return 0; } """ # # Event types # class EventType(StrEnum): RECV_UPCALL = 'dpif_recv__recv_upcall' DP_UPCALL = 'openvswitch__dp_upcall' OP_FLOW_PUT = 'dpif_netlink_operate__op_flow_put' OP_FLOW_EXECUTE = 'dpif_netlink_operate__op_flow_execute' OVS_PKT_EXEC = 'ktrace__ovs_packet_cmd_execute' def short_name(name, length=22): if len(name) < length: return name return '..' + name[-(length - 2):] def from_trace(trace_event): if trace_event == 0: return EventType.RECV_UPCALL elif trace_event == 1: return EventType.DP_UPCALL elif trace_event == 2: return EventType.OP_FLOW_PUT elif trace_event == 3: return EventType.OP_FLOW_EXECUTE elif trace_event == 4: return EventType.OVS_PKT_EXEC raise ValueError # # Simple event class # class Event(object): def __init__(self, ts, pid, comm, cpu, event_type): self.ts = ts self.pid = pid self.comm = comm self.cpu = cpu self.event_type = event_type def __str__(self): return "[{:<22}] {:<16} {:8} [{:03}] {:18.9f}".format( EventType.short_name(self.event_type), self.comm, self.pid, self.cpu, self.ts / 1000000000) def __repr__(self): more = "" if self.__class__.__name__ != "Event": more = ", ..." return "{}({}, {}, {}, {}, {}{})".format(self.__class__.__name__, self.ts, self.pid, self.comm, self.cpu, self.event_type, more) def handle_event(event): event = Event(event.ts, event.pid, event.comm.decode("utf-8"), event.cpu, EventType.from_trace(event.event)) if not options.quiet: print(event) return event def get_event_header_str(): return "{:<24} {:<16} {:>8} {:<3} {:<18} {}".format( "EVENT", "COMM", "PID", "CPU", "TIME", "EVENT DATA[dpif_name/dp_port/pkt_len/pkt_frag_len]") # # dp_upcall event class # class DpUpcall(Event): def __init__(self, ts, pid, comm, cpu, dpif_name, port, pkt, pkt_len, pkt_frag_len): super(DpUpcall, self).__init__(ts, pid, comm, cpu, EventType.DP_UPCALL) self.dpif_name = dpif_name self.dp_port = dp_map.get_port_number(dpif_name, port) if self.dp_port is None: # # As we only identify interfaces at startup, new interfaces could # have been added, causing the lookup to fail. Just something to # keep in mind when running this in a dynamic environment. # raise LookupError("Can't find datapath port mapping!") self.pkt = pkt self.pkt_len = pkt_len self.pkt_frag_len = pkt_frag_len def __str__(self): return "[{:<22}] {:<16} {:8} [{:03}] {:18.9f}: " \ "{:<17} {:4} {:4} {:4}".format(self.event_type, self.comm, self.pid, self.cpu, self.ts / 1000000000, self.dpif_name, self.dp_port, self.pkt_len, self.pkt_frag_len) def handle_event(event): if event.pkt_size < options.packet_size: pkt_len = event.pkt_size else: pkt_len = options.packet_size pkt_data = bytes(event.pkt)[:pkt_len] if len(pkt_data) <= 0 or event.pkt_size == 0: return try: event = DpUpcall(event.ts, event.pid, event.comm.decode("utf-8"), event.cpu, event.dpif_name.decode("utf-8"), event.dev_name.decode("utf-8"), pkt_data, event.pkt_size, event.pkt_frag_size) except LookupError: # # If we can't do the port lookup, ignore this event. # return None if not options.quiet: print(event) return event # # recv_upcall event class # class RecvUpcall(Event): def __init__(self, ts, pid, comm, cpu, dpif_name, key, pkt, pkt_len): super(RecvUpcall, self).__init__(ts, pid, comm, cpu, EventType.RECV_UPCALL) if dpif_name.startswith("system@"): dpif_name = dpif_name[len("system@"):] self.dpif_name = dpif_name nla = RecvUpcall.decode_nlm(key, dump=False) if "OVS_KEY_ATTR_IN_PORT" in nla: self.dp_port = struct.unpack('=L', nla["OVS_KEY_ATTR_IN_PORT"])[0] elif "OVS_KEY_ATTR_TUNNEL" in nla: self.dp_port = DP_TUNNEL_PORT else: self.dp_port = RecvUpcall.get_system_dp_port(self.dpif_name) if self.dp_port is None: raise LookupError("Can't find RecvUpcall dp port mapping!") self.pkt = pkt self.pkt_len = pkt_len def __str__(self): return "[{:<22}] {:<16} {:8} [{:03}] {:18.9f}: {:<17} {:4} {:4}". \ format( self.event_type, self.comm, self.pid, self.cpu, self.ts / 1000000000, self.dpif_name, self.dp_port, self.pkt_len) def get_system_dp_port(dpif_name): return dp_map.get_port_number(dpif_name, "ovs-system") def decode_nlm(msg, indent=4, dump=True): bytes_left = len(msg) result = {} while bytes_left: if bytes_left < 4: if dump: print("{}WARN: decode truncated; can't read header".format( ' ' * indent)) break nla_len, nla_type = struct.unpack("=HH", msg[:4]) if nla_len < 4: if dump: print("{}WARN: decode truncated; nla_len < 4".format( ' ' * indent)) break nla_data = msg[4:nla_len] trunc = "" if nla_len > bytes_left: trunc = "..." nla_data = nla_data[:(bytes_left - 4)] if RecvUpcall.get_ovs_key_attr_str(nla_type) == \ "OVS_KEY_ATTR_TUNNEL": # # If we have truncated tunnel information, we still would # like to know. This is due to the special tunnel handling # needed for port matching. # result[RecvUpcall.get_ovs_key_attr_str(nla_type)] = bytes() else: result[RecvUpcall.get_ovs_key_attr_str(nla_type)] = nla_data if dump: print("{}nla_len {}, nla_type {}[{}], data: {}{}".format( ' ' * indent, nla_len, RecvUpcall.get_ovs_key_attr_str(nla_type), nla_type, "".join("{:02x} ".format(b) for b in nla_data), trunc)) if trunc != "": if dump: print("{}WARN: decode truncated; nla_len > msg_len[{}] ". format(' ' * indent, bytes_left)) break # Update next offset, but make sure it's aligned correctly. next_offset = (nla_len + 3) & ~(3) msg = msg[next_offset:] bytes_left -= next_offset return result def get_ovs_key_attr_str(attr): ovs_key_attr = ["OVS_KEY_ATTR_UNSPEC", "OVS_KEY_ATTR_ENCAP", "OVS_KEY_ATTR_PRIORITY", "OVS_KEY_ATTR_IN_PORT", "OVS_KEY_ATTR_ETHERNET", "OVS_KEY_ATTR_VLAN", "OVS_KEY_ATTR_ETHERTYPE", "OVS_KEY_ATTR_IPV4", "OVS_KEY_ATTR_IPV6", "OVS_KEY_ATTR_TCP", "OVS_KEY_ATTR_UDP", "OVS_KEY_ATTR_ICMP", "OVS_KEY_ATTR_ICMPV6", "OVS_KEY_ATTR_ARP", "OVS_KEY_ATTR_ND", "OVS_KEY_ATTR_SKB_MARK", "OVS_KEY_ATTR_TUNNEL", "OVS_KEY_ATTR_SCTP", "OVS_KEY_ATTR_TCP_FLAGS", "OVS_KEY_ATTR_DP_HASH", "OVS_KEY_ATTR_RECIRC_ID", "OVS_KEY_ATTR_MPLS", "OVS_KEY_ATTR_CT_STATE", "OVS_KEY_ATTR_CT_ZONE", "OVS_KEY_ATTR_CT_MARK", "OVS_KEY_ATTR_CT_LABELS", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "OVS_KEY_ATTR_NSH"] if attr < 0 or attr > len(ovs_key_attr): return "" return ovs_key_attr[attr] def handle_event(event): # # For us, only upcalls with a packet, flow_key, and upcall_type # DPIF_UC_MISS are of interest. # if event.pkt_size <= 0 or event.key_size <= 0 or \ event.upcall_type != 0: return if event.key_size < options.flow_key_size: key_len = event.key_size else: key_len = options.flow_key_size if event.pkt_size < options.packet_size: pkt_len = event.pkt_size else: pkt_len = options.packet_size try: event = RecvUpcall(event.ts, event.pid, event.comm.decode("utf-8"), event.cpu, event.dpif_name.decode("utf-8"), bytes(event.key)[:key_len], bytes(event.pkt)[:pkt_len], event.pkt_size) except LookupError: return None if not options.quiet: print(event) return event # # op_flow_execute event class # class OpFlowExecute(Event): def __init__(self, ts, pid, comm, cpu, pkt, pkt_len): super(OpFlowExecute, self).__init__(ts, pid, comm, cpu, EventType.OP_FLOW_EXECUTE) self.pkt = pkt self.pkt_len = pkt_len def __str__(self): return "[{:<22}] {:<16} {:8} [{:03}] {:18.9f}: " \ "{:<17} {:4} {:4}".format(EventType.short_name(self.event_type), self.comm, self.pid, self.cpu, self.ts / 1000000000, "", "", self.pkt_len) def handle_event(event): if event.pkt_size < options.packet_size: pkt_len = event.pkt_size else: pkt_len = options.packet_size pkt_data = bytes(event.pkt)[:pkt_len] if len(pkt_data) <= 0 or event.pkt_size == 0: return event = OpFlowExecute(event.ts, event.pid, event.comm.decode("utf-8"), event.cpu, pkt_data, event.pkt_size) if not options.quiet: print(event) return event # # event_to_dict() # def event_to_dict(event): event_dict = {} for field, _ in event._fields_: if isinstance(getattr(event, field), (int, bytes)): event_dict[field] = getattr(event, field) else: if (field == "key" and event.key_size == 0) or \ (field == "pkt" and event.pkt_size == 0): data = bytes() else: data = bytes(getattr(event, field)) event_dict[field] = data return event_dict # # receive_event_bcc() # def receive_event_bcc(ctx, data, size): global events_received events_received += 1 event = b['events'].event(data) if export_file is not None: export_file.write("event = {}\n".format(event_to_dict(event))) receive_event(event) # # receive_event() # def receive_event(event): global event_count if event.event == 0: trace_event = RecvUpcall.handle_event(event) elif event.event == 1: trace_event = DpUpcall.handle_event(event) elif event.event == 2: trace_event = Event.handle_event(event) elif event.event == 3: trace_event = OpFlowExecute.handle_event(event) elif event.event == 4: trace_event = Event.handle_event(event) try: event_count['total'][EventType.from_trace(event.event)] += 1 except KeyError: event_count['total'][EventType.from_trace(event.event)] = 1 event_count['valid'][EventType.from_trace(event.event)] = 0 if trace_event is not None: event_count['valid'][EventType.from_trace(event.event)] += 1 trace_data.append(trace_event) # # collect_event_sets() # def collect_event_sets(events, collect_stats=False, profile=False, spinner=False): t1_time = 0 def t1_start(): nonlocal t1_time t1_time = process_time() def t1_stop(description): print("* PROFILING: {:<50}: {:.06f} seconds".format( description, process_time() - t1_time)) warn_parcial_match = False warn_frag = False if profile: t1_start() # # First let's create a dict of per handler thread events. # threads = {} threads_result = {} for idx, event in enumerate(events): if event.event_type == EventType.DP_UPCALL: continue if event.pid not in threads: threads[event.pid] = [] threads[event.pid].append([idx, event]) if profile: t1_stop("Creating per thread dictionary") t1_start() # # Now spit them in per upcall sets, but remember that # RecvUpcall event can be batched. # batch_stats = [] for thread, items in threads.items(): thread_set = [] batch = [] ovs_pkt_exec_set = [] batching = True collecting = 0 has_flow_put = False has_flow_exec = False def next_batch(): nonlocal batching, batch, collecting, has_flow_put, has_flow_exec nonlocal ovs_pkt_exec_set, thread_set if len(batch) > 0: # # If we are done with the batch, see if we need to match up # any batched OVS_PKT_EXEC events. # for event in batch: if len(ovs_pkt_exec_set) <= 0: break if any(isinstance(item, OpFlowExecute) for item in event[2]): event[2].append(ovs_pkt_exec_set.pop(0)) # # Append the batch to the thread-specific set. # thread_set = thread_set + batch if collect_stats: batch_stats.append(len(batch)) batching = True batch = [] ovs_pkt_exec_set = [] has_flow_put = False has_flow_exec = False collecting = 0 def next_batch_set(): nonlocal has_flow_put, has_flow_exec, collecting has_flow_put = False has_flow_exec = False collecting += 1 for item in items: idx, event = item if batching: if event.event_type == EventType.RECV_UPCALL: batch.append(item + [[]]) elif len(batch) > 0: batching = False collecting = 0 else: continue if not batching: if event.event_type == EventType.RECV_UPCALL: next_batch() batch.append(item + [[]]) else: if event.event_type == EventType.OP_FLOW_PUT: if has_flow_put: next_batch_set() if collecting >= len(batch): next_batch() continue batch[collecting][2].append(item[1]) has_flow_put = True elif event.event_type == EventType.OP_FLOW_EXECUTE: if has_flow_exec: next_batch_set() if collecting >= len(batch): next_batch() continue if (event.pkt_len == batch[collecting][1].pkt_len and event.pkt == batch[collecting][1].pkt): batch[collecting][2].append(item[1]) has_flow_put = True has_flow_exec = True else: # # If we end up here it could be that an upcall in a # batch did not generate an EXECUTE and we are out # of sync. Try to match it to the next batch entry. # next_idx = collecting + 1 while True: if next_idx >= len(batch): next_batch() break if (event.pkt_len == batch[next_idx][1].pkt_len and event.pkt == batch[next_idx][1].pkt): batch[next_idx][2] = batch[collecting][2] batch[collecting][2] = [] collecting = next_idx batch[collecting][2].append(item[1]) has_flow_put = True has_flow_exec = True break next_idx += 1 elif event.event_type == EventType.OVS_PKT_EXEC: # # The OVS_PKT_EXEC might also be batched, so we keep # them in a separate list and assign them to the # correct set when completing the set. # ovs_pkt_exec_set.append(item[1]) continue if collecting >= len(batch): next_batch() next_batch() threads_result[thread] = thread_set if profile: t1_stop("Creating upcall sets") t1_start() # # Move thread results from list to dictionary # thread_stats = {} for thread, sets in threads_result.items(): if len(sets) > 0: thread_stats[sets[0][1].comm] = len(sets) threads_result[thread] = {} for upcall in sets: threads_result[thread][upcall[0]] = [upcall[1]] + upcall[2] if profile: t1_stop("Moving upcall list to dictionary") t1_start() if options.debug & 0x4000000 != 0: print() for thread, sets in threads_result.items(): for idx, idx_set in sets.items(): print("DBG: {}".format(idx_set)) # # Create two lists on with DP_UPCALLs and RECV_UPCALLs # dp_upcall_list = [] recv_upcall_list = [] for idx, event in enumerate(events): if event.event_type == EventType.DP_UPCALL: dp_upcall_list.append([idx, event]) elif event.event_type == EventType.RECV_UPCALL: recv_upcall_list.append([idx, event]) if profile: t1_stop("Creating DP_UPCALL and RECV_UPCALL lists") t1_start() if options.debug & 0x4000000 != 0: print() for dp_upcall in dp_upcall_list: print("DBG: {}".format(dp_upcall)) print() for recv_upcall in recv_upcall_list: print("DBG: {}".format(recv_upcall)) # # Now find the matching DP_UPCALL and RECV_UPCALL events # event_sets = [] if spinner: print() with alive_bar(len(dp_upcall_list), title="- Matching DP_UPCALLs to RECV_UPCALLs", spinner=None, disable=not spinner) as bar: for (idx, event) in dp_upcall_list: remove_indexes = [] this_set = None # # TODO: This part needs some optimization, as it's slow in the # PVP test scenario. This is because a lot of DP_UPCALLS # will not have a matching RECV_UPCALL leading to walking # the entire recv_upcall_list list. # # Probably some dictionary, but in the PVP scenario packets # come from a limited set of ports, and the length is all the # same. So we do need the key to be recv.dport + # len(recv.pkt) + recv.pkt, however, the recv.pkt compare # needs to happen on min(len(event.pkt), len(recv.pkt)). # for idx_in_list, (idx_recv, recv) in enumerate(recv_upcall_list): match = False if idx_recv < idx: remove_indexes.append(idx_in_list) continue # # If the RecvUpcall is a tunnel port, we can not map it to # the correct tunnel. For now, we assume the first matching # packet is the correct one. For more details see the OVS # ukey_to_flow_netdev() function. # if (event.dp_port == recv.dp_port or recv.dp_port == DP_TUNNEL_PORT) \ and event.pkt_len == recv.pkt_len: compare_len = min(len(event.pkt), len(recv.pkt)) if len(event.pkt) != len(recv.pkt) \ and event.pkt_frag_len == 0: warn_parcial_match = True elif event.pkt_frag_len != 0: warn_frag = True compare_len = min(compare_len, event.pkt_frag_len) if event.pkt[:compare_len] == recv.pkt[:compare_len]: match = True else: # # There are still some corner cases due to the fact # the kernel dp_upcall tracepoint is hit before the # packet is prepared/modified for upcall pass on. # Example cases are packet checksum update, VLAN # insertion, etc., etc. For now, we try to handle the # checksum part, but we might need to add more # exceptions in the future. # diff_bytes = sum(i != j for i, j in zip( event.pkt[:compare_len], recv.pkt[:compare_len])) if diff_bytes <= 2 and compare_len > 56: # This could be a TCP or UDP checksum event_pkt = Ether(bytes(event.pkt)[:compare_len]) recv_pkt = Ether(bytes(recv.pkt)[:compare_len]) if (event_pkt.haslayer(TCP) and recv_pkt.haslayer(TCP)) or ( event_pkt.haslayer(UDP) and recv_pkt.haslayer(UDP)): if event_pkt.haslayer(TCP): event_chksum = event_pkt[TCP].chksum recv_chksum = recv_pkt[TCP].chksum else: event_chksum = event_pkt[UDP].chksum recv_chksum = recv_pkt[UDP].chksum if event_chksum & 0xff != recv_chksum & 0xff: diff_bytes -= 1 if event_chksum & 0xff00 != \ recv_chksum & 0xff00: diff_bytes -= 1 if diff_bytes == 0: match = True if match: this_set = {event.event_type: event} for sevent in threads_result[recv.pid][idx_recv]: this_set[sevent.event_type] = sevent event_sets.append(this_set) remove_indexes.append(idx_in_list) if options.debug & 0x4000000 != 0: print("DBG: Matched DpUpcall({:6}) => " "RecvUpcall({:6})".format(idx, idx_recv)) break elif options.debug & 0x8000000 != 0: print("DBG: COMPARE DpUpcall({:6}) != " "RecvUpcall({:6})".format(idx, idx_recv)) event_pkt = Ether(bytes(event.pkt)[:compare_len]) recv_pkt = Ether(bytes(recv.pkt)[:compare_len]) print(re.sub('^', 'DBG:' + ' ' * 4, event_pkt.show(dump=True), flags=re.MULTILINE)) print(re.sub('^', 'DBG:' + ' ' * 4, recv_pkt.show(dump=True), flags=re.MULTILINE)) elif options.debug & 0x8000000 != 0: print("DBG: COMPATE DpUpcall({:6}) != " "RecvUpcall({:6}) -> port {}, {} -> " "len = {}, {}".format(idx, idx_recv, event.dp_port, recv.dp_port, event.pkt_len, recv.pkt_len)) bar() for remove_idx in sorted(remove_indexes, reverse=True): del recv_upcall_list[remove_idx] if profile: t1_stop("Matching DP_UPCALLs to a set") if warn_parcial_match: print("WARNING: Packets not fully captured for matching!\n " "Increase the packet buffer with the '--packet-size' option.") if warn_frag: print("WARNING: SKB from kernel had fragments, we could only copy/" "compare the first part!") if collect_stats: return event_sets, batch_stats, thread_stats return event_sets # # unit_test() # def unit_test(): pkt1 = b'\x01\x02\x03\x04\x05' pkt2 = b'\x01\x02\x03\x04\x06' pkt3 = b'\x01\x02\x03\x04\x07' key = b'\x08\x00\x03\x00\x01\x00\x00\x00' # Port 1 # # Basic test with all events in line # t1_events = [DpUpcall(1, 100, "ping", 1, "system", 1, pkt1, len(pkt1), 0), RecvUpcall(2, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), Event(3, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(4, 1, "hndl", 1, pkt1, len(pkt1)), Event(5, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t1_result = [{EventType.DP_UPCALL: t1_events[0], EventType.RECV_UPCALL: t1_events[1], EventType.OP_FLOW_PUT: t1_events[2], EventType.OP_FLOW_EXECUTE: t1_events[3], EventType.OVS_PKT_EXEC: t1_events[4]}] # # Basic test with missing flow put # t2_events = [DpUpcall(1, 100, "ping", 1, "system", 1, pkt1, len(pkt1), 0), RecvUpcall(2, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), OpFlowExecute(4, 1, "hndl", 1, pkt1, len(pkt1)), Event(5, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t2_result = [{EventType.DP_UPCALL: t2_events[0], EventType.RECV_UPCALL: t2_events[1], EventType.OP_FLOW_EXECUTE: t2_events[2], EventType.OVS_PKT_EXEC: t2_events[3]}] # # Test with RecvUpcall's being batched # t3_events = [DpUpcall(1, 101, "ping", 1, "system", 1, pkt1, len(pkt1), 0), DpUpcall(2, 102, "ping", 2, "system", 1, pkt2, len(pkt2), 0), DpUpcall(3, 101, "ping", 3, "system", 1, pkt3, len(pkt3), 0), RecvUpcall(4, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), RecvUpcall(5, 1, "hndl", 1, "systen", key, pkt3, len(pkt3)), RecvUpcall(6, 1, "hndl", 1, "systen", key, pkt2, len(pkt2)), Event(7, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(8, 1, "hndl", 1, pkt1, len(pkt1)), Event(9, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(10, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(11, 1, "hndl", 1, pkt3, len(pkt3)), Event(12, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(13, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(14, 1, "hndl", 1, pkt2, len(pkt2)), Event(15, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t3_result = [{EventType.DP_UPCALL: t3_events[0], EventType.RECV_UPCALL: t3_events[3], EventType.OP_FLOW_PUT: t3_events[6], EventType.OP_FLOW_EXECUTE: t3_events[7], EventType.OVS_PKT_EXEC: t3_events[8]}, {EventType.DP_UPCALL: t3_events[1], EventType.RECV_UPCALL: t3_events[5], EventType.OP_FLOW_PUT: t3_events[12], EventType.OP_FLOW_EXECUTE: t3_events[13], EventType.OVS_PKT_EXEC: t3_events[14]}, {EventType.DP_UPCALL: t3_events[2], EventType.RECV_UPCALL: t3_events[4], EventType.OP_FLOW_PUT: t3_events[9], EventType.OP_FLOW_EXECUTE: t3_events[10], EventType.OVS_PKT_EXEC: t3_events[11]}] # # Test with RecvUpcall's single + batch # t4_events = [DpUpcall(1, 100, "ping", 1, "system", 1, pkt1, len(pkt1), 0), RecvUpcall(2, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), Event(3, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(4, 1, "hndl", 1, pkt1, len(pkt1)), Event(5, 1, "hndl", 1, EventType.OVS_PKT_EXEC), DpUpcall(6, 101, "ping", 1, "system", 1, pkt1, len(pkt1), 0), DpUpcall(7, 102, "ping", 2, "system", 1, pkt2, len(pkt2), 0), DpUpcall(8, 101, "ping", 3, "system", 1, pkt3, len(pkt3), 0), RecvUpcall(9, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), RecvUpcall(10, 1, "hndl", 1, "systen", key, pkt3, len(pkt3)), RecvUpcall(11, 1, "hndl", 1, "systen", key, pkt2, len(pkt2)), Event(12, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(13, 1, "hndl", 1, pkt1, len(pkt1)), Event(14, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(15, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(16, 1, "hndl", 1, pkt3, len(pkt3)), Event(17, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(18, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(14, 1, "hndl", 1, pkt2, len(pkt2)), Event(19, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t4_result = [{EventType.DP_UPCALL: t4_events[0], EventType.RECV_UPCALL: t4_events[1], EventType.OP_FLOW_PUT: t4_events[2], EventType.OP_FLOW_EXECUTE: t4_events[3], EventType.OVS_PKT_EXEC: t4_events[4]}, {EventType.DP_UPCALL: t4_events[5], EventType.RECV_UPCALL: t4_events[8], EventType.OP_FLOW_PUT: t4_events[11], EventType.OP_FLOW_EXECUTE: t4_events[12], EventType.OVS_PKT_EXEC: t4_events[13]}, {EventType.DP_UPCALL: t4_events[6], EventType.RECV_UPCALL: t4_events[10], EventType.OP_FLOW_PUT: t4_events[17], EventType.OP_FLOW_EXECUTE: t4_events[18], EventType.OVS_PKT_EXEC: t4_events[19]}, {EventType.DP_UPCALL: t4_events[7], EventType.RECV_UPCALL: t4_events[9], EventType.OP_FLOW_PUT: t4_events[14], EventType.OP_FLOW_EXECUTE: t4_events[15], EventType.OVS_PKT_EXEC: t4_events[16]}] # # Test with two threads interleaved # t5_events = [DpUpcall(1, 100, "ping", 1, "system", 1, pkt1, len(pkt1), 0), DpUpcall(2, 100, "ping", 1, "system", 1, pkt2, len(pkt2), 0), RecvUpcall(3, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), RecvUpcall(4, 2, "hndl", 2, "systen", key, pkt2, len(pkt2)), Event(5, 2, "hndl", 2, EventType.OP_FLOW_PUT), Event(6, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(7, 2, "hndl", 1, pkt2, len(pkt2)), OpFlowExecute(8, 1, "hndl", 1, pkt1, len(pkt1)), Event(9, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(10, 2, "hndl", 1, EventType.OVS_PKT_EXEC)] t5_result = [{EventType.DP_UPCALL: t5_events[0], EventType.RECV_UPCALL: t5_events[2], EventType.OP_FLOW_PUT: t5_events[5], EventType.OP_FLOW_EXECUTE: t5_events[7], EventType.OVS_PKT_EXEC: t5_events[8]}, {EventType.DP_UPCALL: t5_events[1], EventType.RECV_UPCALL: t5_events[3], EventType.OP_FLOW_PUT: t5_events[4], EventType.OP_FLOW_EXECUTE: t5_events[6], EventType.OVS_PKT_EXEC: t5_events[9]}] # # Test batch with missing events # t6_events = [DpUpcall(1, 101, "ping", 1, "system", 1, pkt1, len(pkt1), 0), DpUpcall(2, 102, "ping", 2, "system", 1, pkt2, len(pkt2), 0), RecvUpcall(3, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), RecvUpcall(4, 1, "hndl", 1, "systen", key, pkt2, len(pkt2)), Event(5, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(6, 1, "hndl", 1, pkt2, len(pkt2)), Event(7, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t6_result = [{EventType.DP_UPCALL: t6_events[0], EventType.RECV_UPCALL: t6_events[2]}, {EventType.DP_UPCALL: t6_events[1], EventType.RECV_UPCALL: t6_events[3], EventType.OP_FLOW_PUT: t6_events[4], EventType.OP_FLOW_EXECUTE: t6_events[5], EventType.OVS_PKT_EXEC: t6_events[6]}] # # Test with RecvUpcall's and OVS_PKT_EXEC being batched # t7_events = [DpUpcall(1, 101, "ping", 1, "system", 1, pkt1, len(pkt1), 0), DpUpcall(2, 102, "ping", 2, "system", 1, pkt2, len(pkt2), 0), DpUpcall(3, 101, "ping", 3, "system", 1, pkt3, len(pkt3), 0), RecvUpcall(4, 1, "hndl", 1, "systen", key, pkt1, len(pkt1)), RecvUpcall(5, 1, "hndl", 1, "systen", key, pkt2, len(pkt2)), RecvUpcall(6, 1, "hndl", 1, "systen", key, pkt3, len(pkt3)), Event(7, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(8, 1, "hndl", 1, pkt1, len(pkt1)), Event(9, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(10, 1, "hndl", 1, pkt2, len(pkt2)), Event(11, 1, "hndl", 1, EventType.OP_FLOW_PUT), OpFlowExecute(12, 1, "hndl", 1, pkt3, len(pkt3)), Event(13, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(14, 1, "hndl", 1, EventType.OVS_PKT_EXEC), Event(15, 1, "hndl", 1, EventType.OVS_PKT_EXEC)] t7_result = [{EventType.DP_UPCALL: t7_events[0], EventType.RECV_UPCALL: t7_events[3], EventType.OP_FLOW_PUT: t7_events[6], EventType.OP_FLOW_EXECUTE: t7_events[7], EventType.OVS_PKT_EXEC: t7_events[12]}, {EventType.DP_UPCALL: t7_events[1], EventType.RECV_UPCALL: t7_events[4], EventType.OP_FLOW_PUT: t7_events[8], EventType.OP_FLOW_EXECUTE: t7_events[9], EventType.OVS_PKT_EXEC: t7_events[13]}, {EventType.DP_UPCALL: t7_events[2], EventType.RECV_UPCALL: t7_events[5], EventType.OP_FLOW_PUT: t7_events[10], EventType.OP_FLOW_EXECUTE: t7_events[11], EventType.OVS_PKT_EXEC: t7_events[14]}] # # Actual test sets # test_set = [["Simple single event", t1_events, t1_result], ["Single event, missing flow_put", t2_events, t2_result], ["Batched events", t3_events, t3_result], ["Single + batched events", t4_events, t4_result], ["Two sets, different threads", t5_events, t5_result], ["Batch with missing exec", t6_events, t6_result], ["Batched events including exec", t7_events, t7_result]] print("Running some simple unit tests:") for test in test_set: print("- {:<32} ".format(test[0]), end="") result = collect_event_sets(test[1][:]) if result == test[2]: print("PASS") else: print("FAIL") print(" OUTPUT :") for event_set in result: hdr = " - " for event_type, event in event_set.items(): print("{} {:<16}: {}".format(hdr, event_type.name, event)) hdr = " " print(" EXPECTED:") for event_set in test[2]: hdr = " - " for event_type, event in event_set.items(): print("{} {:<16}: {}".format(hdr, event_type.name, event)) hdr = " " # # show_key_value() # def show_key_value(data_set, description=None): if description is not None: print("\n=> {}:".format(description)) for k, v in data_set.items(): print(" {:36}: {:>10}".format(str(k), str(v))) # # show_batch_histogram() # def show_batch_histogram(data_set, description=None): nr_of_buckets = 64 if description is not None: print("\n=> {}:".format(description)) if len(data_set) == 0: print("# NumSamples = 0") return min_val = nr_of_buckets max_val = 0 entries = 0 high_buckets = 0 buckets = [0] * nr_of_buckets for entry in data_set: min_val = min(min_val, entry) max_val = max(max_val, entry) if entry == 0: continue elif entry > nr_of_buckets: high_buckets += 1 else: buckets[entry - 1] += 1 entries += 1 if max(buckets + [high_buckets]) > 4: scale = int(max(buckets + [high_buckets]) / 4) else: scale = 1 print("# NumSamples = {}; Min = {}; Max = {}".format(entries, min_val, max_val)) print("# each ∎ represents a count of {}".format(scale)) for idx in range(int(nr_of_buckets / 2)): idx_2nd = idx + int(nr_of_buckets / 2) print("{:5} [{:8}]: {:22} {:5} [{:8}]: {:22}".format( idx + 1, buckets[idx], "∎" * int(buckets[idx] / scale), idx_2nd + 1, buckets[idx_2nd], "∎" * int(buckets[idx_2nd] / scale))) if high_buckets > 0: print("{:>5} [{:8}]: {:22}".format(">" + str(nr_of_buckets), high_buckets, "∎" * int(high_buckets / scale))) # # show_histogram() # def show_histogram(data_set, description=None, options=None, minimum=None, maximum=None, buckets=None, custbuckets=None): if description is not None: print("\n=> {}:".format(description)) if options is not None: if buckets is None: buckets = options.histogram_buckets if options is not None and options.sets: print(data_set) if len(data_set) == 0: print("# NumSamples = 0") elif len(data_set) == 1: print("# NumSamples = 1; Min = {0:.4f}; Max = {0:.4f}". format(data_set[0])) elif len(set(data_set)) == 1 and maximum is None and minimum is None and \ custbuckets is None: histogram(data_set, buckets=buckets, minimum=list(set(data_set))[0], maximum=list(set(data_set))[0] + 1) else: histogram(data_set, buckets=buckets, minimum=minimum, maximum=maximum, custbuckets=custbuckets) # # buffer_size_type() # def buffer_size_type(astr, min=64, max=2048): value = int(astr) if min <= value <= max: return value else: raise argparse.ArgumentTypeError( 'value not in range {}-{}'.format(min, max)) # # next_power_of_two() # def next_power_of_two(val): np = 1 while np < val: np *= 2 return np # # main() # def main(): # # Don't like these globals, but ctx passing does not seem to work with the # existing open_ring_buffer() API :( # global b global options global trace_data global events_received global event_count global export_file global dp_map dp_map = DpPortMapping() # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument("-b", "--histogram-buckets", help="Number of buckets per histogram, default 20", type=int, default=20, metavar="BUCKETS") parser.add_argument("--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=int, default=1024, metavar="NUMBER") parser.add_argument("-D", "--debug", help="Enable eBPF debugging", type=lambda x: int(x, 0), const=0x3f, default=0, nargs='?') parser.add_argument("-f", "--flow-key-size", help="Set maximum flow key size to capture, " "default 64", type=buffer_size_type, default=64, metavar="[64-2048]") parser.add_argument("--handler-filter", help="Post processing handler thread filter", type=str, default=None, metavar="HANDLERS") parser.add_argument("-P", "--packet-size", help="Set maximum packet size to capture, " "default 256", type=buffer_size_type, default=256, metavar="[64-2048]") parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitch's PID", type=int, default=None) parser.add_argument("-q", "--quiet", action="store_true", help="Do not show individual events") parser.add_argument("-r", "--read-events", help="Read events from FILE instead of installing " "tracepoints", type=str, default=None, metavar="FILE") parser.add_argument("--sets", action="store_true", help="Dump content of data sets") parser.add_argument("-s", "--stop", help="Stop after receiving EVENTS number of trace " "events", type=int, default=0, metavar="EVENTS") parser.add_argument("--unit-test", action="store_true", help=argparse.SUPPRESS) parser.add_argument("-w", "--write-events", help="Write events to FILE", type=str, default=None, metavar="FILE") options = parser.parse_args() if options.unit_test: unit_test() sys.exit(0) # # Find the PID of the ovs-vswitchd daemon if not specified. # if options.pid is None and options.read_events is None: for proc in psutil.process_iter(): if 'ovs-vswitchd' in proc.name(): if options.pid is not None: print("ERROR: Multiple ovs-vswitchd daemons running, " "use the -p option!") sys.exit(-1) options.pid = proc.pid # # Error checking on input parameters. # if options.pid is None and options.read_events is None: print("ERROR: Failed to find ovs-vswitchd's PID!") sys.exit(-1) if options.read_events is not None and options.write_events is not None: print("ERROR: Either supply the read or write events option, " "not both!") sys.exit(-1) if options.handler_filter is not None and options.read_events is None: print("ERROR: The --handler-filter option is only valid with the " "--read-events option!") sys.exit(-1) options.buffer_page_count = next_power_of_two(options.buffer_page_count) # # Open write handle if needed. # if options.write_events is not None: try: export_file = open(options.write_events, "w") except (FileNotFoundError, IOError, PermissionError) as e: print("ERROR: Can't create export file \"{}\": {}".format( options.write_events, e.strerror)) sys.exit(-1) else: export_file = None trace_data = [] event_count = {'total': {}, 'valid': {}, 'miss': {}} if options.read_events is None: # # Prepare the datapath port mapping cache # dp_port_map = dp_map.get_map() if export_file is not None: export_file.write("dp_port_map = {}\n".format(dp_port_map)) # # Attach the usdt probe # u = USDT(pid=int(options.pid)) try: u.enable_probe(probe="recv_upcall", fn_name="trace__recv_upcall") u.enable_probe(probe="op_flow_put", fn_name="trace__op_flow_put") u.enable_probe(probe="op_flow_execute", fn_name="trace__op_flow_execute") except USDTException as e: print("ERROR: {}" "ovs-vswitchd!".format( (re.sub('^', ' ' * 7, str(e), flags=re.MULTILINE)).strip(). replace("--with-dtrace or --enable-dtrace", "--enable-usdt-probes"))) sys.exit(-1) # # Uncomment to see how arguments are decoded. # print(u.get_text()) # print("- Compiling eBPF programs...") # # Attach probes to the running process # source = ebpf_source.replace("", str(options.packet_size)) source = source.replace("", str(options.flow_key_size)) source = source.replace("", str(options.buffer_page_count)) b = BPF(text=source, usdt_contexts=[u], debug=options.debug & 0xffffff) # # Dump out all events # print("- Capturing events [Press ^C to stop]...") events_received = 0 if not options.quiet: print("\n" + Event.get_event_header_str()) b['events'].open_ring_buffer(receive_event_bcc) while 1: try: b.ring_buffer_poll() if options.stop != 0 and events_received >= options.stop: break except KeyboardInterrupt: break dropcnt = b.get_table("dropcnt") export_misses = {} for k in dropcnt.keys(): event = EventType.from_trace(k.value) count = dropcnt.sum(k).value if count > 0: if event not in event_count['total']: event_count['total'][event] = 0 event_count['valid'][event] = 0 event_count['miss'][event] = count export_misses[k.value] = count if options.write_events is not None: if sum(event_count['miss'].values()) > 0: export_file.write("event_miss = {}\n".format(export_misses)) export_file.close() print() else: # # Here we are requested to read event from an event export # thread_filter = None if options.handler_filter is not None: thread_filter = options.handler_filter.split(',') try: dp_port_mapping_valid = False with open(options.read_events, 'r') as fd: events_received = 0 if options.quiet: spinner = Halo(spinner="dots", color="cyan", text="Reading events from \"{}\"...".format( options.read_events)) spinner.start() else: print("- Reading events from \"{}\"...".format( options.read_events)) if not options.quiet: print("\n" + Event.get_event_header_str()) for entry in fd: if options.stop != 0 and events_received >= options.stop: break entry.rstrip() if entry.startswith('dp_port_map = {'): if not dp_port_mapping_valid: dp_port_mapping_valid = True dp_map.set_map(ast.literal_eval(entry[14:])) elif (entry.startswith('event = {') and dp_port_mapping_valid): event = ast.literal_eval(entry[8:]) event = namedtuple("EventObject", event.keys())(*event.values()) if thread_filter is not None \ and EventType.from_trace(event.event) != \ EventType.DP_UPCALL \ and event.comm.decode("utf-8") not in thread_filter: # Skip none filtered threads continue if len(event.pkt) > 0: options.packet_size = len(event.pkt) if len(event.key) > 0: options.flow_key_size = len(event.key) receive_event(event) events_received += 1 elif entry.startswith('event_miss = {'): misses = ast.literal_eval(entry[13:]) for e, count in misses.items(): event = EventType.from_trace(e) if count > 0: if event not in event_count['total']: event_count['total'][event] = 0 event_count['valid'][event] = 0 event_count['miss'][event] = count if options.quiet: spinner.stop() print("- Reading events from \"{}\"...".format( options.read_events)) except (FileNotFoundError, PermissionError): print("ERROR: Can't open file \"{}\" for reading!".format( options.read_events)) sys.exit(-1) # # Start analyzing the data # print("- Analyzing results ({} events)...".format(len(trace_data))) if events_received > 0: if sum(event_count['miss'].values()) > 0: print("\nWARNING: Not all events were captured!\n " "Increase the BPF ring buffer size with the " "--buffer-page-count option.") print("\n=> Events received per type (usable/total) [missed events]:") for event, total in sorted(event_count['total'].items()): miss = event_count['miss'][event] if event in event_count['miss'] \ else 0 print(" {:36}: {:10}/{:10} [{:10}]".format( event, event_count['valid'][event], total, miss)) collection, batch_stats, thread_stats = collect_event_sets( trace_data, collect_stats=True, spinner=True) if len(collection) <= 0: print("No upcall data sets where found!!") sys.exit(0) print("\n- Analyzing {} event sets...".format(len(collection))) if options.debug & 0x1000000 != 0: for upcall in collection: print("DBG: {}{}{}{}{}".format( "U" if EventType.DP_UPCALL in upcall else "-", "u" if EventType.RECV_UPCALL in upcall else "-", "p" if EventType.OP_FLOW_PUT in upcall else "-", "e" if EventType.OP_FLOW_EXECUTE in upcall else "-", "E" if EventType.OVS_PKT_EXEC in upcall else "-")) if options.debug & 0x2000000 != 0: try: print("DBG: - {}".format(upcall[EventType.DP_UPCALL])) print("DBG: - {}".format(upcall[EventType.RECV_UPCALL])) print("DBG: - {}".format(upcall[EventType.OP_FLOW_PUT])) print("DBG: - {}".format( upcall[EventType.OP_FLOW_EXECUTE])) print("DBG: - {}".format(upcall[EventType.OVS_PKT_EXEC])) except LookupError: continue show_key_value(thread_stats, description="Upcalls handled per thread") show_batch_histogram(batch_stats, description="Histogram of upcalls per batch") kernel_to_vswitchd = [] kernel_to_kernel_exec = [] vswitchd_to_kernel = [] time_minus_lookup = [] for upcall in collection: kernel_to_vswitchd.append((upcall[EventType.RECV_UPCALL].ts - upcall[EventType.DP_UPCALL].ts) / 1000) if EventType.OP_FLOW_PUT in upcall and \ EventType.OVS_PKT_EXEC in upcall: time_minus_lookup.append( ((upcall[EventType.OVS_PKT_EXEC].ts - upcall[EventType.DP_UPCALL].ts) - (upcall[EventType.OP_FLOW_PUT].ts - upcall[EventType.RECV_UPCALL].ts)) / 1000) if EventType.OP_FLOW_EXECUTE in upcall and \ EventType.OVS_PKT_EXEC in upcall: vswitchd_to_kernel.append((upcall[EventType.OVS_PKT_EXEC].ts - upcall[EventType.OP_FLOW_EXECUTE].ts) / 1000) if EventType.OVS_PKT_EXEC in upcall: kernel_to_kernel_exec.append((upcall[EventType.OVS_PKT_EXEC].ts - upcall[EventType.DP_UPCALL].ts) / 1000) show_histogram(kernel_to_vswitchd, description="Kernel upcall action to vswitchd receive " "(microseconds)", options=options) show_histogram(vswitchd_to_kernel, description="vswitchd execute to kernel receive " "(microseconds)", options=options) show_histogram(time_minus_lookup, description="Upcall overhead (total time minus lookup) " "(microseconds)", options=options) show_histogram(kernel_to_kernel_exec, description="Kernel upcall to kernel packet execute " "(microseconds)", options=options) # # Start main() as the default entry point... # if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/upcall_monitor.py000077500000000000000000000561211514270232600270500ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2021 Red Hat, Inc. # # 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. # # Script information: # ------------------- # upcall_monitor.py uses the dpif_recv:recv_upcall USDT to receive all upcall # packets sent by the kernel to ovs-vswitchd. By default, it will show all # upcall events, which looks something like this: # # TIME CPU COMM PID PORT_NAME TYPE .. # 5952147.003848809 2 handler4 1381158 eth0 (system@ovs-system) 0 # 5952147.003879643 2 handler4 1381158 eth0 (system@ovs-system) 0 # 5952147.003914924 2 handler4 1381158 eth0 (system@ovs-system) 0 # # Also, upcalls dropped by the kernel (e.g: because the netlink buffer is full) # are reported. This requires the kernel version to be greater or equal to # 5.14. # In addition, the packet and flow key data can be dumped. This can be done # using the --packet-decode and --flow-key decode options (see below). # # Note that by default only 64 bytes of the packet and flow key are retrieved. # If you would like to capture all or more of the packet and/or flow key data, # the ----packet-size and --flow-key-size options can be used. # # If required, the received packets can also be stored in a pcap file using the # --pcap option. # # The following are the available options: # # usage: upcall_monitor.py [-h] [-D [DEBUG]] [-d {none,hex,decode}] # [-f [64-2048]] [-k {none,hex,nlraw}] # [-p VSWITCHD_PID] [-s [64-2048]] [-w PCAP_FILE] # # optional arguments: # -h, --help show this help message and exit # -D [DEBUG], --debug [DEBUG] # Enable eBPF debugging # -d {none,hex,decode}, --packet-decode {none,hex,decode} # Display packet content in selected mode, # default none # -f [64-2048], --flow-key-size [64-2048] # Set maximum flow key size to capture, default 64 # -k {none,hex,nlraw}, --flow-key-decode {none,hex,nlraw} # Display flow-key content in selected mode, default # none # -p VSWITCHD_PID, --pid VSWITCHD_PID # ovs-vswitch's PID # -s [64-2048], --packet-size [64-2048] # Set maximum packet size to capture, default 64 # -w PCAP_FILE, --pcap PCAP_FILE # Write upcall packets to specified pcap file. # -r, --result {error,ok,any} # Display only events with the given result, # default: any # # The following is an example of how to use the script on the running # ovs-vswitchd process with a packet and flow key dump enabled: # # $ ./upcall_monitor.py --packet-decode decode --flow-key-decode nlraw \ # --packet-size 128 --flow-key-size 256 # TIME CPU COMM PID PORT_NAME ... # 5953013.333214231 2 handler4 1381158 system@ovs-system ... # Flow key size 132 bytes, size captured 132 bytes. # nla_len 8, nla_type OVS_KEY_ATTR_RECIRC_ID[20], data: 00 00 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_DP_HASH[19], data: 00 00 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_PRIORITY[2], data: 00 00 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_IN_PORT[3], data: 02 00 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_SKB_MARK[15], data: 00 00 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_CT_STATE[22], data: 00 00 00 00 # nla_len 6, nla_type OVS_KEY_ATTR_CT_ZONE[23], data: 00 00 # nla_len 8, nla_type OVS_KEY_ATTR_CT_MARK[24], data: 00 00 00 00 # nla_len 20, nla_type OVS_KEY_ATTR_CT_LABELS[25], data: 00 00 00 00 ... # nla_len 16, nla_type OVS_KEY_ATTR_ETHERNET[4], data: 04 f4 bc 28 57 ... # nla_len 6, nla_type OVS_KEY_ATTR_ETHERTYPE[6], data: 08 00 # nla_len 16, nla_type OVS_KEY_ATTR_IPV4[7], data: 01 01 01 64 01 01 ... # nla_len 6, nla_type OVS_KEY_ATTR_ICMP[11], data: 00 00 # 1: Receive dp_port 2, packet size 98 bytes, size captured 98 bytes. # ###[ Ethernet ]### # dst = 3c:fd:fe:9e:7f:68 # src = 04:f4:bc:28:57:01 # type = IPv4 # ###[ IP ]### # version = 4 # ihl = 5 # tos = 0x0 # len = 84 # id = 41404 # flags = DF # frag = 0 # ttl = 64 # proto = icmp # chksum = 0x940c # src = 1.1.1.100 # dst = 1.1.1.123 # \options \ # ###[ ICMP ]### # type = echo-reply # code = 0 # chksum = 0x2f55 # id = 0x90e6 # seq = 0x1 # ###[ Raw ]### # load = 'GBTa\x00\x00\x00\x00\xd8L\r\x00\x00\x00\x00\... # from bcc import BPF, USDT, USDTException from os.path import exists, join from scapy import VERSION as scapy_version from scapy.layers.l2 import Ether from usdt_lib import DpPortMapping import argparse import psutil import re import struct import sys (scapy_mayor, scapy_minor, _) = scapy_version.split(".", 2) (scapy_mayor, scapy_minor) = (int(scapy_mayor), int(scapy_minor)) scapy_supports_pcap_iface = False if scapy_mayor < 2 or (scapy_mayor == 2 and scapy_minor) <= 4: from scapy.all import hexdump, wrpcap else: from scapy.all import hexdump, PcapNgWriter if scapy_mayor > 2 or (scapy_mayor == 2 and scapy_minor >= 6): # Support for setting the iface name in the pcapng file was added in: # 56b4fa4a pcapng enhancements (idb,epb) and some fixes (#4342) scapy_supports_pcap_iface = True # # Actual eBPF source code # ebpf_source = """ #include #include #define MAX_PACKET #define MAX_KEY struct event_t { int result; u32 cpu; u32 pid; u32 upcall_type; u64 ts; u32 pkt_size; u64 key_size; char comm[TASK_COMM_LEN]; char dpif_name[32]; char dev_name[16]; unsigned char pkt[MAX_PACKET]; unsigned char key[MAX_KEY]; }; BPF_RINGBUF_OUTPUT(events, ); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); static void report_missed_event() { uint32_t type = 0; uint64_t *value = dropcnt.lookup(&type); if (value) __sync_fetch_and_add(value, 1); } #if int do_trace(struct pt_regs *ctx) { uint64_t addr; uint64_t size; struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { report_missed_event(); return 1; } event->ts = bpf_ktime_get_ns(); event->cpu = bpf_get_smp_processor_id(); event->pid = bpf_get_current_pid_tgid(); event->result = 0; event->dev_name[0] = 0; bpf_get_current_comm(&event->comm, sizeof(event->comm)); bpf_usdt_readarg(1, ctx, &addr); bpf_probe_read_str(&event->dpif_name, sizeof(event->dpif_name), (void *)addr); bpf_usdt_readarg(2, ctx, &event->upcall_type); bpf_usdt_readarg(4, ctx, &event->pkt_size); bpf_usdt_readarg(6, ctx, &event->key_size); if (event->pkt_size > MAX_PACKET) size = MAX_PACKET; else size = event->pkt_size; bpf_usdt_readarg(3, ctx, &addr); bpf_probe_read(&event->pkt, size, (void *)addr); if (event->key_size > MAX_KEY) size = MAX_KEY; else size = event->key_size; bpf_usdt_readarg(5, ctx, &addr); bpf_probe_read(&event->key, size, (void *)addr); events.ringbuf_submit(event, 0); return 0; }; #endif #if struct inflight_upcall { u32 cpu; u32 upcall_type; u64 ts; struct sk_buff *skb; char dpif_name[32]; }; BPF_HASH(inflight_upcalls, u64, struct inflight_upcall); TRACEPOINT_PROBE(openvswitch, ovs_dp_upcall) { u64 pid = bpf_get_current_pid_tgid(); struct inflight_upcall upcall = {}; upcall.cpu = bpf_get_smp_processor_id(); upcall.ts = bpf_ktime_get_ns(); upcall.upcall_type = args->upcall_cmd; upcall.skb = args->skbaddr; TP_DATA_LOC_READ_CONST(&upcall.dpif_name, dp_name, sizeof(upcall.dpif_name)); inflight_upcalls.insert(&pid, &upcall); return 0; } int kretprobe__ovs_dp_upcall(struct pt_regs *ctx) { u64 pid = bpf_get_current_pid_tgid(); struct inflight_upcall *upcall; int ret = PT_REGS_RC(ctx); struct net_device *dev; u64 size; upcall = inflight_upcalls.lookup(&pid); inflight_upcalls.delete(&pid); if (!upcall) return 0; /* Successfull upcalls are reported in the USDT probe. */ if (!ret) return 0; struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { report_missed_event(); return 1; } event->ts = upcall->ts; event->cpu = upcall->cpu; event->pid = pid; event->result = ret; __builtin_memcpy(&event->dpif_name, &upcall->dpif_name, 32); bpf_get_current_comm(&event->comm, sizeof(event->comm)); event->pkt_size = upcall->skb->len; event->upcall_type = upcall->upcall_type; event->key_size = 0; bpf_probe_read(&dev, sizeof(upcall->skb->dev), ((char *)upcall->skb + offsetof(struct sk_buff, dev))); bpf_probe_read_kernel(&event->dev_name, 16, dev->name); size = upcall->skb->len - upcall->skb->data_len; if (size > MAX_PACKET) size = MAX_PACKET; bpf_probe_read_kernel(event->pkt, size, upcall->skb->data); events.ringbuf_submit(event, 0); return 0; } #endif """ pcap_writer = None # # format_key() # def format_key(event, decode_dump): lines = [] if event.key_size < options.flow_key_size: key_len = event.key_size else: key_len = options.flow_key_size if not key_len: return [] if options.flow_key_decode != 'none': lines.append(" Flow key size {} bytes, size captured {} bytes.". format(event.key_size, key_len)) if options.flow_key_decode == 'hex': # # Abuse scapy's hex dump to dump flow key # lines.extend(re.sub('^', ' ' * 4, hexdump( Ether(bytes(event.key)[:key_len]), dump=True), flags=re.MULTILINE).split("\n")) if options.flow_key_decode == "nlraw": lines.extend(decode_dump) return lines # # print_event() # def print_event(ctx, data, size): global pcap_writer event = b["events"].event(data) dp = event.dpif_name.decode("utf-8") nla, key_dump = decode_nlm( bytes(event.key)[: min(event.key_size, options.flow_key_size)] ) if event.dev_name: port = event.dev_name.decode("utf-8") elif "OVS_KEY_ATTR_IN_PORT" in nla: port_no = struct.unpack("=I", nla["OVS_KEY_ATTR_IN_PORT"])[0] port = dp_map.get_port_name(dp.partition("@")[-1], port_no) if not port: port = str(port_no) else: port = "Unknown" print( "{:<18.9f} {:<4} {:<16} {:<10} {:<40} {:<4} {:<10} {:<12} {:<8}". format( event.ts / 1000000000, event.cpu, event.comm.decode("utf-8"), event.pid, "{} ({})".format(port, dp), event.upcall_type, event.pkt_size, event.key_size, event.result, ) ) # # Dump flow key information # key_lines = format_key(event, key_dump) for line in key_lines: print(line) # # Decode packet only if there is data # if event.pkt_size <= 0: return pkt_id = get_pkt_id() if event.pkt_size < options.packet_size: pkt_len = event.pkt_size pkt_data = bytes(event.pkt)[:event.pkt_size] else: pkt_len = options.packet_size pkt_data = bytes(event.pkt) if options.packet_decode != 'none' or options.pcap is not None: print(" {}: Receive dp_port {}, packet size {} bytes, size " "captured {} bytes.".format(pkt_id, port, event.pkt_size, pkt_len)) if options.packet_decode == 'hex': print(re.sub('^', ' ' * 4, hexdump(pkt_data, dump=True), flags=re.MULTILINE)) packet = Ether(pkt_data) packet.wirelen = event.pkt_size if options.packet_decode == 'decode': print(re.sub('^', ' ' * 4, packet.show(dump=True), flags=re.MULTILINE)) if options.pcap is not None: try: if pcap_writer is None: pcap_writer = PcapNgWriter(options.pcap) comment = "cpu={} comm={} pid={} upcall_type={} result={}". format( event.cpu, event.comm.decode("utf-8"), event.pid, event.upcall_type, event.result) if options.flow_key_decode != 'none': comment = comment + "\n" + "\n".join(key_lines) if scapy_supports_pcap_iface: packet.sniffed_on = "{} ({})".format(port, dp) else: comment = "iface={}({}) ".format(port, dp) + comment packet.comment = comment pcap_writer.write(packet) except NameError: # PcapNgWriter not found wrpcap(options.pcap, packet, append=True, snaplen=options.packet_size) # # decode_nlm() # def decode_nlm(msg, indent=4): bytes_left = len(msg) result = {} dump = [] while bytes_left: if bytes_left < 4: dump.append( "{}WARN: decode truncated; can't read header".format( " " * indent ) ) break nla_len, nla_type = struct.unpack("=HH", msg[:4]) if nla_len < 4: dump.append( "{}WARN: decode truncated; nla_len < 4".format(" " * indent) ) break nla_data = msg[4:nla_len] trunc = "" if nla_len > bytes_left: trunc = "..." nla_data = nla_data[:(bytes_left - 4)] else: result[get_ovs_key_attr_str(nla_type)] = nla_data dump.append( "{}nla_len {}, nla_type {}[{}], data: {}{}".format( ' ' * indent, nla_len, get_ovs_key_attr_str(nla_type), nla_type, "".join("{:02x} ".format(b) for b in nla_data), trunc) ) if trunc != "": dump.append( "{}WARN: decode truncated; nla_len > msg_len[{}] ".format( " " * indent, bytes_left ) ) break # update next offset, but make sure it's aligned correctly next_offset = (nla_len + 3) & ~(3) msg = msg[next_offset:] bytes_left -= next_offset return result, dump # # get_ovs_key_attr_str() # def get_ovs_key_attr_str(attr): ovs_key_attr = ["OVS_KEY_ATTR_UNSPEC", "OVS_KEY_ATTR_ENCAP", "OVS_KEY_ATTR_PRIORITY", "OVS_KEY_ATTR_IN_PORT", "OVS_KEY_ATTR_ETHERNET", "OVS_KEY_ATTR_VLAN", "OVS_KEY_ATTR_ETHERTYPE", "OVS_KEY_ATTR_IPV4", "OVS_KEY_ATTR_IPV6", "OVS_KEY_ATTR_TCP", "OVS_KEY_ATTR_UDP", "OVS_KEY_ATTR_ICMP", "OVS_KEY_ATTR_ICMPV6", "OVS_KEY_ATTR_ARP", "OVS_KEY_ATTR_ND", "OVS_KEY_ATTR_SKB_MARK", "OVS_KEY_ATTR_TUNNEL", "OVS_KEY_ATTR_SCTP", "OVS_KEY_ATTR_TCP_FLAGS", "OVS_KEY_ATTR_DP_HASH", "OVS_KEY_ATTR_RECIRC_ID", "OVS_KEY_ATTR_MPLS", "OVS_KEY_ATTR_CT_STATE", "OVS_KEY_ATTR_CT_ZONE", "OVS_KEY_ATTR_CT_MARK", "OVS_KEY_ATTR_CT_LABELS", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "OVS_KEY_ATTR_NSH"] if attr < 0 or attr > len(ovs_key_attr): return "" return ovs_key_attr[attr] # # get_pkt_id() # def get_pkt_id(): if not hasattr(get_pkt_id, "counter"): get_pkt_id.counter = 0 get_pkt_id.counter += 1 return get_pkt_id.counter # # buffer_size_type() # def buffer_size_type(astr, min=64, max=2048): value = int(astr) if min <= value <= max: return value else: raise argparse.ArgumentTypeError( 'value not in range {}-{}'.format(min, max)) # # next_power_of_two() # def next_power_of_two(val): np = 1 while np < val: np *= 2 return np # # main() # def main(): # # Don't like these globals, but ctx passing does not seem to work with the # existing open_ring_buffer() API :( # global b global options global dp_map dp_map = DpPortMapping() # # Argument parsing # parser = argparse.ArgumentParser() parser.add_argument("--buffer-page-count", help="Number of BPF ring buffer pages, default 1024", type=int, default=1024, metavar="NUMBER") parser.add_argument("-D", "--debug", help="Enable eBPF debugging", type=int, const=0x3f, default=0, nargs='?') parser.add_argument('-d', '--packet-decode', help='Display packet content in selected mode, ' 'default none', choices=['none', 'hex', 'decode'], default='none') parser.add_argument("-f", "--flow-key-size", help="Set maximum flow key size to capture, " "default 64", type=buffer_size_type, default=64, metavar="[64-2048]") parser.add_argument('-k', '--flow-key-decode', help='Display flow-key content in selected mode, ' 'default none', choices=['none', 'hex', 'nlraw'], default='none') parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID", help="ovs-vswitch's PID", type=int, default=None) parser.add_argument("-s", "--packet-size", help="Set maximum packet size to capture, " "default 64", type=buffer_size_type, default=64, metavar="[64-2048]") parser.add_argument("-w", "--pcap", metavar="PCAP_FILE", help="Write upcall packets to specified pcap file.", type=str, default=None) parser.add_argument("-r", "--result", help="Display only events with the given result, " "default: any", choices=["error", "ok", "any"], default="any") options = parser.parse_args() # # Check if current kernel supports error reporting. # tracefs_paths = ("/sys/kernel/debug/tracing/", "/sys/kernel/tracing/") upcall_tp_found = False for tracefs in tracefs_paths: if exists(join(tracefs, "events/openvswitch/ovs_dp_upcall")): upcall_tp_found = True break if not upcall_tp_found: if options.result == "error": print("ERROR: Monitoring error upcalls is not supported by the " "running kernel (or the tracefs is not mounted).") sys.exit(-1) if options.result == "any": print("WARN: Monitoring error upcalls is not supported by the " "running kernel (or the tracefs is not mounted). " "Only successful ones will be monitored.") options.result = "ok" # # Find the PID of the ovs-vswitchd daemon if not specified. # if options.pid is None: for proc in psutil.process_iter(): if 'ovs-vswitchd' in proc.name(): if options.pid is not None: print("ERROR: Multiple ovs-vswitchd daemons running, " "use the -p option!") sys.exit(-1) options.pid = proc.pid # # Error checking on input parameters # if options.pid is None: print("ERROR: Failed to find ovs-vswitchd's PID!") sys.exit(-1) if options.pcap is not None: if exists(options.pcap): print("ERROR: Destination capture file \"{}\" already exists!". format(options.pcap)) sys.exit(-1) options.buffer_page_count = next_power_of_two(options.buffer_page_count) # # Attach the usdt probe # usdt = [] if options.result in ["ok", "any"]: u = USDT(pid=int(options.pid)) try: u.enable_probe(probe="recv_upcall", fn_name="do_trace") usdt.append(u) except USDTException as e: print("ERROR: {}" "ovs-vswitchd!".format( (re.sub('^', ' ' * 7, str(e), flags=re.MULTILINE)). strip().replace("--with-dtrace or --enable-dtrace", "--enable-usdt-probes"))) sys.exit(-1) # # Uncomment to see how arguments are decoded. # print(u.get_text()) # # # Attach probe to running process # source = ebpf_source.replace("", str(options.packet_size)) source = source.replace("", str(options.flow_key_size)) source = source.replace("", str(options.buffer_page_count)) source = source.replace("", "1" if options.result in ["ok", "any"] else "0") source = source.replace("", "1" if options.result in ["error", "any"] else "0") b = BPF(text=source, usdt_contexts=usdt, debug=options.debug) # # Print header # print("{:<18} {:<4} {:<16} {:<10} {:<40} {:<4} {:<10} {:<12} {:<8}".format( "TIME", "CPU", "COMM", "PID", "PORT_NAME", "TYPE", "PKT_LEN", "FLOW_KEY_LEN", "RESULT")) # # Dump out all events # b['events'].open_ring_buffer(print_event) while 1: try: b.ring_buffer_poll() except KeyboardInterrupt: break dropcnt = b.get_table("dropcnt") for k in dropcnt.keys(): count = dropcnt.sum(k).value if k.value == 0 and count > 0: print("\nWARNING: Not all upcalls were captured, {} were dropped!" "\n Increase the BPF ring buffer size with the " "--buffer-page-count option.".format(count)) # # Start main() as the default entry point... # if __name__ == '__main__': main() openvswitch-3.7.0~git20260211.8c6ebf8/utilities/usdt-scripts/usdt_lib.py000066400000000000000000000057431514270232600256270ustar00rootroot00000000000000# Copyright (c) 2021 Red Hat, Inc. # # 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. import re import subprocess class DpPortMapping: """Class used to retrieve and cache datapath port numbers to port names.""" MAX_REQUESTS = 2 def __init__(self): self.cache_map = None self.n_requests = 0 def get_map(self): """Get the cache map.""" self._get_mapping() return self.cache_map def set_map(self, cache_map): """Override the internal cache map.""" self.cache_map = cache_map self.n_requests = self.MAX_REQUESTS def _retry(self, func): self._get_mapping() result = func(self.cache_map) if not result: self._get_mapping(refresh=True) return func(self.cache_map) return result def get_port_name(self, dp, port_no): """Get the port name from a port number.""" def _get_port_name(cache_map): if not cache_map.get(dp): return None for name, num in cache_map[dp].items(): if num == port_no: return name return self._retry(_get_port_name) def get_port_number(self, dp, port): """Get the port number from a port name.""" def _get_port_number(cache_map): return cache_map.get(dp, {}).get(port, None) return self._retry(_get_port_number) def _get_mapping(self, refresh=False): """Get the datapath port mapping from the running OVS.""" if self.n_requests >= self.MAX_REQUESTS \ or (self.cache_map and not refresh): return self.n_requests += 1 try: output = subprocess.check_output( ["ovs-appctl", "dpctl/show"], encoding="utf8" ).split("\n") except subprocess.CalledProcessError: output = "" return current_dp = None self.cache_map = {} for line in output: match = re.match("^system@(.*):$", line) if match: current_dp = match.group(1) match = re.match("^ port ([0-9]+): ([^ /]*)", line) if match and current_dp: try: self.cache_map[current_dp][match.group(2)] = int( match.group(1) ) except KeyError: self.cache_map[current_dp] = { match.group(2): int(match.group(1)) } openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/000077500000000000000000000000001514270232600206135ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/.gitignore000066400000000000000000000001771514270232600226100ustar00rootroot00000000000000/Makefile /Makefile.in /ovs-vswitchd /ovs-vswitchd.8 /ovs-vswitchd.conf.db.5 /vswitch.ovsschema.stamp /vswitch.gv /vswitch.pic openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/automake.mk000066400000000000000000000040661514270232600227600ustar00rootroot00000000000000sbin_PROGRAMS += vswitchd/ovs-vswitchd man_MANS += vswitchd/ovs-vswitchd.8 CLEANFILES += \ vswitchd/ovs-vswitchd.8 vswitchd_ovs_vswitchd_SOURCES = \ vswitchd/bridge.c \ vswitchd/bridge.h \ vswitchd/ovs-vswitchd.c \ vswitchd/system-stats.c \ vswitchd/system-stats.h vswitchd_ovs_vswitchd_LDADD = \ ofproto/libofproto.la \ lib/libsflow.la \ lib/libopenvswitch.la vswitchd_ovs_vswitchd_LDFLAGS = $(AM_LDFLAGS) $(DPDK_vswitchd_LDFLAGS) MAN_ROOTS += vswitchd/ovs-vswitchd.8.in # vswitch schema and IDL EXTRA_DIST += vswitchd/vswitch.ovsschema pkgdata_DATA += vswitchd/vswitch.ovsschema # vswitch E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_DOT vswitchd/vswitch.gv: ovsdb/ovsdb-dot.in vswitchd/vswitch.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vswitchd/vswitch.ovsschema > $@ vswitchd/vswitch.pic: vswitchd/vswitch.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < vswitchd/vswitch.gv | $(PYTHON3) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ VSWITCH_PIC = vswitchd/vswitch.pic VSWITCH_DOT_DIAGRAM_ARG = --er-diagram=$(VSWITCH_PIC) CLEANFILES += vswitchd/vswitch.gv vswitchd/vswitch.pic endif # vswitch schema documentation EXTRA_DIST += vswitchd/vswitch.xml CLEANFILES += vswitchd/ovs-vswitchd.conf.db.5 man_MANS += vswitchd/ovs-vswitchd.conf.db.5 vswitchd/ovs-vswitchd.conf.db.5: \ ovsdb/ovsdb-doc vswitchd/vswitch.xml vswitchd/vswitch.ovsschema \ $(VSWITCH_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VSWITCH_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vswitchd/vswitch.ovsschema \ $(srcdir)/vswitchd/vswitch.xml > $@.tmp && \ mv $@.tmp $@ # Version checking for vswitch.ovsschema. ALL_LOCAL += vswitchd/vswitch.ovsschema.stamp vswitchd/vswitch.ovsschema.stamp: vswitchd/vswitch.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += vswitchd/vswitch.ovsschema.stamp # Clean up generated files from older OVS versions. (This is important so that # #include "vswitch-idl.h" doesn't get the wrong copy.) CLEANFILES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/bridge.c000066400000000000000000005406271514270232600222310ustar00rootroot00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc. * * 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. */ #include #include "bridge.h" #include #include #include #include "async-append.h" #include "bfd.h" #include "bitmap.h" #include "cfm.h" #include "connectivity.h" #include "coverage.h" #include "daemon.h" #include "dirs.h" #include "dpif.h" #include "dpif-offload.h" #include "dpdk.h" #include "hash.h" #include "openvswitch/hmap.h" #include "hmapx.h" #include "if-notifier.h" #include "jsonrpc.h" #include "lacp.h" #include "mac-learning.h" #include "mcast-snooping.h" #include "netdev.h" #include "nx-match.h" #include "odp-execute.h" #include "ofproto/bond.h" #include "ofproto/ofproto.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/list.h" #include "openvswitch/meta-flow.h" #include "openvswitch/ofp-print.h" #include "openvswitch/ofpbuf.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovs-lldp.h" #include "ovs-numa.h" #include "packets.h" #include "openvswitch/poll-loop.h" #include "seq.h" #include "sflow_api.h" #include "sha1.h" #include "openvswitch/shash.h" #include "smap.h" #include "socket-util.h" #include "stream.h" #include "stream-ssl.h" #include "sset.h" #include "system-stats.h" #include "timeval.h" #include "tnl-ports.h" #include "userspace-tso.h" #include "util.h" #include "unixctl.h" #include "lib/vswitch-idl.h" #include "vlan-bitmap.h" VLOG_DEFINE_THIS_MODULE(bridge); COVERAGE_DEFINE(bridge_reconfigure); struct iface { /* These members are always valid. * * They are immutable: they never change between iface_create() and * iface_destroy(). */ struct ovs_list port_elem; /* Element in struct port's "ifaces" list. */ struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */ struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */ struct port *port; /* Containing port. */ char *name; /* Host network device name. */ struct netdev *netdev; /* Network device. */ ofp_port_t ofp_port; /* OpenFlow port number. */ uint64_t change_seq; /* These members are valid only within bridge_reconfigure(). */ const char *type; /* Usually same as cfg->type. */ const struct ovsrec_interface *cfg; }; struct mirror { struct uuid uuid; /* UUID of this "mirror" record in database. */ struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */ struct bridge *bridge; char *name; const struct ovsrec_mirror *cfg; }; struct port { struct hmap_node hmap_node; /* Element in struct bridge's "ports" hmap. */ struct bridge *bridge; char *name; const struct ovsrec_port *cfg; /* An ordinary bridge port has 1 interface. * A bridge port for bonding has at least 2 interfaces. */ struct ovs_list ifaces; /* List of "struct iface"s. */ }; struct bridge { struct hmap_node node; /* In 'all_bridges'. */ char *name; /* User-specified arbitrary name. */ char *type; /* Datapath type. */ struct eth_addr ea; /* Bridge Ethernet Address. */ struct eth_addr default_ea; /* Default MAC. */ const struct ovsrec_bridge *cfg; /* OpenFlow switch processing. */ struct ofproto *ofproto; /* OpenFlow switch. */ /* Bridge ports. */ struct hmap ports; /* "struct port"s indexed by name. */ struct hmap ifaces; /* "struct iface"s indexed by ofp_port. */ struct hmap iface_by_name; /* "struct iface"s indexed by name. */ /* Port mirroring. */ struct hmap mirrors; /* "struct mirror" indexed by UUID. */ /* Auto Attach */ struct hmap mappings; /* "struct" indexed by UUID */ /* Used during reconfiguration. */ struct shash wanted_ports; /* Synthetic local port if necessary. */ struct ovsrec_port synth_local_port; struct ovsrec_interface synth_local_iface; struct ovsrec_interface *synth_local_ifacep; }; struct aa_mapping { struct hmap_node hmap_node; /* In struct bridge's "mappings" hmap. */ struct bridge *bridge; uint32_t isid; uint16_t vlan; char *br_name; }; /* Internal representation of conntrack zone configuration table in OVSDB. */ struct ct_zone { uint16_t zone_id; int64_t limit; /* Limit of allowed entries. '-1' if not * specified. */ struct simap tp; /* A map from timeout policy attribute to * timeout value. */ struct hmap_node node; /* Node in 'struct datapath' 'ct_zones' * hmap. */ unsigned int last_used; /* The last idl_seqno that this 'ct_zone' used * in OVSDB. This number is used for garbage * collection. */ }; /* Internal representation of datapath configuration table in OVSDB. */ struct datapath { char *type; /* Datapath type. */ struct hmap ct_zones; /* Map of 'struct ct_zone' elements, * indexed by 'zone'. */ struct hmap_node node; /* Node in 'all_datapaths' hmap. */ struct smap caps; /* Capabilities. */ unsigned int last_used; /* The last idl_seqno that this 'datapath' * used in OVSDB. This number is used for * garbage collection. */ int64_t ct_zone_default_limit; /* Default CT limit for all zones. */ }; /* All bridges, indexed by name. */ static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges); /* All datapath configuartions, indexed by type. */ static struct hmap all_datapaths = HMAP_INITIALIZER(&all_datapaths); /* OVSDB IDL used to obtain configuration. */ static struct ovsdb_idl *idl; /* We want to complete daemonization, fully detaching from our parent process, * only after we have completed our initial configuration, committed our state * to the database, and received confirmation back from the database server * that it applied the commit. This allows our parent process to know that, * post-detach, ephemeral fields such as datapath-id and ofport are very likely * to have already been filled in. (It is only "very likely" rather than * certain because there is always a slim possibility that the transaction will * fail or that some other client has added new bridges, ports, etc. while * ovs-vswitchd was configuring using an old configuration.) * * We only need to do this once for our initial configuration at startup, so * 'initial_config_done' tracks whether we've already done it. While we are * waiting for a response to our commit, 'daemonize_txn' tracks the transaction * itself and is otherwise NULL. */ static bool initial_config_done; static struct ovsdb_idl_txn *daemonize_txn; /* Most recently processed IDL sequence number. */ static unsigned int idl_seqno; /* Track changes to port connectivity. */ static uint64_t connectivity_seqno = LLONG_MIN; /* Status update to database. * * Some information in the database must be kept as up-to-date as possible to * allow controllers to respond rapidly to network outages. Those status are * updated via the 'status_txn'. * * We use the global connectivity sequence number to detect the status change. * Also, to prevent the status update from sending too much to the database, * we check the return status of each update transaction and do not start new * update if the previous transaction status is 'TXN_INCOMPLETE'. * * 'statux_txn' is NULL if there is no ongoing status update. * * If the previous database transaction was failed (is not 'TXN_SUCCESS', * 'TXN_UNCHANGED' or 'TXN_INCOMPLETE'), 'status_txn_try_again' is set to true, * which will cause the main thread wake up soon and retry the status update. */ static struct ovsdb_idl_txn *status_txn; static bool status_txn_try_again; /* When the status update transaction returns 'TXN_INCOMPLETE', should register a * timeout in 'STATUS_CHECK_AGAIN_MSEC' to check again. */ #define STATUS_CHECK_AGAIN_MSEC 100 /* Statistics update to database. */ static struct ovsdb_idl_txn *stats_txn; /* Each time this timer expires, the bridge fetches interface and mirror * statistics and pushes them into the database. */ static int stats_timer_interval; static long long int stats_timer = LLONG_MIN; /* Each time this timer expires, the bridge fetches the list of port/VLAN * membership that has been modified by the AA. */ #define AA_REFRESH_INTERVAL (1000) /* In milliseconds. */ static long long int aa_refresh_timer = LLONG_MIN; /* Whenever system interfaces are added, removed or change state, the bridge * will be reconfigured. */ static struct if_notifier *ifnotifier; static struct seq *ifaces_changed; static uint64_t last_ifaces_changed; /* Default/min/max packet-in queue sizes towards the controllers. */ #define BRIDGE_CONTROLLER_PACKET_QUEUE_DEFAULT_SIZE 100 #define BRIDGE_CONTROLLER_PACKET_QUEUE_MIN_SIZE 1 #define BRIDGE_CONTROLLER_PACKET_QUEUE_MAX_SIZE 512 static void add_del_bridges(const struct ovsrec_open_vswitch *); static void bridge_run__(void); static void bridge_create(const struct ovsrec_bridge *); static void bridge_destroy(struct bridge *, bool del); static struct bridge *bridge_lookup(const char *name); static unixctl_cb_func bridge_unixctl_dump_flows; static unixctl_cb_func bridge_unixctl_reconnect; static size_t bridge_get_controllers(const struct bridge *br, struct ovsrec_controller ***controllersp); static void bridge_collect_wanted_ports(struct bridge *, struct shash *wanted_ports); static void bridge_delete_ofprotos(void); static void bridge_delete_or_reconfigure_ports(struct bridge *); static void bridge_del_ports(struct bridge *, const struct shash *wanted_ports); static void bridge_add_ports(struct bridge *, const struct shash *wanted_ports); static void bridge_configure_datapath_id(struct bridge *); static void bridge_configure_netflow(struct bridge *); static void bridge_configure_forward_bpdu(struct bridge *); static void bridge_configure_mac_table(struct bridge *); static void bridge_configure_mcast_snooping(struct bridge *); static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number); static void bridge_configure_ipfix(struct bridge *); static void bridge_configure_lsample(struct bridge *); static void bridge_configure_spanning_tree(struct bridge *); static void bridge_configure_tables(struct bridge *); static void bridge_configure_dp_desc(struct bridge *); static void bridge_configure_serial_desc(struct bridge *); static void bridge_configure_aa(struct bridge *); static void bridge_aa_refresh_queued(struct bridge *); static bool bridge_aa_need_refresh(struct bridge *); static void bridge_configure_remotes(struct bridge *, const struct sockaddr_in *managers, size_t n_managers); static void bridge_pick_local_hw_addr(struct bridge *, struct eth_addr *ea, struct iface **hw_addr_iface); static uint64_t bridge_pick_datapath_id(struct bridge *, const struct eth_addr bridge_ea); static bool bridge_has_bond_fake_iface(const struct bridge *, const char *name); static bool port_is_bond_fake_iface(const struct port *); static void datapath_destroy(struct datapath *dp); static unixctl_cb_func qos_unixctl_show_types; static unixctl_cb_func qos_unixctl_show; static struct port *port_create(struct bridge *, const struct ovsrec_port *); static void port_del_ifaces(struct port *); static void port_destroy(struct port *); static struct port *port_lookup(const struct bridge *, const char *name); static void port_configure(struct port *); static struct lacp_settings *port_configure_lacp(struct port *, struct lacp_settings *); static void port_configure_bond(struct port *, struct bond_settings *); static bool port_is_synthetic(const struct port *); static void reconfigure_system_stats(const struct ovsrec_open_vswitch *); static void run_system_stats(void); static void bridge_configure_mirrors(struct bridge *); static struct mirror *mirror_create(struct bridge *, const struct ovsrec_mirror *); static void mirror_destroy(struct mirror *); static bool mirror_configure(struct mirror *); static void mirror_refresh_stats(struct mirror *); static void iface_configure_lacp(struct iface *, struct lacp_member_settings *); static bool iface_create(struct bridge *, const struct ovsrec_interface *, const struct ovsrec_port *); static bool iface_is_internal(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br); static const char *iface_get_type(const struct ovsrec_interface *, const struct ovsrec_bridge *); static void iface_destroy(struct iface *); static void iface_destroy__(struct iface *); static struct iface *iface_lookup(const struct bridge *, const char *name); static struct iface *iface_find(const char *name); static struct iface *iface_from_ofp_port(const struct bridge *, ofp_port_t ofp_port); static void iface_set_mac(const struct bridge *, const struct port *, struct iface *); static void iface_set_ofport(const struct ovsrec_interface *, ofp_port_t ofport); static void iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp); static void iface_configure_qos(struct iface *, const struct ovsrec_qos *); static void iface_configure_cfm(struct iface *); static void iface_refresh_cfm_stats(struct iface *); static void iface_refresh_stats(struct iface *); static void iface_refresh_netdev_status(struct iface *); static void iface_refresh_ofproto_status(struct iface *); static bool iface_is_synthetic(const struct iface *); static ofp_port_t iface_get_requested_ofp_port( const struct ovsrec_interface *); static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *); static void discover_types(const struct ovsrec_open_vswitch *cfg); static void bridge_init_ofproto(const struct ovsrec_open_vswitch *cfg) { struct shash iface_hints; static bool initialized = false; int i; if (initialized) { return; } shash_init(&iface_hints); if (cfg) { for (i = 0; i < cfg->n_bridges; i++) { const struct ovsrec_bridge *br_cfg = cfg->bridges[i]; int j; for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; int k; for (k = 0; k < port_cfg->n_interfaces; k++) { struct ovsrec_interface *if_cfg = port_cfg->interfaces[k]; struct iface_hint *iface_hint; iface_hint = xmalloc(sizeof *iface_hint); iface_hint->br_name = br_cfg->name; iface_hint->br_type = br_cfg->datapath_type; iface_hint->ofp_port = iface_pick_ofport(if_cfg); shash_add(&iface_hints, if_cfg->name, iface_hint); } } } } ofproto_init(&iface_hints); shash_destroy_free_data(&iface_hints); initialized = true; } static void if_change_cb(void *aux OVS_UNUSED) { seq_change(ifaces_changed); } static bool if_notifier_changed(struct if_notifier *notifier OVS_UNUSED) { uint64_t new_seq; bool changed = false; new_seq = seq_read(ifaces_changed); if (new_seq != last_ifaces_changed) { changed = true; last_ifaces_changed = new_seq; } seq_wait(ifaces_changed, last_ifaces_changed); return changed; } /* Public functions. */ /* Initializes the bridge module, configuring it to obtain its configuration * from an OVSDB server accessed over 'remote', which should be a string in a * form acceptable to ovsdb_idl_create(). */ void bridge_init(const char *remote) { /* Create connection to database. */ idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true, true); idl_seqno = ovsdb_idl_get_seqno(idl); ovsdb_idl_set_lock(idl, "ovs_vswitchd"); ovsdb_idl_verify_write_only(idl); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_datapath_types); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_iface_types); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_ovs_version); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_db_version); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_type); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_version); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_dpdk_version); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_dpdk_initialized); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_version); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_status); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_stp_enable); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_enable); ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_status); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_bond_active_slave); ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_trunks); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_vlan_mode); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_admin_state); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_duplex); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_resets); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mac_in_use); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ifindex); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_flap_count); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_health); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_opstate); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_bfd_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_error); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_is_connected); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_role); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_status); ovsdb_idl_omit(idl, &ovsrec_controller_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_qos_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_queue_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_mirror_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_mirror_col_statistics); ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_sflow_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_ipfix_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_flow_sample_collector_set_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_manager_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_manager_col_inactivity_probe); ovsdb_idl_omit(idl, &ovsrec_manager_col_is_connected); ovsdb_idl_omit(idl, &ovsrec_manager_col_max_backoff); ovsdb_idl_omit(idl, &ovsrec_manager_col_status); ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids); /* Register unixctl commands. */ unixctl_command_register("qos/show-types", "interface", 1, 1, qos_unixctl_show_types, NULL); unixctl_command_register("qos/show", "interface", 1, 1, qos_unixctl_show, NULL); unixctl_command_register("bridge/dump-flows", "[--offload-stats] bridge", 1, 2, bridge_unixctl_dump_flows, NULL); unixctl_command_register("bridge/reconnect", "[bridge]", 0, 1, bridge_unixctl_reconnect, NULL); lacp_init(); bond_init(); cfm_init(); bfd_init(); ovs_numa_init(); stp_init(); lldp_init(); rstp_init(); odp_execute_init(); ifaces_changed = seq_create(); last_ifaces_changed = seq_read(ifaces_changed); ifnotifier = if_notifier_create(if_change_cb, NULL); if_notifier_manual_set_cb(if_change_cb); } void bridge_exit(bool delete_datapath) { if_notifier_manual_set_cb(NULL); if_notifier_destroy(ifnotifier); seq_destroy(ifaces_changed); struct datapath *dp; HMAP_FOR_EACH_SAFE (dp, node, &all_datapaths) { datapath_destroy(dp); } struct bridge *br; HMAP_FOR_EACH_SAFE (br, node, &all_bridges) { bridge_destroy(br, delete_datapath); } ovsdb_idl_destroy(idl); } /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP * addresses and ports into '*managersp' and '*n_managersp'. The caller is * responsible for freeing '*managersp' (with free()). * * You may be asking yourself "why does ovs-vswitchd care?", because * ovsdb-server is responsible for connecting to the managers, and ovs-vswitchd * should not be and in fact is not directly involved in that. But * ovs-vswitchd needs to make sure that ovsdb-server can reach the managers, so * it has to tell in-band control where the managers are to enable that. * (Thus, only managers connected in-band and with non-loopback addresses * are collected.) */ static void collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg, struct sockaddr_in **managersp, size_t *n_managersp) { struct sockaddr_in *managers = NULL; size_t n_managers = 0; struct sset targets; size_t i; /* Collect all of the potential targets from the "targets" columns of the * rows pointed to by "manager_options", excluding any that are * out-of-band. */ sset_init(&targets); for (i = 0; i < ovs_cfg->n_manager_options; i++) { struct ovsrec_manager *m = ovs_cfg->manager_options[i]; if (m->connection_mode && !strcmp(m->connection_mode, "out-of-band")) { sset_find_and_delete(&targets, m->target); } else { sset_add(&targets, m->target); } } /* Now extract the targets' IP addresses. */ if (!sset_is_empty(&targets)) { const char *target; managers = xmalloc(sset_count(&targets) * sizeof *managers); SSET_FOR_EACH (target, &targets) { union { struct sockaddr_storage ss; struct sockaddr_in in; } sa; /* Ignore loopback. */ if (stream_parse_target_with_default_port(target, OVSDB_PORT, &sa.ss) && sa.ss.ss_family == AF_INET && sa.in.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { managers[n_managers++] = sa.in; } } } sset_destroy(&targets); *managersp = managers; *n_managersp = n_managers; } static void config_ofproto_types(const struct smap *other_config) { struct sset types; const char *type; /* Pass custom configuration to datapath types. */ sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_set_config(type, other_config); } sset_destroy(&types); } static void get_timeout_policy_from_ovsrec(struct simap *tp, const struct ovsrec_ct_timeout_policy *tp_cfg) { if (tp_cfg) { for (size_t i = 0; i < tp_cfg->n_timeouts; i++) { simap_put(tp, tp_cfg->key_timeouts[i], tp_cfg->value_timeouts[i]); } } } static struct ct_zone * ct_zone_lookup(struct hmap *ct_zones, uint16_t zone_id) { struct ct_zone *ct_zone; HMAP_FOR_EACH_WITH_HASH (ct_zone, node, hash_int(zone_id, 0), ct_zones) { if (ct_zone->zone_id == zone_id) { return ct_zone; } } return NULL; } static struct ct_zone * ct_zone_alloc(uint16_t zone_id, struct ovsrec_ct_timeout_policy *tp_cfg) { struct ct_zone *ct_zone = xzalloc(sizeof *ct_zone); ct_zone->zone_id = zone_id; ct_zone->limit = -1; simap_init(&ct_zone->tp); get_timeout_policy_from_ovsrec(&ct_zone->tp, tp_cfg); return ct_zone; } static void ct_zone_remove_and_destroy(struct datapath *dp, struct ct_zone *ct_zone) { if (!simap_is_empty(&ct_zone->tp)) { ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id); } if (ct_zone->limit > -1) { ofproto_ct_zone_limit_update(dp->type, ct_zone->zone_id, NULL); } hmap_remove(&dp->ct_zones, &ct_zone->node); simap_destroy(&ct_zone->tp); free(ct_zone); } /* Replace 'old_tp' by 'new_tp' (destroyed 'new_tp'). Returns true if 'old_tp' * and 'new_tp' contains different data, false if they are the same. */ static bool update_timeout_policy(struct simap *old_tp, struct simap *new_tp) { bool changed = !simap_equal(old_tp, new_tp); if (changed) { simap_swap(old_tp, new_tp); } simap_destroy(new_tp); return changed; } static struct datapath * datapath_lookup(const char *type) { struct datapath *dp; HMAP_FOR_EACH_WITH_HASH (dp, node, hash_string(type, 0), &all_datapaths) { if (!strcmp(dp->type, type)) { return dp; } } return NULL; } static struct datapath * datapath_create(const char *type) { struct datapath *dp = xzalloc(sizeof *dp); dp->type = xstrdup(type); dp->ct_zone_default_limit = -1; hmap_init(&dp->ct_zones); hmap_insert(&all_datapaths, &dp->node, hash_string(type, 0)); smap_init(&dp->caps); return dp; } static void datapath_destroy(struct datapath *dp) { if (dp) { struct ct_zone *ct_zone; HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) { ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id); ct_zone_remove_and_destroy(dp, ct_zone); } if (dp->ct_zone_default_limit > -1) { ofproto_ct_zone_limit_update(dp->type, OVS_ZONE_LIMIT_DEFAULT_ZONE, NULL); } ofproto_ct_zone_limit_protection_update(dp->type, false); hmap_remove(&all_datapaths, &dp->node); hmap_destroy(&dp->ct_zones); free(dp->type); smap_destroy(&dp->caps); free(dp); } } static void ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg) { struct ct_zone *ct_zone; bool protected = false; /* Add new 'ct_zone's or update existing 'ct_zone's based on the database * state. */ for (size_t i = 0; i < dp_cfg->n_ct_zones; i++) { uint16_t zone_id = dp_cfg->key_ct_zones[i]; struct ovsrec_ct_zone *zone_cfg = dp_cfg->value_ct_zones[i]; struct ovsrec_ct_timeout_policy *tp_cfg = zone_cfg->timeout_policy; ct_zone = ct_zone_lookup(&dp->ct_zones, zone_id); if (!ct_zone) { ct_zone = ct_zone_alloc(zone_id, tp_cfg); hmap_insert(&dp->ct_zones, &ct_zone->node, hash_int(zone_id, 0)); } struct simap new_tp = SIMAP_INITIALIZER(&new_tp); get_timeout_policy_from_ovsrec(&new_tp, tp_cfg); if (update_timeout_policy(&ct_zone->tp, &new_tp)) { if (simap_count(&ct_zone->tp)) { ofproto_ct_set_zone_timeout_policy(dp->type, ct_zone->zone_id, &ct_zone->tp); } else { ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id); } } int64_t desired_limit = zone_cfg->limit ? *zone_cfg->limit : -1; if (ct_zone->limit != desired_limit) { ofproto_ct_zone_limit_update(dp->type, zone_id, zone_cfg->limit); ct_zone->limit = desired_limit; } ct_zone->last_used = idl_seqno; protected = protected || !!zone_cfg->limit; } /* Purge 'ct_zone's no longer found in the database. */ HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) { if (ct_zone->last_used != idl_seqno) { ct_zone_remove_and_destroy(dp, ct_zone); } } /* Reconfigure default CT zone limit if needed. */ int64_t default_limit = dp_cfg->ct_zone_default_limit ? *dp_cfg->ct_zone_default_limit : -1; if (dp->ct_zone_default_limit != default_limit) { ofproto_ct_zone_limit_update(dp->type, OVS_ZONE_LIMIT_DEFAULT_ZONE, dp_cfg->ct_zone_default_limit); dp->ct_zone_default_limit = default_limit; } protected = protected || !!dp_cfg->ct_zone_default_limit; ofproto_ct_zone_limit_protection_update(dp->type, protected); } static void dp_capability_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg) { struct smap_node *node; struct smap cap; smap_init(&cap); ofproto_get_datapath_cap(dp->type, &cap); SMAP_FOR_EACH (node, &cap) { ovsrec_datapath_update_capabilities_setkey(dp_cfg, node->key, node->value); } smap_destroy(&cap); } static void datapath_reconfigure(const struct ovsrec_open_vswitch *cfg) { struct datapath *dp; /* Add new 'datapath's or update existing ones. */ for (size_t i = 0; i < cfg->n_datapaths; i++) { struct ovsrec_datapath *dp_cfg = cfg->value_datapaths[i]; char *dp_name = cfg->key_datapaths[i]; dp = datapath_lookup(dp_name); if (!dp) { dp = datapath_create(dp_name); dp_capability_reconfigure(dp, dp_cfg); } dp->last_used = idl_seqno; ct_zones_reconfigure(dp, dp_cfg); } /* Purge deleted 'datapath's. */ HMAP_FOR_EACH_SAFE (dp, node, &all_datapaths) { if (dp->last_used != idl_seqno) { datapath_destroy(dp); } } } static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { struct sockaddr_in *managers; struct bridge *br; int sflow_bridge_number; size_t n_managers; COVERAGE_INC(bridge_reconfigure); ofproto_set_flow_limit(smap_get_uint(&ovs_cfg->other_config, "flow-limit", OFPROTO_FLOW_LIMIT_DEFAULT)); ofproto_set_max_idle(smap_get_uint(&ovs_cfg->other_config, "max-idle", OFPROTO_MAX_IDLE_DEFAULT)); ofproto_set_max_revalidator(smap_get_uint(&ovs_cfg->other_config, "max-revalidator", OFPROTO_MAX_REVALIDATOR_DEFAULT)); ofproto_set_min_revalidate_pps( smap_get_uint(&ovs_cfg->other_config, "min-revalidate-pps", OFPROTO_MIN_REVALIDATE_PPS_DEFAULT)); ofproto_set_offloaded_stats_delay( smap_get_uint(&ovs_cfg->other_config, "offloaded-stats-delay", OFPROTO_OFFLOADED_STATS_DELAY)); ofproto_set_vlan_limit(smap_get_int(&ovs_cfg->other_config, "vlan-limit", LEGACY_MAX_VLAN_HEADERS)); ofproto_set_bundle_idle_timeout(smap_get_uint(&ovs_cfg->other_config, "bundle-idle-timeout", 0)); ofproto_set_threads( smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0), smap_get_int(&ovs_cfg->other_config, "n-revalidator-threads", 0)); ofproto_set_explicit_sampled_drops( smap_get_bool(&ovs_cfg->other_config, "explicit-sampled-drops", OFPROTO_EXPLICIT_SAMPLED_DROPS_DEFAULT)); /* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according * to 'ovs_cfg', with only very minimal configuration otherwise. * * This is mostly an update to bridge data structures. Nothing is pushed * down to ofproto or lower layers. */ add_del_bridges(ovs_cfg); HMAP_FOR_EACH (br, node, &all_bridges) { bridge_collect_wanted_ports(br, &br->wanted_ports); bridge_del_ports(br, &br->wanted_ports); } /* Start pushing configuration changes down to the ofproto layer: * * - Delete ofprotos that are no longer configured. * * - Delete ports that are no longer configured. * * - Reconfigure existing ports to their desired configurations, or * delete them if not possible. * * We have to do all the deletions before we can do any additions, because * the ports to be added might require resources that will be freed up by * deletions (they might especially overlap in name). */ bridge_delete_ofprotos(); HMAP_FOR_EACH (br, node, &all_bridges) { if (br->ofproto) { bridge_delete_or_reconfigure_ports(br); } } /* Finish pushing configuration changes to the ofproto layer: * * - Create ofprotos that are missing. * * - Add ports that are missing. */ HMAP_FOR_EACH_SAFE (br, node, &all_bridges) { if (!br->ofproto) { int error; error = ofproto_create(br->name, br->type, &br->ofproto); if (error) { VLOG_ERR("failed to create bridge %s: %s", br->name, ovs_strerror(error)); shash_destroy(&br->wanted_ports); bridge_destroy(br, true); } else { /* Trigger storing datapath version. */ seq_change(connectivity_seq_get()); } } } config_ofproto_types(&ovs_cfg->other_config); HMAP_FOR_EACH (br, node, &all_bridges) { bridge_add_ports(br, &br->wanted_ports); shash_destroy(&br->wanted_ports); } reconfigure_system_stats(ovs_cfg); datapath_reconfigure(ovs_cfg); /* Complete the configuration. */ sflow_bridge_number = 0; collect_in_band_managers(ovs_cfg, &managers, &n_managers); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; /* We need the datapath ID early to allow LACP ports to use it as the * default system ID. */ bridge_configure_datapath_id(br); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; port_configure(port); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_set_ofport(iface->cfg, iface->ofp_port); /* Clear eventual previous errors */ ovsrec_interface_set_error(iface->cfg, NULL); iface_configure_cfm(iface); iface_configure_qos(iface, port->cfg->qos); iface_set_mac(br, port, iface); ofproto_port_set_bfd(br->ofproto, iface->ofp_port, &iface->cfg->bfd); ofproto_port_set_lldp(br->ofproto, iface->ofp_port, &iface->cfg->lldp); ofproto_port_set_config(br->ofproto, iface->ofp_port, &iface->cfg->other_config); } } bridge_configure_mirrors(br); bridge_configure_forward_bpdu(br); bridge_configure_mac_table(br); bridge_configure_mcast_snooping(br); bridge_configure_remotes(br, managers, n_managers); bridge_configure_netflow(br); bridge_configure_sflow(br, &sflow_bridge_number); bridge_configure_ipfix(br); bridge_configure_lsample(br); bridge_configure_spanning_tree(br); bridge_configure_tables(br); bridge_configure_dp_desc(br); bridge_configure_serial_desc(br); bridge_configure_aa(br); } free(managers); /* The ofproto-dpif provider does some final reconfiguration in its * ->type_run() function. We have to call it before notifying the database * client that reconfiguration is complete, otherwise there is a very * narrow race window in which e.g. ofproto/trace will not recognize the * new configuration (sometimes this causes unit test failures). */ bridge_run__(); } /* Delete ofprotos which aren't configured or have the wrong type. Create * ofprotos which don't exist but need to. */ static void bridge_delete_ofprotos(void) { struct bridge *br; struct sset names; struct sset types; const char *type; /* Delete ofprotos with no bridge or with the wrong type. */ sset_init(&names); sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { const char *name; ofproto_enumerate_names(type, &names); SSET_FOR_EACH (name, &names) { br = bridge_lookup(name); if (!br || strcmp(type, br->type)) { ofproto_delete(name, type); } } } sset_destroy(&names); sset_destroy(&types); } static ofp_port_t * add_ofp_port(ofp_port_t port, ofp_port_t *ports, size_t *n, size_t *allocated) { if (*n >= *allocated) { ports = x2nrealloc(ports, allocated, sizeof *ports); } ports[(*n)++] = port; return ports; } /* Configures the MTU of 'netdev' based on the "mtu_request" column * in 'iface_cfg'. */ static int iface_set_netdev_mtu(const struct ovsrec_interface *iface_cfg, struct netdev *netdev) { if (iface_cfg->n_mtu_request == 1) { /* The user explicitly asked for this MTU. */ netdev_mtu_user_config(netdev, true); /* Try to set the MTU to the requested value. */ return netdev_set_mtu(netdev, *iface_cfg->mtu_request); } /* The user didn't explicitly asked for any MTU. */ netdev_mtu_user_config(netdev, false); return 0; } static void bridge_delete_or_reconfigure_ports(struct bridge *br) { struct ofproto_port ofproto_port; struct ofproto_port_dump dump; struct sset ofproto_ports; struct port *port; /* List of "ofp_port"s to delete. We make a list instead of deleting them * right away because ofproto implementations aren't necessarily able to * iterate through a changing list of ports in an entirely robust way. */ ofp_port_t *del; size_t n, allocated; size_t i; del = NULL; n = allocated = 0; sset_init(&ofproto_ports); /* Main task: Iterate over the ports in 'br->ofproto' and remove the ports * that are not configured in the database. (This commonly happens when * ports have been deleted, e.g. with "ovs-vsctl del-port".) * * Side tasks: Reconfigure the ports that are still in 'br'. Delete ports * that have the wrong OpenFlow port number (and arrange to add them back * with the correct OpenFlow port number). */ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) { ofp_port_t requested_ofp_port; struct iface *iface; sset_add(&ofproto_ports, ofproto_port.name); iface = iface_lookup(br, ofproto_port.name); if (!iface) { /* No such iface is configured, so we should delete this * ofproto_port. * * As a corner case exception, keep the port if it's a bond fake * interface. */ if (bridge_has_bond_fake_iface(br, ofproto_port.name) && !strcmp(ofproto_port.type, "internal")) { continue; } goto delete; } const char *netdev_type = ofproto_port_open_type(br->ofproto, iface->type); if (strcmp(ofproto_port.type, netdev_type) || netdev_set_config(iface->netdev, &iface->cfg->options, NULL)) { /* The interface is the wrong type or can't be configured. * Delete it. */ goto delete; } iface_set_netdev_mtu(iface->cfg, iface->netdev); /* If the requested OpenFlow port for 'iface' changed, and it's not * already the correct port, then we might want to temporarily delete * this interface, so we can add it back again with the new OpenFlow * port number. */ requested_ofp_port = iface_get_requested_ofp_port(iface->cfg); if (iface->ofp_port != OFPP_LOCAL && requested_ofp_port != OFPP_NONE && requested_ofp_port != iface->ofp_port) { ofp_port_t victim_request; struct iface *victim; /* Check for an existing OpenFlow port currently occupying * 'iface''s requested port number. If there isn't one, then * delete this port. Otherwise we need to consider further. */ victim = iface_from_ofp_port(br, requested_ofp_port); if (!victim) { goto delete; } /* 'victim' is a port currently using 'iface''s requested port * number. Unless 'victim' specifically requested that port * number, too, then we can delete both 'iface' and 'victim' * temporarily. (We'll add both of them back again later with new * OpenFlow port numbers.) * * If 'victim' did request port number 'requested_ofp_port', just * like 'iface', then that's a configuration inconsistency that we * can't resolve. We might as well let it keep its current port * number. */ victim_request = iface_get_requested_ofp_port(victim->cfg); if (victim_request != requested_ofp_port) { del = add_ofp_port(victim->ofp_port, del, &n, &allocated); iface_destroy(victim); goto delete; } } /* Keep it. */ continue; delete: iface_destroy(iface); del = add_ofp_port(ofproto_port.ofp_port, del, &n, &allocated); } for (i = 0; i < n; i++) { ofproto_port_del(br->ofproto, del[i]); } free(del); /* Iterate over this module's idea of interfaces in 'br'. Remove any ports * that we didn't see when we iterated through the datapath, i.e. ports * that disappeared underneath use. This is an unusual situation, but it * can happen in some cases: * * - An admin runs a command like "ovs-dpctl del-port" (which is a bad * idea but could happen). * * - The port represented a device that disappeared, e.g. a tuntap * device destroyed via "tunctl -d", a physical Ethernet device * whose module was just unloaded via "rmmod", or a virtual NIC for a * VM whose VM was just terminated. */ HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) { struct iface *iface; LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) { if (!sset_contains(&ofproto_ports, iface->name)) { iface_destroy__(iface); } } if (ovs_list_is_empty(&port->ifaces)) { port_destroy(port); } } sset_destroy(&ofproto_ports); } static void bridge_add_ports__(struct bridge *br, const struct shash *wanted_ports, bool with_requested_port) { struct shash_node *port_node; SHASH_FOR_EACH (port_node, wanted_ports) { const struct ovsrec_port *port_cfg = port_node->data; size_t i; for (i = 0; i < port_cfg->n_interfaces; i++) { const struct ovsrec_interface *iface_cfg = port_cfg->interfaces[i]; ofp_port_t requested_ofp_port; requested_ofp_port = iface_get_requested_ofp_port(iface_cfg); if ((requested_ofp_port != OFPP_NONE) == with_requested_port) { struct iface *iface = iface_lookup(br, iface_cfg->name); if (!iface) { iface_create(br, iface_cfg, port_cfg); } } } } } static void bridge_add_ports(struct bridge *br, const struct shash *wanted_ports) { /* First add interfaces that request a particular port number. */ bridge_add_ports__(br, wanted_ports, true); /* Then add interfaces that want automatic port number assignment. * We add these afterward to avoid accidentally taking a specifically * requested port number. */ bridge_add_ports__(br, wanted_ports, false); } static void port_configure(struct port *port) { const struct ovsrec_port *cfg = port->cfg; struct bond_settings bond_settings; struct lacp_settings lacp_settings; struct ofproto_bundle_settings s; struct iface *iface; /* Get name. */ s.name = port->name; /* Get members. */ s.n_members = 0; s.members = xmalloc(ovs_list_size(&port->ifaces) * sizeof *s.members); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { s.members[s.n_members++] = iface->ofp_port; } /* Get VLAN tag. */ s.vlan = -1; if (cfg->tag && *cfg->tag >= 0 && *cfg->tag <= 4095) { s.vlan = *cfg->tag; } /* Get VLAN trunks. */ s.trunks = NULL; if (cfg->n_trunks) { s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks); } s.cvlans = NULL; if (cfg->n_cvlans) { s.cvlans = vlan_bitmap_from_array(cfg->cvlans, cfg->n_cvlans); } /* Get VLAN mode. */ if (cfg->vlan_mode) { if (!strcmp(cfg->vlan_mode, "access")) { s.vlan_mode = PORT_VLAN_ACCESS; } else if (!strcmp(cfg->vlan_mode, "trunk")) { s.vlan_mode = PORT_VLAN_TRUNK; } else if (!strcmp(cfg->vlan_mode, "native-tagged")) { s.vlan_mode = PORT_VLAN_NATIVE_TAGGED; } else if (!strcmp(cfg->vlan_mode, "native-untagged")) { s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED; } else if (!strcmp(cfg->vlan_mode, "dot1q-tunnel")) { s.vlan_mode = PORT_VLAN_DOT1Q_TUNNEL; } else { /* This "can't happen" because ovsdb-server should prevent it. */ VLOG_WARN("port %s: unknown VLAN mode %s, falling " "back to trunk mode", port->name, cfg->vlan_mode); s.vlan_mode = PORT_VLAN_TRUNK; } } else { if (s.vlan >= 0) { s.vlan_mode = PORT_VLAN_ACCESS; if (cfg->n_trunks || cfg->n_cvlans) { VLOG_WARN("port %s: ignoring trunks in favor of implicit vlan", port->name); } } else { s.vlan_mode = PORT_VLAN_TRUNK; } } const char *qe = smap_get_def(&cfg->other_config, "qinq-ethtype", ""); s.qinq_ethtype = (!strcmp(qe, "802.1q") ? ETH_TYPE_VLAN_8021Q : ETH_TYPE_VLAN_8021AD); const char *pt = smap_get_def(&cfg->other_config, "priority-tags", ""); if (!strcmp(pt, "if-nonzero") || !strcmp(pt, "true")) { s.use_priority_tags = PORT_PRIORITY_TAGS_IF_NONZERO; } else if (!strcmp(pt, "always")) { s.use_priority_tags = PORT_PRIORITY_TAGS_ALWAYS; } else { s.use_priority_tags = PORT_PRIORITY_TAGS_NEVER; } /* Get LACP settings. */ s.lacp = port_configure_lacp(port, &lacp_settings); if (s.lacp) { size_t i = 0; s.lacp_members = xmalloc(s.n_members * sizeof *s.lacp_members); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_configure_lacp(iface, &s.lacp_members[i++]); } } else { s.lacp_members = NULL; } /* Get bond settings. */ if (s.n_members > 1) { s.bond = &bond_settings; port_configure_bond(port, &bond_settings); } else { s.bond = NULL; LIST_FOR_EACH (iface, port_elem, &port->ifaces) { netdev_set_miimon_interval(iface->netdev, 0); } } /* Protected port mode */ s.protected = cfg->protected_; /* Register. */ ofproto_bundle_register(port->bridge->ofproto, port, &s); /* Clean up. */ free(s.cvlans); free(s.members); free(s.trunks); free(s.lacp_members); } /* Pick local port hardware address and datapath ID for 'br'. */ static void bridge_configure_datapath_id(struct bridge *br) { struct eth_addr ea; uint64_t dpid; struct iface *local_iface; struct iface *hw_addr_iface; char *dpid_string; bridge_pick_local_hw_addr(br, &ea, &hw_addr_iface); local_iface = iface_from_ofp_port(br, OFPP_LOCAL); if (local_iface) { int error = netdev_set_etheraddr(local_iface->netdev, ea); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge " "Ethernet address: %s", br->name, ovs_strerror(error)); } } br->ea = ea; dpid = bridge_pick_datapath_id(br, ea); if (dpid != ofproto_get_datapath_id(br->ofproto)) { VLOG_INFO("bridge %s: using datapath ID %016"PRIx64, br->name, dpid); ofproto_set_datapath_id(br->ofproto, dpid); } dpid_string = xasprintf("%016"PRIx64, dpid); ovsrec_bridge_set_datapath_id(br->cfg, dpid_string); free(dpid_string); } /* Returns a bitmap of "enum ofputil_protocol"s that are allowed for use with * 'br'. */ static uint32_t bridge_get_allowed_versions(struct bridge *br) { if (!br->cfg->n_protocols) { return 0; } return ofputil_versions_from_strings(br->cfg->protocols, br->cfg->n_protocols); } static int bridge_get_controller_queue_size(struct bridge *br, struct ovsrec_controller *c) { if (c && c->controller_queue_size) { return *c->controller_queue_size; } int queue_size = smap_get_int(&br->cfg->other_config, "controller-queue-size", BRIDGE_CONTROLLER_PACKET_QUEUE_DEFAULT_SIZE); if (queue_size < BRIDGE_CONTROLLER_PACKET_QUEUE_MIN_SIZE || queue_size > BRIDGE_CONTROLLER_PACKET_QUEUE_MAX_SIZE) { return BRIDGE_CONTROLLER_PACKET_QUEUE_DEFAULT_SIZE; } return queue_size; } /* Set NetFlow configuration on 'br'. */ static void bridge_configure_netflow(struct bridge *br) { struct ovsrec_netflow *cfg = br->cfg->netflow; struct netflow_options opts; if (!cfg) { ofproto_set_netflow(br->ofproto, NULL); return; } memset(&opts, 0, sizeof opts); /* Get default NetFlow configuration from datapath. * Apply overrides from 'cfg'. */ ofproto_get_netflow_ids(br->ofproto, &opts.engine_type, &opts.engine_id); if (cfg->engine_type) { opts.engine_type = *cfg->engine_type; } if (cfg->engine_id) { opts.engine_id = *cfg->engine_id; } /* Configure active timeout interval. */ opts.active_timeout = cfg->active_timeout; if (!opts.active_timeout) { opts.active_timeout = -1; } else if (opts.active_timeout < 0) { VLOG_WARN("bridge %s: active timeout interval set to negative " "value, using default instead (%d seconds)", br->name, NF_ACTIVE_TIMEOUT_DEFAULT); opts.active_timeout = -1; } /* Add engine ID to interface number to disambiguate bridgs? */ opts.add_id_to_iface = cfg->add_id_to_interface; if (opts.add_id_to_iface) { if (opts.engine_id > 0x7f) { VLOG_WARN("bridge %s: NetFlow port mangling may conflict with " "another vswitch, choose an engine id less than 128", br->name); } if (hmap_count(&br->ports) > 508) { VLOG_WARN("bridge %s: NetFlow port mangling will conflict with " "another port when more than 508 ports are used", br->name); } } /* Collectors. */ sset_init(&opts.collectors); sset_add_array(&opts.collectors, cfg->targets, cfg->n_targets); /* Configure. */ if (ofproto_set_netflow(br->ofproto, &opts)) { VLOG_ERR("bridge %s: problem setting netflow collectors", br->name); } sset_destroy(&opts.collectors); } /* Set sFlow configuration on 'br'. */ static void bridge_configure_sflow(struct bridge *br, int *sflow_bridge_number) { const struct ovsrec_sflow *cfg = br->cfg->sflow; struct ovsrec_controller **controllers; struct ofproto_sflow_options oso; size_t n_controllers; size_t i; if (!cfg) { ofproto_set_sflow(br->ofproto, NULL); return; } memset(&oso, 0, sizeof oso); sset_init(&oso.targets); sset_add_array(&oso.targets, cfg->targets, cfg->n_targets); oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; if (cfg->sampling) { oso.sampling_rate = *cfg->sampling; } oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL; if (cfg->polling) { oso.polling_interval = *cfg->polling; } oso.header_len = SFL_DEFAULT_HEADER_SIZE; if (cfg->header) { oso.header_len = *cfg->header; } oso.sub_id = (*sflow_bridge_number)++; oso.agent_device = cfg->agent; oso.control_ip = NULL; n_controllers = bridge_get_controllers(br, &controllers); for (i = 0; i < n_controllers; i++) { if (controllers[i]->local_ip) { oso.control_ip = controllers[i]->local_ip; break; } } ofproto_set_sflow(br->ofproto, &oso); sset_destroy(&oso.targets); } /* Returns whether a IPFIX row is valid. */ static bool ovsrec_ipfix_is_valid(const struct ovsrec_ipfix *ipfix) { return ipfix && ipfix->n_targets > 0; } /* Returns whether a Flow_Sample_Collector_Set row contains a valid IPFIX * configuration. */ static bool ovsrec_fscs_is_valid_ipfix(const struct ovsrec_flow_sample_collector_set *fscs, const struct bridge *br) { return ovsrec_ipfix_is_valid(fscs->ipfix) && fscs->bridge == br->cfg; } /* Set IPFIX configuration on 'br'. */ static void bridge_configure_ipfix(struct bridge *br) { const struct ovsrec_ipfix *be_cfg = br->cfg->ipfix; bool valid_be_cfg = ovsrec_ipfix_is_valid(be_cfg); const struct ovsrec_flow_sample_collector_set *fe_cfg; struct ofproto_ipfix_bridge_exporter_options be_opts; struct ofproto_ipfix_flow_exporter_options *fe_opts = NULL; size_t n_fe_opts = 0; const char *virtual_obs_id; OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) { if (ovsrec_fscs_is_valid_ipfix(fe_cfg, br)) { n_fe_opts++; } } if (!valid_be_cfg && n_fe_opts == 0) { ofproto_set_ipfix(br->ofproto, NULL, NULL, 0); return; } if (valid_be_cfg) { memset(&be_opts, 0, sizeof be_opts); sset_init(&be_opts.targets); sset_add_array(&be_opts.targets, be_cfg->targets, be_cfg->n_targets); if (be_cfg->sampling) { be_opts.sampling_rate = *be_cfg->sampling; } else { be_opts.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; } if (be_cfg->obs_domain_id) { be_opts.obs_domain_id = *be_cfg->obs_domain_id; } if (be_cfg->obs_point_id) { be_opts.obs_point_id = *be_cfg->obs_point_id; } if (be_cfg->cache_active_timeout) { be_opts.cache_active_timeout = *be_cfg->cache_active_timeout; } if (be_cfg->cache_max_flows) { be_opts.cache_max_flows = *be_cfg->cache_max_flows; } if (be_cfg->stats_interval) { be_opts.stats_interval = *be_cfg->stats_interval; } else { be_opts.stats_interval = OFPROTO_IPFIX_DEFAULT_TEMPLATE_INTERVAL; } if (be_cfg->template_interval) { be_opts.template_interval = *be_cfg->template_interval; } else { be_opts.template_interval = OFPROTO_IPFIX_DEFAULT_TEMPLATE_INTERVAL; } be_opts.enable_tunnel_sampling = smap_get_bool(&be_cfg->other_config, "enable-tunnel-sampling", true); be_opts.enable_input_sampling = smap_get_bool(&be_cfg->other_config, "enable-input-sampling", true); be_opts.enable_output_sampling = smap_get_bool(&be_cfg->other_config, "enable-output-sampling", true); virtual_obs_id = smap_get(&be_cfg->other_config, "virtual_obs_id"); be_opts.virtual_obs_id = nullable_xstrdup(virtual_obs_id); } if (n_fe_opts > 0) { struct ofproto_ipfix_flow_exporter_options *opts; fe_opts = xcalloc(n_fe_opts, sizeof *fe_opts); opts = fe_opts; OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) { if (ovsrec_fscs_is_valid_ipfix(fe_cfg, br)) { opts->collector_set_id = fe_cfg->id; sset_init(&opts->targets); sset_add_array(&opts->targets, fe_cfg->ipfix->targets, fe_cfg->ipfix->n_targets); opts->cache_active_timeout = fe_cfg->ipfix->cache_active_timeout ? *fe_cfg->ipfix->cache_active_timeout : 0; opts->cache_max_flows = fe_cfg->ipfix->cache_max_flows ? *fe_cfg->ipfix->cache_max_flows : 0; opts->stats_interval = fe_cfg->ipfix->stats_interval ? *fe_cfg->ipfix->stats_interval : OFPROTO_IPFIX_DEFAULT_TEMPLATE_INTERVAL; opts->template_interval = fe_cfg->ipfix->template_interval ? *fe_cfg->ipfix->template_interval : OFPROTO_IPFIX_DEFAULT_TEMPLATE_INTERVAL; opts->enable_tunnel_sampling = smap_get_bool( &fe_cfg->ipfix->other_config, "enable-tunnel-sampling", true); virtual_obs_id = smap_get(&fe_cfg->ipfix->other_config, "virtual_obs_id"); opts->virtual_obs_id = nullable_xstrdup(virtual_obs_id); opts++; } } } ofproto_set_ipfix(br->ofproto, valid_be_cfg ? &be_opts : NULL, fe_opts, n_fe_opts); if (valid_be_cfg) { sset_destroy(&be_opts.targets); free(be_opts.virtual_obs_id); } if (n_fe_opts > 0) { struct ofproto_ipfix_flow_exporter_options *opts = fe_opts; size_t i; for (i = 0; i < n_fe_opts; i++) { sset_destroy(&opts->targets); free(opts->virtual_obs_id); opts++; } free(fe_opts); } } /* Returns whether a Flow_Sample_Collector_Set row contains a valid local * sampling configuration. */ static bool ovsrec_fscs_is_valid_local(const struct ovsrec_flow_sample_collector_set *fscs, const struct bridge *br) { return fscs->local_group_id && fscs->n_local_group_id == 1 && fscs->bridge == br->cfg; } /* Set local sample configuration on 'br'. */ static void bridge_configure_lsample(struct bridge *br) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct ovsrec_flow_sample_collector_set *fscs; struct ofproto_lsample_options *opts_array, *opts; size_t n_opts = 0; int ret; /* Iterate the Flow_Sample_Collector_Set table twice. * First to get the number of valid configuration entries, then to process * each of them and build an array of options. */ OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH (fscs, idl) { if (ovsrec_fscs_is_valid_local(fscs, br)) { n_opts++; } } if (n_opts == 0) { ofproto_set_local_sample(br->ofproto, NULL, 0); return; } opts_array = xcalloc(n_opts, sizeof *opts_array); opts = opts_array; OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH (fscs, idl) { if (!ovsrec_fscs_is_valid_local(fscs, br)) { continue; } opts->collector_set_id = fscs->id; opts->group_id = *fscs->local_group_id; opts++; } ret = ofproto_set_local_sample(br->ofproto, opts_array, n_opts); if (ret == EOPNOTSUPP) { if (n_opts) { VLOG_WARN_RL(&rl, "bridge %s: ignoring local sampling configuration: " "not supported by this datapath", br->name); } } else if (ret) { VLOG_ERR_RL(&rl, "bridge %s: error configuring local sampling: %s", br->name, ovs_strerror(ret)); } if (n_opts > 0) { free(opts_array); } } static void port_configure_stp(const struct ofproto *ofproto, struct port *port, struct ofproto_port_stp_settings *port_s, int *port_num_counter, unsigned long *port_num_bitmap) { const char *config_str; struct iface *iface; if (!smap_get_bool(&port->cfg->other_config, "stp-enable", true)) { port_s->enable = false; return; } else { port_s->enable = true; } /* STP over bonds is not supported. */ if (!ovs_list_is_singleton(&port->ifaces)) { VLOG_ERR("port %s: cannot enable STP on bonds, disabling", port->name); port_s->enable = false; return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); /* Internal ports shouldn't participate in spanning tree, so * skip them. */ if (!strcmp(iface->type, "internal")) { VLOG_DBG("port %s: disable STP on internal ports", port->name); port_s->enable = false; return; } /* STP on mirror output ports is not supported. */ if (ofproto_is_mirror_output_bundle(ofproto, port)) { VLOG_DBG("port %s: disable STP on mirror ports", port->name); port_s->enable = false; return; } config_str = smap_get(&port->cfg->other_config, "stp-port-num"); if (config_str) { unsigned long int port_num = strtoul(config_str, NULL, 0); int port_idx = port_num - 1; if (port_num < 1 || port_num > STP_MAX_PORTS) { VLOG_ERR("port %s: invalid stp-port-num", port->name); port_s->enable = false; return; } if (bitmap_is_set(port_num_bitmap, port_idx)) { VLOG_ERR("port %s: duplicate stp-port-num %lu, disabling", port->name, port_num); port_s->enable = false; return; } bitmap_set1(port_num_bitmap, port_idx); port_s->port_num = port_idx; } else { if (*port_num_counter >= STP_MAX_PORTS) { VLOG_ERR("port %s: too many STP ports, disabling", port->name); port_s->enable = false; return; } port_s->port_num = (*port_num_counter)++; } config_str = smap_get(&port->cfg->other_config, "stp-path-cost"); if (config_str) { port_s->path_cost = strtoul(config_str, NULL, 10); } else { uint32_t mbps; netdev_get_speed(iface->netdev, &mbps, NULL); if (!mbps) { mbps = NETDEV_DEFAULT_BPS / 1000000; } port_s->path_cost = stp_convert_speed_to_cost(mbps); } config_str = smap_get(&port->cfg->other_config, "stp-port-priority"); if (config_str) { port_s->priority = strtoul(config_str, NULL, 0); } else { port_s->priority = STP_DEFAULT_PORT_PRIORITY; } } static void port_configure_rstp(const struct ofproto *ofproto, struct port *port, struct ofproto_port_rstp_settings *port_s, int *port_num_counter) { const char *config_str; struct iface *iface; if (!smap_get_bool(&port->cfg->other_config, "rstp-enable", true)) { port_s->enable = false; return; } else { port_s->enable = true; } /* RSTP over bonds is not supported. */ if (!ovs_list_is_singleton(&port->ifaces)) { VLOG_ERR("port %s: cannot enable RSTP on bonds, disabling", port->name); port_s->enable = false; return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); /* Internal ports shouldn't participate in spanning tree, so * skip them. */ if (!strcmp(iface->type, "internal")) { VLOG_DBG("port %s: disable RSTP on internal ports", port->name); port_s->enable = false; return; } /* RSTP on mirror output ports is not supported. */ if (ofproto_is_mirror_output_bundle(ofproto, port)) { VLOG_DBG("port %s: disable RSTP on mirror ports", port->name); port_s->enable = false; return; } config_str = smap_get(&port->cfg->other_config, "rstp-port-num"); if (config_str) { unsigned long int port_num = strtoul(config_str, NULL, 0); if (port_num < 1 || port_num > RSTP_MAX_PORTS) { VLOG_ERR("port %s: invalid rstp-port-num", port->name); port_s->enable = false; return; } port_s->port_num = port_num; } else { if (*port_num_counter >= RSTP_MAX_PORTS) { VLOG_ERR("port %s: too many RSTP ports, disabling", port->name); port_s->enable = false; return; } /* If rstp-port-num is not specified, use 0. * rstp_port_set_port_number() will look for the first free one. */ port_s->port_num = 0; } /* Increment the port num counter, because we only support * RSTP_MAX_PORTS rstp ports. */ (*port_num_counter)++; config_str = smap_get(&port->cfg->other_config, "rstp-path-cost"); if (config_str) { port_s->path_cost = strtoul(config_str, NULL, 10); } else { uint32_t mbps; netdev_get_speed(iface->netdev, &mbps, NULL); if (!mbps) { mbps = NETDEV_DEFAULT_BPS / 1000000; } port_s->path_cost = rstp_convert_speed_to_cost(mbps); } config_str = smap_get(&port->cfg->other_config, "rstp-port-priority"); if (config_str) { port_s->priority = strtoul(config_str, NULL, 0); } else { port_s->priority = RSTP_DEFAULT_PORT_PRIORITY; } port_s->admin_p2p_mac_state = smap_get_ullong( &port->cfg->other_config, "rstp-admin-p2p-mac", RSTP_ADMIN_P2P_MAC_FORCE_TRUE); port_s->admin_port_state = smap_get_bool(&port->cfg->other_config, "rstp-admin-port-state", true); port_s->admin_edge_port = smap_get_bool(&port->cfg->other_config, "rstp-port-admin-edge", false); port_s->auto_edge = smap_get_bool(&port->cfg->other_config, "rstp-port-auto-edge", true); port_s->mcheck = smap_get_bool(&port->cfg->other_config, "rstp-port-mcheck", false); } /* Set spanning tree configuration on 'br'. */ static void bridge_configure_stp(struct bridge *br, bool enable_stp) { if (!enable_stp) { ofproto_set_stp(br->ofproto, NULL); } else { struct ofproto_stp_settings br_s; const char *config_str; struct port *port; int port_num_counter; unsigned long *port_num_bitmap; config_str = smap_get(&br->cfg->other_config, "stp-system-id"); if (config_str) { struct eth_addr ea; if (eth_addr_from_string(config_str, &ea)) { br_s.system_id = eth_addr_to_uint64(ea); } else { br_s.system_id = eth_addr_to_uint64(br->ea); VLOG_ERR("bridge %s: invalid stp-system-id, defaulting " "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea)); } } else { br_s.system_id = eth_addr_to_uint64(br->ea); } br_s.priority = smap_get_ullong(&br->cfg->other_config, "stp-priority", STP_DEFAULT_BRIDGE_PRIORITY); br_s.hello_time = smap_get_ullong(&br->cfg->other_config, "stp-hello-time", STP_DEFAULT_HELLO_TIME); br_s.max_age = smap_get_ullong(&br->cfg->other_config, "stp-max-age", STP_DEFAULT_MAX_AGE / 1000) * 1000; br_s.fwd_delay = smap_get_ullong(&br->cfg->other_config, "stp-forward-delay", STP_DEFAULT_FWD_DELAY / 1000) * 1000; /* Configure STP on the bridge. */ if (ofproto_set_stp(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable STP", br->name); return; } /* Users must either set the port number with the "stp-port-num" * configuration on all ports or none. If manual configuration * is not done, then we allocate them sequentially. */ port_num_counter = 0; port_num_bitmap = bitmap_allocate(STP_MAX_PORTS); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_port_stp_settings port_s; struct iface *iface; port_configure_stp(br->ofproto, port, &port_s, &port_num_counter, port_num_bitmap); /* As bonds are not supported, just apply configuration to * all interfaces. */ LIST_FOR_EACH (iface, port_elem, &port->ifaces) { if (ofproto_port_set_stp(br->ofproto, iface->ofp_port, &port_s)) { VLOG_ERR("port %s: could not enable STP", port->name); continue; } } } if (bitmap_scan(port_num_bitmap, 1, 0, STP_MAX_PORTS) != STP_MAX_PORTS && port_num_counter) { VLOG_ERR("bridge %s: must manually configure all STP port " "IDs or none, disabling", br->name); ofproto_set_stp(br->ofproto, NULL); } bitmap_free(port_num_bitmap); } } static void bridge_configure_rstp(struct bridge *br, bool enable_rstp) { if (!enable_rstp) { ofproto_set_rstp(br->ofproto, NULL); } else { struct ofproto_rstp_settings br_s; const char *config_str; struct port *port; int port_num_counter; config_str = smap_get(&br->cfg->other_config, "rstp-address"); if (config_str) { struct eth_addr ea; if (eth_addr_from_string(config_str, &ea)) { br_s.address = eth_addr_to_uint64(ea); } else { br_s.address = eth_addr_to_uint64(br->ea); VLOG_ERR("bridge %s: invalid rstp-address, defaulting " "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea)); } } else { br_s.address = eth_addr_to_uint64(br->ea); } const struct smap *oc = &br->cfg->other_config; br_s.priority = smap_get_ullong(oc, "rstp-priority", RSTP_DEFAULT_PRIORITY); br_s.ageing_time = smap_get_ullong(oc, "rstp-ageing-time", RSTP_DEFAULT_AGEING_TIME); br_s.force_protocol_version = smap_get_ullong( oc, "rstp-force-protocol-version", FPV_DEFAULT); br_s.bridge_max_age = smap_get_ullong(oc, "rstp-max-age", RSTP_DEFAULT_BRIDGE_MAX_AGE); br_s.bridge_forward_delay = smap_get_ullong( oc, "rstp-forward-delay", RSTP_DEFAULT_BRIDGE_FORWARD_DELAY); br_s.transmit_hold_count = smap_get_ullong( oc, "rstp-transmit-hold-count", RSTP_DEFAULT_TRANSMIT_HOLD_COUNT); /* Configure RSTP on the bridge. */ if (ofproto_set_rstp(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable RSTP", br->name); return; } port_num_counter = 0; HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_port_rstp_settings port_s; struct iface *iface; port_configure_rstp(br->ofproto, port, &port_s, &port_num_counter); /* As bonds are not supported, just apply configuration to * all interfaces. */ LIST_FOR_EACH (iface, port_elem, &port->ifaces) { if (ofproto_port_set_rstp(br->ofproto, iface->ofp_port, &port_s)) { VLOG_ERR("port %s: could not enable RSTP", port->name); continue; } } } } } static void bridge_configure_spanning_tree(struct bridge *br) { bool enable_rstp = br->cfg->rstp_enable; bool enable_stp = br->cfg->stp_enable; if (enable_rstp && enable_stp) { VLOG_WARN("%s: RSTP and STP are mutually exclusive but both are " "configured; enabling RSTP", br->name); enable_stp = false; } bridge_configure_stp(br, enable_stp); bridge_configure_rstp(br, enable_rstp); } static bool bridge_has_bond_fake_iface(const struct bridge *br, const char *name) { const struct port *port = port_lookup(br, name); return port && port_is_bond_fake_iface(port); } static bool port_is_bond_fake_iface(const struct port *port) { return port->cfg->bond_fake_iface && !ovs_list_is_short(&port->ifaces); } static void add_del_bridges(const struct ovsrec_open_vswitch *cfg) { struct bridge *br; struct shash_node *node; struct shash new_br; size_t i; /* Collect new bridges' names and types. */ shash_init(&new_br); for (i = 0; i < cfg->n_bridges; i++) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct ovsrec_bridge *br_cfg = cfg->bridges[i]; if (strchr(br_cfg->name, '/') || strchr(br_cfg->name, '\\')) { /* Prevent remote ovsdb-server users from accessing arbitrary * directories, e.g. consider a bridge named "../../../etc/". * * Prohibiting "\" is only necessary on Windows but it's no great * loss elsewhere. */ VLOG_WARN_RL(&rl, "ignoring bridge with invalid name \"%s\"", br_cfg->name); } else if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) { VLOG_WARN_RL(&rl, "bridge %s specified twice", br_cfg->name); } } /* Get rid of deleted bridges or those whose types have changed. * Update 'cfg' of bridges that still exist. */ HMAP_FOR_EACH_SAFE (br, node, &all_bridges) { br->cfg = shash_find_data(&new_br, br->name); if (!br->cfg || strcmp(br->type, ofproto_normalize_type( br->cfg->datapath_type))) { bridge_destroy(br, true); } } /* Add new bridges. */ SHASH_FOR_EACH(node, &new_br) { const struct ovsrec_bridge *br_cfg = node->data; if (!bridge_lookup(br_cfg->name)) { bridge_create(br_cfg); } } shash_destroy(&new_br); } /* Configures 'netdev' based on the "options" column in 'iface_cfg'. * Returns 0 if successful, otherwise a positive errno value. */ static int iface_set_netdev_config(const struct ovsrec_interface *iface_cfg, struct netdev *netdev, char **errp) { return netdev_set_config(netdev, &iface_cfg->options, errp); } /* Opens a network device for 'if_cfg' and configures it. Adds the network * device to br->ofproto and stores the OpenFlow port number in '*ofp_portp'. * * If successful, returns 0 and stores the network device in '*netdevp'. On * failure, returns a positive errno value and stores NULL in '*netdevp'. */ static int iface_do_create(const struct bridge *br, const struct ovsrec_interface *iface_cfg, ofp_port_t *ofp_portp, struct netdev **netdevp, char **errp) { struct netdev *netdev = NULL; int error; const char *type; if (netdev_is_reserved_name(iface_cfg->name)) { VLOG_WARN("could not create interface %s, name is reserved", iface_cfg->name); error = EINVAL; goto error; } type = ofproto_port_open_type(br->ofproto, iface_get_type(iface_cfg, br->cfg)); error = netdev_open(iface_cfg->name, type, &netdev); if (error) { VLOG_WARN_BUF(errp, "could not open network device %s (%s)", iface_cfg->name, ovs_strerror(error)); goto error; } error = iface_set_netdev_config(iface_cfg, netdev, errp); if (error) { goto error; } iface_set_netdev_mtu(iface_cfg, netdev); *ofp_portp = iface_pick_ofport(iface_cfg); error = ofproto_port_add(br->ofproto, netdev, ofp_portp); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); *errp = xasprintf("could not add network device %s to ofproto (%s)", iface_cfg->name, ovs_strerror(error)); if (!VLOG_DROP_WARN(&rl)) { VLOG_WARN("%s", *errp); } goto error; } VLOG_INFO("bridge %s: added interface %s on port %d", br->name, iface_cfg->name, *ofp_portp); *netdevp = netdev; return 0; error: *netdevp = NULL; netdev_close(netdev); return error; } /* Creates a new iface on 'br' based on 'if_cfg'. The new iface has OpenFlow * port number 'ofp_port'. If ofp_port is OFPP_NONE, an OpenFlow port is * automatically allocated for the iface. Takes ownership of and * deallocates 'if_cfg'. * * Return true if an iface is successfully created, false otherwise. */ static bool iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg, const struct ovsrec_port *port_cfg) { struct netdev *netdev; struct iface *iface; ofp_port_t ofp_port; struct port *port; char *errp = NULL; int error; /* Do the bits that can fail up front. */ ovs_assert(!iface_lookup(br, iface_cfg->name)); error = iface_do_create(br, iface_cfg, &ofp_port, &netdev, &errp); if (error) { iface_clear_db_record(iface_cfg, errp); free(errp); return false; } /* Get or create the port structure. */ port = port_lookup(br, port_cfg->name); if (!port) { port = port_create(br, port_cfg); } /* Create the iface structure. */ iface = xzalloc(sizeof *iface); ovs_list_push_back(&port->ifaces, &iface->port_elem); hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(iface_cfg->name, 0)); iface->port = port; iface->name = xstrdup(iface_cfg->name); iface->ofp_port = ofp_port; iface->netdev = netdev; iface->type = iface_get_type(iface_cfg, br->cfg); iface->cfg = iface_cfg; hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_ofp_port(ofp_port)); /* Populate initial status in database. */ iface_refresh_stats(iface); iface_refresh_netdev_status(iface); /* Add bond fake iface if necessary. */ if (port_is_bond_fake_iface(port)) { struct ofproto_port ofproto_port; if (ofproto_port_query_by_name(br->ofproto, port->name, &ofproto_port)) { error = netdev_open(port->name, "internal", &netdev); if (!error) { ofp_port_t fake_ofp_port = OFPP_NONE; ofproto_port_add(br->ofproto, netdev, &fake_ofp_port); netdev_close(netdev); } else { VLOG_WARN("could not open network device %s (%s)", port->name, ovs_strerror(error)); } } else { /* Already exists, nothing to do. */ ofproto_port_destroy(&ofproto_port); } } return true; } /* Set forward BPDU option. */ static void bridge_configure_forward_bpdu(struct bridge *br) { ofproto_set_forward_bpdu(br->ofproto, smap_get_bool(&br->cfg->other_config, "forward-bpdu", false)); } /* Set MAC learning table configuration for 'br'. */ static void bridge_configure_mac_table(struct bridge *br) { const struct smap *oc = &br->cfg->other_config; int idle_time = smap_get_int(oc, "mac-aging-time", 0); if (!idle_time) { idle_time = MAC_ENTRY_DEFAULT_IDLE_TIME; } int mac_table_size = smap_get_int(oc, "mac-table-size", 0); if (!mac_table_size) { mac_table_size = MAC_DEFAULT_MAX; } ofproto_set_mac_table_config(br->ofproto, idle_time, mac_table_size); } /* Set multicast snooping table configuration for 'br'. */ static void bridge_configure_mcast_snooping(struct bridge *br) { if (!br->cfg->mcast_snooping_enable) { ofproto_set_mcast_snooping(br->ofproto, NULL); } else { struct port *port; struct ofproto_mcast_snooping_settings br_s; const struct smap *oc = &br->cfg->other_config; int idle_time = smap_get_int(oc, "mcast-snooping-aging-time", 0); br_s.idle_time = idle_time ? idle_time : MCAST_ENTRY_DEFAULT_IDLE_TIME; int max_entries = smap_get_int(oc, "mcast-snooping-table-size", 0); br_s.max_entries = (max_entries ? max_entries : MCAST_DEFAULT_MAX_ENTRIES); br_s.flood_unreg = !smap_get_bool( oc, "mcast-snooping-disable-flood-unregistered", false); /* Configure multicast snooping on the bridge */ if (ofproto_set_mcast_snooping(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable multicast snooping", br->name); return; } HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_mcast_snooping_port_settings port_s; port_s.flood = smap_get_bool(&port->cfg->other_config, "mcast-snooping-flood", false); port_s.flood_reports = smap_get_bool(&port->cfg->other_config, "mcast-snooping-flood-reports", false); if (ofproto_port_set_mcast_snooping(br->ofproto, port, &port_s)) { VLOG_ERR("port %s: could not configure mcast snooping", port->name); } } } } static void find_local_hw_addr(const struct bridge *br, struct eth_addr *ea, const struct port *fake_br, struct iface **hw_addr_iface) { struct hmapx mirror_output_ports; struct port *port; bool found_addr = false; int error; int i; /* Mirror output ports don't participate in picking the local hardware * address. ofproto can't help us find out whether a given port is a * mirror output because we haven't configured mirrors yet, so we need to * accumulate them ourselves. */ hmapx_init(&mirror_output_ports); for (i = 0; i < br->cfg->n_mirrors; i++) { struct ovsrec_mirror *m = br->cfg->mirrors[i]; if (m->output_port) { hmapx_add(&mirror_output_ports, m->output_port); } } /* Otherwise choose the minimum non-local MAC address among all of the * interfaces. */ HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct eth_addr iface_ea; struct iface *candidate; struct iface *iface; /* Mirror output ports don't participate. */ if (hmapx_contains(&mirror_output_ports, port->cfg)) { continue; } /* Choose the MAC address to represent the port. */ iface = NULL; if (port->cfg->mac && eth_addr_from_string(port->cfg->mac, &iface_ea)) { /* Find the interface with this Ethernet address (if any) so that * we can provide the correct devname to the caller. */ LIST_FOR_EACH (candidate, port_elem, &port->ifaces) { struct eth_addr candidate_ea; if (!netdev_get_etheraddr(candidate->netdev, &candidate_ea) && eth_addr_equals(iface_ea, candidate_ea)) { iface = candidate; } } } else { /* Choose the interface whose MAC address will represent the port. * The Linux kernel bonding code always chooses the MAC address of * the first member added to a bond, and the Fedora networking * scripts always add members to a bond in alphabetical order, so * for compatibility we choose the interface with the name that is * first in alphabetical order. */ LIST_FOR_EACH (candidate, port_elem, &port->ifaces) { if (!iface || strcmp(candidate->name, iface->name) < 0) { iface = candidate; } } /* A port always has at least one interface. */ ovs_assert(iface != NULL); /* The local port doesn't count (since we're trying to choose its * MAC address anyway). */ if (iface->ofp_port == OFPP_LOCAL) { continue; } /* For fake bridges we only choose from ports with the same tag */ if (fake_br && fake_br->cfg && fake_br->cfg->tag) { if (!port->cfg->tag) { continue; } if (*port->cfg->tag != *fake_br->cfg->tag) { continue; } } /* Grab MAC. */ error = netdev_get_etheraddr(iface->netdev, &iface_ea); if (error) { continue; } } /* Compare against our current choice. */ if (!eth_addr_is_multicast(iface_ea) && !eth_addr_is_local(iface_ea) && !eth_addr_is_reserved(iface_ea) && !eth_addr_is_zero(iface_ea) && (!found_addr || eth_addr_compare_3way(iface_ea, *ea) < 0)) { *ea = iface_ea; *hw_addr_iface = iface; found_addr = true; } } if (!found_addr) { *ea = br->default_ea; *hw_addr_iface = NULL; } hmapx_destroy(&mirror_output_ports); } static void bridge_pick_local_hw_addr(struct bridge *br, struct eth_addr *ea, struct iface **hw_addr_iface) { *hw_addr_iface = NULL; /* Did the user request a particular MAC? */ const char *hwaddr = smap_get_def(&br->cfg->other_config, "hwaddr", ""); if (eth_addr_from_string(hwaddr, ea)) { if (eth_addr_is_multicast(*ea)) { VLOG_ERR("bridge %s: cannot set MAC address to multicast " "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(*ea)); } else if (eth_addr_is_zero(*ea)) { VLOG_ERR("bridge %s: cannot set MAC address to zero", br->name); } else { return; } } /* Find a local hw address */ find_local_hw_addr(br, ea, NULL, hw_addr_iface); } /* Choose and returns the datapath ID for bridge 'br' given that the bridge * Ethernet address is 'bridge_ea'. */ static uint64_t bridge_pick_datapath_id(struct bridge *br, const struct eth_addr bridge_ea) { /* * The procedure for choosing a bridge MAC address will, in the most * ordinary case, also choose a unique MAC that we can use as a datapath * ID. In some special cases, though, multiple bridges will end up with * the same MAC address. This is OK for the bridges, but it will confuse * the OpenFlow controller, because each datapath needs a unique datapath * ID. * * Datapath IDs must be unique. It is also very desirable that they be * stable from one run to the next, so that policy set on a datapath * "sticks". */ const char *datapath_id; uint64_t dpid; datapath_id = smap_get_def(&br->cfg->other_config, "datapath-id", ""); if (dpid_from_string(datapath_id, &dpid)) { return dpid; } return eth_addr_to_uint64(bridge_ea); } static void iface_refresh_netdev_status(struct iface *iface) { struct smap smap; enum netdev_flags flags; const char *link_state; struct eth_addr mac; int64_t bps, mtu_64, ifindex64, link_resets; bool full_duplex; int mtu, error; uint32_t mbps; if (iface_is_synthetic(iface)) { return; } if (iface->change_seq == netdev_get_change_seq(iface->netdev) && !status_txn_try_again) { return; } iface->change_seq = netdev_get_change_seq(iface->netdev); smap_init(&smap); if (!netdev_get_status(iface->netdev, &smap)) { ovsrec_interface_set_status(iface->cfg, &smap); } else { ovsrec_interface_set_status(iface->cfg, NULL); } smap_destroy(&smap); error = netdev_get_flags(iface->netdev, &flags); if (!error) { const char *state = flags & NETDEV_UP ? "up" : "down"; ovsrec_interface_set_admin_state(iface->cfg, state); } else { ovsrec_interface_set_admin_state(iface->cfg, NULL); } link_state = netdev_get_carrier(iface->netdev) ? "up" : "down"; ovsrec_interface_set_link_state(iface->cfg, link_state); link_resets = netdev_get_carrier_resets(iface->netdev); ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1); error = netdev_get_duplex(iface->netdev, &full_duplex); if (!error) { ovsrec_interface_set_duplex(iface->cfg, full_duplex ? "full" : "half"); } else { ovsrec_interface_set_duplex(iface->cfg, NULL); } netdev_get_speed(iface->netdev, &mbps, NULL); if (mbps) { bps = mbps * 1000000ULL; ovsrec_interface_set_link_speed(iface->cfg, &bps, 1); } else { ovsrec_interface_set_link_speed(iface->cfg, NULL, 0); } error = netdev_get_mtu(iface->netdev, &mtu); if (!error) { mtu_64 = mtu; ovsrec_interface_set_mtu(iface->cfg, &mtu_64, 1); } else { ovsrec_interface_set_mtu(iface->cfg, NULL, 0); } error = netdev_get_etheraddr(iface->netdev, &mac); if (!error) { char mac_string[ETH_ADDR_STRLEN + 1]; snprintf(mac_string, sizeof mac_string, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); ovsrec_interface_set_mac_in_use(iface->cfg, mac_string); } else { ovsrec_interface_set_mac_in_use(iface->cfg, NULL); } /* The netdev may return a negative number (such as -EOPNOTSUPP) * if there is no valid ifindex number. */ ifindex64 = netdev_get_ifindex(iface->netdev); if (ifindex64 < 0) { ifindex64 = 0; } ovsrec_interface_set_ifindex(iface->cfg, &ifindex64, 1); } static void iface_refresh_ofproto_status(struct iface *iface) { int current; int error; char *errp = NULL; if (iface_is_synthetic(iface)) { return; } error = ofproto_vport_get_status(iface->port->bridge->ofproto, iface->ofp_port, &errp); if (error && error != EOPNOTSUPP) { /* Need to verify to avoid race with transaction from * 'bridge_reconfigure' that clears errors explicitly. */ ovsrec_interface_verify_error(iface->cfg); ovsrec_interface_set_error(iface->cfg, errp ? errp : ovs_strerror(error)); free(errp); } current = ofproto_port_is_lacp_current(iface->port->bridge->ofproto, iface->ofp_port); if (current >= 0) { bool bl = current; ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1); } else { ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0); } if (ofproto_port_cfm_status_changed(iface->port->bridge->ofproto, iface->ofp_port) || status_txn_try_again) { iface_refresh_cfm_stats(iface); } if (ofproto_port_bfd_status_changed(iface->port->bridge->ofproto, iface->ofp_port) || status_txn_try_again) { struct smap smap; smap_init(&smap); ofproto_port_get_bfd_status(iface->port->bridge->ofproto, iface->ofp_port, &smap); ovsrec_interface_set_bfd_status(iface->cfg, &smap); smap_destroy(&smap); } } /* Writes 'iface''s CFM statistics to the database. 'iface' must not be * synthetic. */ static void iface_refresh_cfm_stats(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; struct cfm_status status; int error; error = ofproto_port_get_cfm_status(iface->port->bridge->ofproto, iface->ofp_port, &status); if (error > 0) { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); ovsrec_interface_set_cfm_flap_count(cfg, NULL, 0); ovsrec_interface_set_cfm_health(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0); } else { const char *reasons[CFM_FAULT_N_REASONS]; int64_t cfm_health = status.health; int64_t cfm_flap_count = status.flap_count; bool faulted = status.faults != 0; size_t i, j; ovsrec_interface_set_cfm_fault(cfg, &faulted, 1); j = 0; for (i = 0; i < CFM_FAULT_N_REASONS; i++) { int reason = 1 << i; if (status.faults & reason) { reasons[j++] = cfm_fault_reason_to_str(reason); } } ovsrec_interface_set_cfm_fault_status(cfg, reasons, j); ovsrec_interface_set_cfm_flap_count(cfg, &cfm_flap_count, 1); if (status.remote_opstate >= 0) { const char *remote_opstate = status.remote_opstate ? "up" : "down"; ovsrec_interface_set_cfm_remote_opstate(cfg, remote_opstate); } else { ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); } ovsrec_interface_set_cfm_remote_mpids(cfg, (const int64_t *)status.rmps, status.n_rmps); if (cfm_health >= 0) { ovsrec_interface_set_cfm_health(cfg, &cfm_health, 1); } else { ovsrec_interface_set_cfm_health(cfg, NULL, 0); } free(status.rmps); } } static void iface_refresh_stats(struct iface *iface) { struct netdev_custom_stats custom_stats; struct netdev_stats stats; int n; uint32_t i, counters_size; #define IFACE_STATS \ IFACE_STAT(rx_packets, "rx_packets") \ IFACE_STAT(tx_packets, "tx_packets") \ IFACE_STAT(rx_bytes, "rx_bytes") \ IFACE_STAT(tx_bytes, "tx_bytes") \ IFACE_STAT(rx_dropped, "rx_dropped") \ IFACE_STAT(tx_dropped, "tx_dropped") \ IFACE_STAT(rx_errors, "rx_errors") \ IFACE_STAT(tx_errors, "tx_errors") \ IFACE_STAT(rx_frame_errors, "rx_frame_err") \ IFACE_STAT(rx_over_errors, "rx_over_err") \ IFACE_STAT(rx_crc_errors, "rx_crc_err") \ IFACE_STAT(rx_missed_errors, "rx_missed_errors") \ IFACE_STAT(collisions, "collisions") \ IFACE_STAT(rx_1_to_64_packets, "rx_1_to_64_packets") \ IFACE_STAT(rx_65_to_127_packets, "rx_65_to_127_packets") \ IFACE_STAT(rx_128_to_255_packets, "rx_128_to_255_packets") \ IFACE_STAT(rx_256_to_511_packets, "rx_256_to_511_packets") \ IFACE_STAT(rx_512_to_1023_packets, "rx_512_to_1023_packets") \ IFACE_STAT(rx_1024_to_1522_packets, "rx_1024_to_1522_packets") \ IFACE_STAT(rx_1523_to_max_packets, "rx_1523_to_max_packets") \ IFACE_STAT(tx_1_to_64_packets, "tx_1_to_64_packets") \ IFACE_STAT(tx_65_to_127_packets, "tx_65_to_127_packets") \ IFACE_STAT(tx_128_to_255_packets, "tx_128_to_255_packets") \ IFACE_STAT(tx_256_to_511_packets, "tx_256_to_511_packets") \ IFACE_STAT(tx_512_to_1023_packets, "tx_512_to_1023_packets") \ IFACE_STAT(tx_1024_to_1522_packets, "tx_1024_to_1522_packets") \ IFACE_STAT(tx_1523_to_max_packets, "tx_1523_to_max_packets") \ IFACE_STAT(multicast, "rx_multicast_packets") \ IFACE_STAT(tx_multicast_packets, "tx_multicast_packets") \ IFACE_STAT(rx_broadcast_packets, "rx_broadcast_packets") \ IFACE_STAT(tx_broadcast_packets, "tx_broadcast_packets") \ IFACE_STAT(rx_undersized_errors, "rx_undersized_errors") \ IFACE_STAT(rx_oversize_errors, "rx_oversize_errors") \ IFACE_STAT(rx_fragmented_errors, "rx_fragmented_errors") \ IFACE_STAT(rx_jabber_errors, "rx_jabber_errors") \ IFACE_STAT(upcall_packets, "upcall_packets") \ IFACE_STAT(upcall_errors, "upcall_errors") #define IFACE_STAT(MEMBER, NAME) + 1 enum { N_IFACE_STATS = IFACE_STATS }; #undef IFACE_STAT if (iface_is_synthetic(iface)) { return; } netdev_get_custom_stats(iface->netdev, &custom_stats); counters_size = custom_stats.size + N_IFACE_STATS; int64_t *values = xmalloc(counters_size * sizeof(int64_t)); const char **keys = xmalloc(counters_size * sizeof(char *)); /* Intentionally ignore return value, since errors will set 'stats' to * all-1s, and we will deal with that correctly below. */ netdev_get_stats(iface->netdev, &stats); /* Copy statistics into keys[] and values[]. */ n = 0; #define IFACE_STAT(MEMBER, NAME) \ if (stats.MEMBER != UINT64_MAX) { \ keys[n] = NAME; \ values[n] = stats.MEMBER; \ n++; \ } IFACE_STATS; #undef IFACE_STAT /* Copy custom statistics into keys[] and values[]. */ if (custom_stats.size && custom_stats.counters) { for (i = 0 ; i < custom_stats.size ; i++) { values[n] = custom_stats.counters[i].value; keys[n] = custom_stats.counters[i].name; n++; } } ovs_assert(n <= counters_size); ovsrec_interface_set_statistics(iface->cfg, keys, values, n); #undef IFACE_STATS free(values); free(keys); netdev_free_custom_stats_counters(&custom_stats); } static void br_refresh_datapath_info(struct bridge *br) { const char *version; version = (br->ofproto && br->ofproto->ofproto_class->get_datapath_version ? br->ofproto->ofproto_class->get_datapath_version(br->ofproto) : NULL); ovsrec_bridge_set_datapath_version(br->cfg, version ? version : ""); } static void br_refresh_stp_status(struct bridge *br) { struct smap smap = SMAP_INITIALIZER(&smap); struct ofproto *ofproto = br->ofproto; struct ofproto_stp_status status; if (ofproto_get_stp_status(ofproto, &status)) { return; } if (!status.enabled) { ovsrec_bridge_set_status(br->cfg, NULL); return; } smap_add_format(&smap, "stp_bridge_id", STP_ID_FMT, STP_ID_ARGS(status.bridge_id)); smap_add_format(&smap, "stp_designated_root", STP_ID_FMT, STP_ID_ARGS(status.designated_root)); smap_add_format(&smap, "stp_root_path_cost", "%d", status.root_path_cost); ovsrec_bridge_set_status(br->cfg, &smap); smap_destroy(&smap); } static void port_refresh_stp_status(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_stp_status status; struct smap smap; if (port_is_synthetic(port)) { return; } /* STP doesn't currently support bonds. */ if (!ovs_list_is_singleton(&port->ifaces)) { ovsrec_port_set_status(port->cfg, NULL); return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_stp_status(ofproto, iface->ofp_port, &status)) { return; } if (!status.enabled) { ovsrec_port_set_status(port->cfg, NULL); return; } /* Set Status column. */ smap_init(&smap); smap_add_format(&smap, "stp_port_id", "%d", status.port_id); smap_add(&smap, "stp_state", stp_state_name(status.state)); smap_add_format(&smap, "stp_sec_in_state", "%u", status.sec_in_state); smap_add(&smap, "stp_role", stp_role_name(status.role)); ovsrec_port_set_status(port->cfg, &smap); smap_destroy(&smap); } static void port_refresh_stp_stats(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_stp_stats stats; const char *keys[3]; int64_t int_values[3]; if (port_is_synthetic(port)) { return; } /* STP doesn't currently support bonds. */ if (!ovs_list_is_singleton(&port->ifaces)) { return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_stp_stats(ofproto, iface->ofp_port, &stats)) { return; } if (!stats.enabled) { ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0); return; } /* Set Statistics column. */ keys[0] = "stp_tx_count"; int_values[0] = stats.tx_count; keys[1] = "stp_rx_count"; int_values[1] = stats.rx_count; keys[2] = "stp_error_count"; int_values[2] = stats.error_count; ovsrec_port_set_statistics(port->cfg, keys, int_values, ARRAY_SIZE(int_values)); } static void br_refresh_rstp_status(struct bridge *br) { struct smap smap = SMAP_INITIALIZER(&smap); struct ofproto *ofproto = br->ofproto; struct ofproto_rstp_status status; if (ofproto_get_rstp_status(ofproto, &status)) { return; } if (!status.enabled) { ovsrec_bridge_set_rstp_status(br->cfg, NULL); return; } smap_add_format(&smap, "rstp_bridge_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.bridge_id)); smap_add_format(&smap, "rstp_root_path_cost", "%"PRIu32, status.root_path_cost); smap_add_format(&smap, "rstp_root_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.root_id)); smap_add_format(&smap, "rstp_designated_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.designated_id)); smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT, status.designated_port_id); smap_add_format(&smap, "rstp_bridge_port_id", RSTP_PORT_ID_FMT, status.bridge_port_id); ovsrec_bridge_set_rstp_status(br->cfg, &smap); smap_destroy(&smap); } static void port_refresh_rstp_status(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_rstp_status status; struct smap smap; if (port_is_synthetic(port)) { return; } /* RSTP doesn't currently support bonds. */ if (!ovs_list_is_singleton(&port->ifaces)) { ovsrec_port_set_rstp_status(port->cfg, NULL); return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_rstp_status(ofproto, iface->ofp_port, &status)) { return; } if (!status.enabled) { ovsrec_port_set_rstp_status(port->cfg, NULL); return; } /* Set Status column. */ smap_init(&smap); smap_add_format(&smap, "rstp_port_id", RSTP_PORT_ID_FMT, status.port_id); smap_add_format(&smap, "rstp_port_role", "%s", rstp_port_role_name(status.role)); smap_add_format(&smap, "rstp_port_state", "%s", rstp_state_name(status.state)); smap_add_format(&smap, "rstp_designated_bridge_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.designated_bridge_id)); smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT, status.designated_port_id); smap_add_format(&smap, "rstp_designated_path_cost", "%"PRIu32, status.designated_path_cost); ovsrec_port_set_rstp_status(port->cfg, &smap); smap_destroy(&smap); } static void port_refresh_rstp_stats(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_rstp_status status; const char *keys[4]; int64_t int_values[4]; if (port_is_synthetic(port)) { return; } /* RSTP doesn't currently support bonds. */ if (!ovs_list_is_singleton(&port->ifaces)) { ovsrec_port_set_rstp_statistics(port->cfg, NULL, NULL, 0); return; } iface = CONTAINER_OF(ovs_list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_rstp_status(ofproto, iface->ofp_port, &status)) { return; } if (!status.enabled) { ovsrec_port_set_rstp_statistics(port->cfg, NULL, NULL, 0); return; } /* Set Statistics column. */ keys[0] = "rstp_tx_count"; int_values[0] = status.tx_count; keys[1] = "rstp_rx_count"; int_values[1] = status.rx_count; keys[2] = "rstp_uptime"; int_values[2] = status.uptime; keys[3] = "rstp_error_count"; int_values[3] = status.error_count; ovsrec_port_set_rstp_statistics(port->cfg, keys, int_values, ARRAY_SIZE(int_values)); } static void port_refresh_bond_status(struct port *port, bool force_update) { struct eth_addr mac; /* Return if port is not a bond */ if (ovs_list_is_singleton(&port->ifaces)) { return; } if (bond_get_changed_active_member(port->name, &mac, force_update)) { struct ds mac_s; ds_init(&mac_s); ds_put_format(&mac_s, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); ovsrec_port_set_bond_active_slave(port->cfg, ds_cstr(&mac_s)); ds_destroy(&mac_s); } } static bool enable_system_stats(const struct ovsrec_open_vswitch *cfg) { return smap_get_bool(&cfg->other_config, "enable-statistics", false); } static void reconfigure_system_stats(const struct ovsrec_open_vswitch *cfg) { bool enable = enable_system_stats(cfg); system_stats_enable(enable); if (!enable) { ovsrec_open_vswitch_set_statistics(cfg, NULL); } } static void run_system_stats(void) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl); struct smap *stats; stats = system_stats_run(); if (stats && cfg) { struct ovsdb_idl_txn *txn; struct ovsdb_datum datum; txn = ovsdb_idl_txn_create(idl); ovsdb_datum_from_smap(&datum, stats); smap_destroy(stats); ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics, &datum); ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); free(stats); } } static const char * ofp12_controller_role_to_str(enum ofp12_controller_role role) { switch (role) { case OFPCR12_ROLE_EQUAL: return "other"; case OFPCR12_ROLE_PRIMARY: return "master"; case OFPCR12_ROLE_SECONDARY: return "slave"; case OFPCR12_ROLE_NOCHANGE: default: return NULL; } } static void refresh_controller_status(void) { struct bridge *br; /* Accumulate status for controllers on all bridges. */ HMAP_FOR_EACH (br, node, &all_bridges) { struct shash info = SHASH_INITIALIZER(&info); ofproto_get_ofproto_controller_info(br->ofproto, &info); /* Update each controller of the bridge in the database with * current status. */ struct ovsrec_controller **controllers; size_t n_controllers = bridge_get_controllers(br, &controllers); size_t i; for (i = 0; i < n_controllers; i++) { struct ovsrec_controller *cfg = controllers[i]; struct ofproto_controller_info *cinfo = shash_find_data(&info, cfg->target); /* cinfo is NULL when 'cfg->target' is a passive connection. */ if (cinfo) { ovsrec_controller_set_is_connected(cfg, cinfo->is_connected); const char *role = ofp12_controller_role_to_str(cinfo->role); ovsrec_controller_set_role(cfg, role); ovsrec_controller_set_status(cfg, &cinfo->pairs); } else { ovsrec_controller_set_is_connected(cfg, false); ovsrec_controller_set_role(cfg, NULL); ovsrec_controller_set_status(cfg, NULL); } } ofproto_free_ofproto_controller_info(&info); } } /* Update interface and mirror statistics if necessary. */ static void run_stats_update(void) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl); int stats_interval; if (!cfg) { return; } /* Statistics update interval should always be greater than or equal to * 5000 ms. */ stats_interval = MAX(smap_get_int(&cfg->other_config, "stats-update-interval", 5000), 5000); if (stats_timer_interval != stats_interval) { stats_timer_interval = stats_interval; stats_timer = LLONG_MIN; } if (time_msec() >= stats_timer) { enum ovsdb_idl_txn_status status; /* Rate limit the update. Do not start a new update if the * previous one is not done. */ if (!stats_txn) { struct bridge *br; stats_txn = ovsdb_idl_txn_create(idl); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; struct mirror *m; HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_refresh_stats(iface); } port_refresh_stp_stats(port); port_refresh_rstp_stats(port); } HMAP_FOR_EACH (m, hmap_node, &br->mirrors) { mirror_refresh_stats(m); } } refresh_controller_status(); } status = ovsdb_idl_txn_commit(stats_txn); if (status != TXN_INCOMPLETE) { stats_timer = time_msec() + stats_timer_interval; ovsdb_idl_txn_destroy(stats_txn); stats_txn = NULL; } } } static void stats_update_wait(void) { /* If the 'stats_txn' is non-null (transaction incomplete), waits for the * transaction to complete. Otherwise, waits for the 'stats_timer'. */ if (stats_txn) { ovsdb_idl_txn_wait(stats_txn); } else { poll_timer_wait_until(stats_timer); } } /* Update bridge/port/interface status if necessary. */ static void run_status_update(void) { if (!status_txn) { uint64_t seq; /* Rate limit the update. Do not start a new update if the * previous one is not done. */ seq = seq_read(connectivity_seq_get()); if (seq != connectivity_seqno || status_txn_try_again) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl); struct bridge *br; connectivity_seqno = seq; status_txn = ovsdb_idl_txn_create(idl); dpdk_status(cfg); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; br_refresh_stp_status(br); br_refresh_rstp_status(br); br_refresh_datapath_info(br); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; port_refresh_stp_status(port); port_refresh_rstp_status(port); port_refresh_bond_status(port, status_txn_try_again); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_refresh_netdev_status(iface); iface_refresh_ofproto_status(iface); } } } } } /* Commit the transaction and get the status. If the transaction finishes, * then destroy the transaction. Otherwise, keep it so that we can check * progress the next time that this function is called. */ if (status_txn) { enum ovsdb_idl_txn_status status; status = ovsdb_idl_txn_commit(status_txn); if (status != TXN_INCOMPLETE) { ovsdb_idl_txn_destroy(status_txn); status_txn = NULL; /* Sets the 'status_txn_try_again' if the transaction fails. */ if (status == TXN_SUCCESS || status == TXN_UNCHANGED) { status_txn_try_again = false; } else { status_txn_try_again = true; } } } /* Refresh AA port status if necessary. */ if (time_msec() >= aa_refresh_timer) { struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { if (bridge_aa_need_refresh(br)) { struct ovsdb_idl_txn *txn; txn = ovsdb_idl_txn_create(idl); bridge_aa_refresh_queued(br); ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); } } aa_refresh_timer = time_msec() + AA_REFRESH_INTERVAL; } } static void status_update_wait(void) { /* If the 'status_txn' is non-null (transaction incomplete), waits for the * transaction to complete. If the status update to database needs to be * run again (transaction fails), registers a timeout in * 'STATUS_CHECK_AGAIN_MSEC'. Otherwise, waits on the global connectivity * sequence number. */ if (status_txn) { ovsdb_idl_txn_wait(status_txn); } else if (status_txn_try_again) { poll_timer_wait_until(time_msec() + STATUS_CHECK_AGAIN_MSEC); } else { seq_wait(connectivity_seq_get(), connectivity_seqno); } } static void bridge_run__(void) { struct bridge *br; struct sset types; const char *type; /* Let each datapath type do the work that it needs to do. */ sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_run(type); } sset_destroy(&types); /* Let each bridge do the work that it needs to do. */ HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_run(br->ofproto); } } void bridge_run(void) { static struct ovsrec_open_vswitch null_cfg; const struct ovsrec_open_vswitch *cfg; ovsrec_open_vswitch_init(&null_cfg); ovsdb_idl_run(idl); if_notifier_run(); if (ovsdb_idl_is_lock_contended(idl)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); struct bridge *br; VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, " "disabling this process (pid %ld) until it goes away", (long int) getpid()); HMAP_FOR_EACH_SAFE (br, node, &all_bridges) { bridge_destroy(br, false); } /* Since we will not be running system_stats_run() in this process * with the current situation of multiple ovs-vswitchd daemons, * disable system stats collection. */ system_stats_enable(false); return; } else if (!ovsdb_idl_has_lock(idl) || !ovsdb_idl_has_ever_connected(idl)) { /* Returns if not holding the lock or not done retrieving db * contents. */ return; } cfg = ovsrec_open_vswitch_first(idl); if (cfg && ovsdb_idl_get_seqno(idl) != idl_seqno) { dpif_offload_set_global_cfg(cfg); } if (cfg) { dpdk_init(&cfg->other_config); userspace_tso_init(&cfg->other_config); } /* Initialize the ofproto library. This only needs to run once, but * it must be done after the configuration is set. If the * initialization has already occurred, bridge_init_ofproto() * returns immediately. */ bridge_init_ofproto(cfg); /* Once the value of flow-restore-wait is false, we no longer should * check its value from the database. */ if (cfg && ofproto_get_flow_restore_wait()) { ofproto_set_flow_restore_wait(smap_get_bool(&cfg->other_config, "flow-restore-wait", false)); } bridge_run__(); /* Re-configure SSL/TLS. We do this on every trip through the main loop, * instead of just when the database changes, because the contents of the * key and certificate files can change without the database changing. * * We do this before bridge_reconfigure() because that function might * initiate SSL/TLS connections and thus requires SSL/TLS to be configured. */ if (cfg && cfg->ssl) { const struct ovsrec_ssl *ssl = cfg->ssl; stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate); stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert); } if (ovsdb_idl_get_seqno(idl) != idl_seqno || if_notifier_changed(ifnotifier)) { struct ovsdb_idl_txn *txn; idl_seqno = ovsdb_idl_get_seqno(idl); txn = ovsdb_idl_txn_create(idl); bridge_reconfigure(cfg ? cfg : &null_cfg); if (cfg) { ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg); discover_types(cfg); } /* If we are completing our initial configuration for this run * of ovs-vswitchd, then keep the transaction around to monitor * it for completion. */ if (initial_config_done) { /* Always sets the 'status_txn_try_again' to check again, * in case that this transaction fails. */ status_txn_try_again = true; ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); } else { initial_config_done = true; daemonize_txn = txn; } } if (daemonize_txn) { enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(daemonize_txn); if (status != TXN_INCOMPLETE) { ovsdb_idl_txn_destroy(daemonize_txn); daemonize_txn = NULL; /* ovs-vswitchd has completed initialization, so allow the * process that forked us to exit successfully. */ daemonize_complete(); vlog_enable_async(); VLOG_INFO_ONCE("%s", ovs_get_program_version()); } } run_stats_update(); run_status_update(); run_system_stats(); } void bridge_wait(void) { struct sset types; const char *type; ovsdb_idl_wait(idl); if (daemonize_txn) { ovsdb_idl_txn_wait(daemonize_txn); } if_notifier_wait(); sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_wait(type); } sset_destroy(&types); if (!hmap_is_empty(&all_bridges)) { struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_wait(br->ofproto); } stats_update_wait(); status_update_wait(); } system_stats_wait(); } /* Adds some memory usage statistics for bridges into 'usage', for use with * memory_report(). */ void bridge_get_memory_usage(struct simap *usage) { struct bridge *br; struct sset types; const char *type; sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_get_memory_usage(type, usage); } sset_destroy(&types); HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_get_memory_usage(br->ofproto, usage); } ovsdb_idl_get_memory_usage(idl, usage); } /* QoS unixctl user interface functions. */ struct qos_unixctl_show_cbdata { struct ds *ds; struct iface *iface; }; static void qos_unixctl_show_queue(unsigned int queue_id, const struct smap *details, struct iface *iface, struct ds *ds) { struct netdev_queue_stats stats; struct smap_node *node; int error; ds_put_cstr(ds, "\n"); if (queue_id) { ds_put_format(ds, "Queue %u:\n", queue_id); } else { ds_put_cstr(ds, "Default:\n"); } SMAP_FOR_EACH (node, details) { ds_put_format(ds, " %s: %s\n", node->key, node->value); } error = netdev_get_queue_stats(iface->netdev, queue_id, &stats); if (!error) { if (stats.tx_packets != UINT64_MAX) { ds_put_format(ds, " tx_packets: %"PRIu64"\n", stats.tx_packets); } if (stats.tx_bytes != UINT64_MAX) { ds_put_format(ds, " tx_bytes: %"PRIu64"\n", stats.tx_bytes); } if (stats.tx_errors != UINT64_MAX) { ds_put_format(ds, " tx_errors: %"PRIu64"\n", stats.tx_errors); } } else { ds_put_format(ds, " Failed to get statistics for queue %u: %s", queue_id, ovs_strerror(error)); } } static void qos_unixctl_show_types(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct sset types = SSET_INITIALIZER(&types); struct iface *iface; const char * types_name; int error; iface = iface_find(argv[1]); if (!iface) { unixctl_command_reply_error(conn, "no such interface"); return; } error = netdev_get_qos_types(iface->netdev, &types); if (!error) { if (!sset_is_empty(&types)) { SSET_FOR_EACH (types_name, &types) { ds_put_format(&ds, "QoS type: %s\n", types_name); } unixctl_command_reply(conn, ds_cstr(&ds)); } else { ds_put_format(&ds, "No QoS types supported for interface: %s\n", iface->name); unixctl_command_reply(conn, ds_cstr(&ds)); } } else { ds_put_format(&ds, "%s: failed to retrieve supported QoS types (%s)", iface->name, ovs_strerror(error)); unixctl_command_reply_error(conn, ds_cstr(&ds)); } sset_destroy(&types); ds_destroy(&ds); } static void qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct smap smap = SMAP_INITIALIZER(&smap); struct iface *iface; const char *type; struct smap_node *node; int error; iface = iface_find(argv[1]); if (!iface) { unixctl_command_reply_error(conn, "no such interface"); return; } error = netdev_get_qos(iface->netdev, &type, &smap); if (!error) { if (*type != '\0') { struct netdev_queue_dump dump; struct smap details; unsigned int queue_id; ds_put_format(&ds, "QoS: %s %s\n", iface->name, type); SMAP_FOR_EACH (node, &smap) { ds_put_format(&ds, "%s: %s\n", node->key, node->value); } smap_init(&details); NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) { qos_unixctl_show_queue(queue_id, &details, iface, &ds); } smap_destroy(&details); unixctl_command_reply(conn, ds_cstr(&ds)); } else { ds_put_format(&ds, "QoS not configured on %s\n", iface->name); unixctl_command_reply(conn, ds_cstr(&ds)); } } else { ds_put_format(&ds, "%s: failed to retrieve QOS configuration (%s)\n", iface->name, ovs_strerror(error)); unixctl_command_reply_error(conn, ds_cstr(&ds)); } smap_destroy(&smap); ds_destroy(&ds); } /* Bridge reconfiguration functions. */ static void bridge_create(const struct ovsrec_bridge *br_cfg) { struct bridge *br; ovs_assert(!bridge_lookup(br_cfg->name)); br = xzalloc(sizeof *br); br->name = xstrdup(br_cfg->name); br->type = xstrdup(ofproto_normalize_type(br_cfg->datapath_type)); br->cfg = br_cfg; /* Derive the default Ethernet address from the bridge's UUID. This should * be unique and it will be stable between ovs-vswitchd runs. */ memcpy(&br->default_ea, &br_cfg->header_.uuid, ETH_ADDR_LEN); eth_addr_mark_random(&br->default_ea); hmap_init(&br->ports); hmap_init(&br->ifaces); hmap_init(&br->iface_by_name); hmap_init(&br->mirrors); hmap_init(&br->mappings); hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0)); } static void bridge_destroy(struct bridge *br, bool del) { if (br) { struct mirror *mirror; struct port *port; HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) { port_destroy(port); } HMAP_FOR_EACH_SAFE (mirror, hmap_node, &br->mirrors) { mirror_destroy(mirror); } hmap_remove(&all_bridges, &br->node); ofproto_destroy(br->ofproto, del); hmap_destroy(&br->ifaces); hmap_destroy(&br->ports); hmap_destroy(&br->iface_by_name); hmap_destroy(&br->mirrors); hmap_destroy(&br->mappings); free(br->name); free(br->type); free(br); } } static struct bridge * bridge_lookup(const char *name) { struct bridge *br; HMAP_FOR_EACH_WITH_HASH (br, node, hash_string(name, 0), &all_bridges) { if (!strcmp(br->name, name)) { return br; } } return NULL; } /* Handle requests for a listing of all flows known by the OpenFlow * stack, including those normally hidden. */ static void bridge_unixctl_dump_flows(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; struct ds results; br = bridge_lookup(argv[argc - 1]); if (!br) { unixctl_command_reply_error(conn, "Unknown bridge"); return; } bool offload_stats = false; for (int i = 1; i < argc - 1; i++) { if (!strcmp(argv[i], "--offload-stats")) { offload_stats = true; } } ds_init(&results); ofproto_get_all_flows(br->ofproto, &results, offload_stats); unixctl_command_reply(conn, ds_cstr(&results)); ds_destroy(&results); } /* "bridge/reconnect [BRIDGE]": makes BRIDGE drop all of its controller * connections and reconnect. If BRIDGE is not specified, then all bridges * drop their controller connections and reconnect. */ static void bridge_unixctl_reconnect(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; if (argc > 1) { br = bridge_lookup(argv[1]); if (!br) { unixctl_command_reply_error(conn, "Unknown bridge"); return; } ofproto_reconnect_controllers(br->ofproto); } else { HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_reconnect_controllers(br->ofproto); } } unixctl_command_reply(conn, NULL); } static size_t bridge_get_controllers(const struct bridge *br, struct ovsrec_controller ***controllersp) { struct ovsrec_controller **controllers; size_t n_controllers; controllers = br->cfg->controller; n_controllers = br->cfg->n_controller; if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) { controllers = NULL; n_controllers = 0; } if (controllersp) { *controllersp = controllers; } return n_controllers; } static void bridge_collect_wanted_ports(struct bridge *br, struct shash *wanted_ports) { size_t i; shash_init(wanted_ports); for (i = 0; i < br->cfg->n_ports; i++) { const char *name = br->cfg->ports[i]->name; if (!shash_add_once(wanted_ports, name, br->cfg->ports[i])) { VLOG_WARN("bridge %s: %s specified twice as bridge port", br->name, name); } } if (bridge_get_controllers(br, NULL) && !shash_find(wanted_ports, br->name)) { VLOG_WARN("bridge %s: no port named %s, synthesizing one", br->name, br->name); ovsrec_interface_init(&br->synth_local_iface); ovsrec_port_init(&br->synth_local_port); br->synth_local_port.interfaces = &br->synth_local_ifacep; br->synth_local_port.n_interfaces = 1; br->synth_local_port.name = br->name; br->synth_local_iface.name = br->name; br->synth_local_iface.type = "internal"; br->synth_local_ifacep = &br->synth_local_iface; shash_add(wanted_ports, br->name, &br->synth_local_port); } } /* Deletes "struct port"s and "struct iface"s under 'br' which aren't * consistent with 'br->cfg'. Updates 'br->if_cfg_queue' with interfaces which * 'br' needs to complete its configuration. */ static void bridge_del_ports(struct bridge *br, const struct shash *wanted_ports) { struct shash_node *port_node; struct port *port; /* Get rid of deleted ports. * Get rid of deleted interfaces on ports that still exist. */ HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) { port->cfg = shash_find_data(wanted_ports, port->name); if (!port->cfg) { port_destroy(port); } else { port_del_ifaces(port); } } /* Update iface->cfg and iface->type in interfaces that still exist. */ SHASH_FOR_EACH (port_node, wanted_ports) { const struct ovsrec_port *port_rec = port_node->data; size_t i; for (i = 0; i < port_rec->n_interfaces; i++) { const struct ovsrec_interface *cfg = port_rec->interfaces[i]; struct iface *iface = iface_lookup(br, cfg->name); const char *type = iface_get_type(cfg, br->cfg); if (iface) { iface->cfg = cfg; iface->type = type; } else { /* We will add new interfaces later. */ } } } } /* Configures the IP stack for 'br''s local interface properly according to the * configuration in 'c'. */ static void bridge_configure_local_iface_netdev(struct bridge *br, struct ovsrec_controller *c) { struct netdev *netdev; struct in_addr mask, gateway; struct iface *local_iface; struct in_addr ip; /* If there's no local interface or no IP address, give up. */ local_iface = iface_from_ofp_port(br, OFPP_LOCAL); if (!local_iface || !c->local_ip || !ip_parse(c->local_ip, &ip.s_addr)) { return; } /* Bring up the local interface. */ netdev = local_iface->netdev; netdev_turn_flags_on(netdev, NETDEV_UP, NULL); /* Configure the IP address and netmask. */ if (!c->local_netmask || !ip_parse(c->local_netmask, &mask.s_addr) || !mask.s_addr) { mask.s_addr = guess_netmask(ip.s_addr); } if (!netdev_set_in4(netdev, ip, mask)) { VLOG_INFO("bridge %s: configured IP address "IP_FMT", netmask "IP_FMT, br->name, IP_ARGS(ip.s_addr), IP_ARGS(mask.s_addr)); } /* Configure the default gateway. */ if (c->local_gateway && ip_parse(c->local_gateway, &gateway.s_addr) && gateway.s_addr) { if (!netdev_add_router(netdev, gateway)) { VLOG_INFO("bridge %s: configured gateway "IP_FMT, br->name, IP_ARGS(gateway.s_addr)); } } } /* Returns true if 'a' and 'b' are the same except that any number of slashes * in either string are treated as equal to any number of slashes in the other, * e.g. "x///y" is equal to "x/y". * * Also, if 'b_stoplen' bytes from 'b' are found to be equal to corresponding * bytes from 'a', the function considers this success. Specify 'b_stoplen' as * SIZE_MAX to compare all of 'a' to all of 'b' rather than just a prefix of * 'b' against a prefix of 'a'. */ static bool equal_pathnames(const char *a, const char *b, size_t b_stoplen) { const char *b_start = b; for (;;) { if (b - b_start >= b_stoplen) { return true; } else if (*a != *b) { return false; } else if (*a == '/') { a += strspn(a, "/"); b += strspn(b, "/"); } else if (*a == '\0') { return true; } else { a++; b++; } } } static enum ofconn_type get_controller_ofconn_type(const char *target, const char *type) { return (type ? (!strcmp(type, "primary") ? OFCONN_PRIMARY : OFCONN_SERVICE) : (!vconn_verify_name(target) ? OFCONN_PRIMARY : OFCONN_SERVICE)); } static void bridge_configure_remotes(struct bridge *br, const struct sockaddr_in *managers, size_t n_managers) { bool disable_in_band; struct ovsrec_controller **controllers; size_t n_controllers; enum ofproto_fail_mode fail_mode; /* Check if we should disable in-band control on this bridge. */ disable_in_band = smap_get_bool(&br->cfg->other_config, "disable-in-band", false); /* Set OpenFlow queue ID for in-band control. */ ofproto_set_in_band_queue(br->ofproto, smap_get_int(&br->cfg->other_config, "in-band-queue", -1)); if (disable_in_band) { ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0); } else { ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers); } n_controllers = (ofproto_get_flow_restore_wait() ? 0 : bridge_get_controllers(br, &controllers)); /* The set of controllers to pass down to ofproto. */ struct shash ocs = SHASH_INITIALIZER(&ocs); /* Add managment controller. */ struct ofproto_controller *oc = xmalloc(sizeof *oc); *oc = (struct ofproto_controller) { .type = OFCONN_SERVICE, .probe_interval = 60, .band = OFPROTO_OUT_OF_BAND, .enable_async_msgs = true, .allowed_versions = bridge_get_allowed_versions(br), .max_pktq_size = bridge_get_controller_queue_size(br, NULL), }; shash_add_nocopy( &ocs, xasprintf("punix:%s/%s.mgmt", ovs_rundir(), br->name), oc); for (size_t i = 0; i < n_controllers; i++) { struct ovsrec_controller *c = controllers[i]; if (daemon_should_self_confine() && (!strncmp(c->target, "punix:", 6) || !strncmp(c->target, "unix:", 5))) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); char *allowed; if (!strncmp(c->target, "unix:", 5)) { /* Connect to a listening socket */ allowed = xasprintf("unix:%s/", ovs_rundir()); if (strchr(c->target, '/') && !equal_pathnames(c->target, allowed, strlen(allowed))) { /* Absolute path specified, but not in ovs_rundir */ VLOG_ERR_RL(&rl, "bridge %s: Not connecting to socket " "controller \"%s\" due to possibility for " "remote exploit. Instead, specify socket " "in permitted directory \"%s\" or connect to " "\"unix:%s/%s.mgmt\" (which is always " "available without special configuration).", br->name, c->target, allowed, ovs_rundir(), br->name); free(allowed); continue; } } else { allowed = xasprintf("punix:%s/%s.", ovs_rundir(), br->name); if (!equal_pathnames(c->target, allowed, strlen(allowed)) || strchr(c->target + strlen(allowed), '/')) { /* Prevent remote ovsdb-server users from accessing * arbitrary Unix domain sockets and overwriting arbitrary * local files. */ VLOG_ERR_RL(&rl, "bridge %s: Not adding Unix domain socket " "controller \"%s\" due to possibility of " "overwriting local files. Instead, specify " "path in permitted format \"%s*\" or " "connect to \"unix:%s/%s.mgmt\" (which is " "always available without special " "configuration).", br->name, c->target, allowed, ovs_rundir(), br->name); free(allowed); continue; } } free(allowed); } if (shash_find(&ocs, c->target)) { static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl2, "bridge %s: Duplicate controllers \"%s\".", br->name, c->target); continue; } bridge_configure_local_iface_netdev(br, c); int dscp = smap_get_int(&c->other_config, "dscp", DSCP_DEFAULT); if (dscp < 0 || dscp > 63) { dscp = DSCP_DEFAULT; } oc = xmalloc(sizeof *oc); *oc = (struct ofproto_controller) { .type = get_controller_ofconn_type(c->target, c->type), .max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8, .probe_interval = (!c->inactivity_probe ? 5 : !*c->inactivity_probe ? 0 : *c->inactivity_probe < 1000 ? 1 : *c->inactivity_probe / 1000), .band = ((!c->connection_mode || !strcmp(c->connection_mode, "in-band")) && !disable_in_band ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND), .enable_async_msgs = (!c->enable_async_messages || *c->enable_async_messages), .allowed_versions = bridge_get_allowed_versions(br), .max_pktq_size = bridge_get_controller_queue_size(br, c), .rate_limit = (c->controller_rate_limit ? *c->controller_rate_limit : 0), .burst_limit = (c->controller_burst_limit ? *c->controller_burst_limit : 0), .dscp = dscp, }; shash_add(&ocs, c->target, oc); } ofproto_set_controllers(br->ofproto, &ocs); shash_destroy_free_data(&ocs); /* Set the fail-mode. */ fail_mode = !br->cfg->fail_mode || !strcmp(br->cfg->fail_mode, "standalone") ? OFPROTO_FAIL_STANDALONE : OFPROTO_FAIL_SECURE; ofproto_set_fail_mode(br->ofproto, fail_mode); /* Configure OpenFlow controller connection snooping. */ if (!ofproto_has_snoops(br->ofproto)) { struct sset snoops; sset_init(&snoops); sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop", ovs_rundir(), br->name)); ofproto_set_snoops(br->ofproto, &snoops); sset_destroy(&snoops); } } static void bridge_configure_tables(struct bridge *br) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); char *prev_prefixes = NULL; int prev_start = 0; int n_tables; int i, j; n_tables = ofproto_get_n_tables(br->ofproto); j = 0; for (i = 0; i < n_tables; i++) { struct ofproto_table_settings s; bool use_default_prefixes = true; s.name = NULL; s.max_flows = UINT_MAX; s.groups = NULL; s.enable_eviction = false; s.n_groups = 0; s.n_prefix_fields = 0; memset(s.prefix_fields, ~0, sizeof(s.prefix_fields)); if (j < br->cfg->n_flow_tables && i == br->cfg->key_flow_tables[j]) { struct ovsrec_flow_table *cfg = br->cfg->value_flow_tables[j++]; s.name = cfg->name; if (cfg->n_flow_limit && *cfg->flow_limit < UINT_MAX) { s.max_flows = *cfg->flow_limit; } s.enable_eviction = (cfg->overflow_policy && !strcmp(cfg->overflow_policy, "evict")); if (cfg->n_groups) { s.groups = xmalloc(cfg->n_groups * sizeof *s.groups); for (int k = 0; k < cfg->n_groups; k++) { const char *string = cfg->groups[k]; char *msg; msg = mf_parse_subfield__(&s.groups[k], &string); if (msg) { VLOG_WARN_RL(&rl, "bridge %s table %d: error parsing " "'groups' (%s)", br->name, i, msg); free(msg); } else if (*string) { VLOG_WARN_RL(&rl, "bridge %s table %d: 'groups' " "element '%s' contains trailing garbage", br->name, i, cfg->groups[k]); } else { s.n_groups++; } } } /* Prefix lookup fields. */ s.n_prefix_fields = 0; for (int k = 0; k < cfg->n_prefixes; k++) { const char *name = cfg->prefixes[k]; const struct mf_field *mf; if (strcmp(name, "none") == 0) { use_default_prefixes = false; s.n_prefix_fields = 0; break; } mf = mf_from_name(name); if (!mf) { VLOG_WARN_RL(&rl, "bridge %s: " "'prefixes' with unknown field: %s", br->name, name); continue; } if (mf->flow_be32ofs < 0 || mf->n_bits % 32) { VLOG_WARN_RL(&rl, "bridge %s: " "'prefixes' with incompatible field: %s", br->name, name); continue; } if (s.n_prefix_fields >= ARRAY_SIZE(s.prefix_fields)) { VLOG_WARN_RL(&rl, "bridge %s: " "'prefixes' with too many fields, " "field not used: %s", br->name, name); continue; } use_default_prefixes = false; s.prefix_fields[s.n_prefix_fields++] = mf->id; } } if (use_default_prefixes) { /* Use default values. */ s.n_prefix_fields = ARRAY_SIZE(default_prefix_fields); memcpy(s.prefix_fields, default_prefix_fields, sizeof default_prefix_fields); } if (VLOG_IS_DBG_ENABLED()) { struct ds ds = DS_EMPTY_INITIALIZER; for (int k = 0; k < s.n_prefix_fields; k++) { if (k) { ds_put_char(&ds, ','); } ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name); } if (s.n_prefix_fields == 0) { ds_put_cstr(&ds, "none"); } if (!prev_prefixes) { prev_prefixes = ds_steal_cstr(&ds); prev_start = i; } else if (prev_prefixes && strcmp(prev_prefixes, ds_cstr(&ds))) { VLOG_DBG("bridge %s tables %d-%d: Prefix lookup with: %s.", br->name, prev_start, i - 1, prev_prefixes); free(prev_prefixes); prev_prefixes = ds_steal_cstr(&ds); prev_start = i; } ds_destroy(&ds); } ofproto_configure_table(br->ofproto, i, &s); free(s.groups); } if (prev_prefixes) { VLOG_DBG("bridge %s tables %d-%d: Prefix lookup with: %s.", br->name, prev_start, n_tables - 1, prev_prefixes); free(prev_prefixes); } for (; j < br->cfg->n_flow_tables; j++) { VLOG_WARN_RL(&rl, "bridge %s: ignoring configuration for flow table " "%"PRId64" not supported by this datapath", br->name, br->cfg->key_flow_tables[j]); } } static void bridge_configure_dp_desc(struct bridge *br) { ofproto_set_dp_desc(br->ofproto, smap_get(&br->cfg->other_config, "dp-desc")); } static void bridge_configure_serial_desc(struct bridge *br) { ofproto_set_serial_desc(br->ofproto, smap_get(&br->cfg->other_config, "dp-sn")); } static struct aa_mapping * bridge_aa_mapping_find(struct bridge *br, const int64_t isid) { struct aa_mapping *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash_bytes(&isid, sizeof isid, 0), &br->mappings) { if (isid == m->isid) { return m; } } return NULL; } static struct aa_mapping * bridge_aa_mapping_create(struct bridge *br, const int64_t isid, const int64_t vlan) { struct aa_mapping *m; m = xzalloc(sizeof *m); m->bridge = br; m->isid = isid; m->vlan = vlan; m->br_name = xstrdup(br->name); hmap_insert(&br->mappings, &m->hmap_node, hash_bytes(&isid, sizeof isid, 0)); return m; } static void bridge_aa_mapping_destroy(struct aa_mapping *m) { if (m) { struct bridge *br = m->bridge; if (br->ofproto) { ofproto_aa_mapping_unregister(br->ofproto, m); } hmap_remove(&br->mappings, &m->hmap_node); if (m->br_name) { free(m->br_name); } free(m); } } static bool bridge_aa_mapping_configure(struct aa_mapping *m) { struct aa_mapping_settings s; s.isid = m->isid; s.vlan = m->vlan; /* Configure. */ ofproto_aa_mapping_register(m->bridge->ofproto, m, &s); return true; } static void bridge_configure_aa(struct bridge *br) { const struct ovsdb_datum *mc; struct ovsrec_autoattach *auto_attach = br->cfg->auto_attach; struct aa_settings aa_s; struct aa_mapping *m; size_t i; if (!auto_attach) { ofproto_set_aa(br->ofproto, NULL, NULL); return; } memset(&aa_s, 0, sizeof aa_s); aa_s.system_description = auto_attach->system_description; aa_s.system_name = auto_attach->system_name; ofproto_set_aa(br->ofproto, NULL, &aa_s); mc = ovsrec_autoattach_get_mappings(auto_attach, OVSDB_TYPE_INTEGER, OVSDB_TYPE_INTEGER); HMAP_FOR_EACH_SAFE (m, hmap_node, &br->mappings) { union ovsdb_atom atom; atom.integer = m->isid; if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER, NULL)) { VLOG_INFO("Deleting isid=%"PRIu32", vlan=%"PRIu16, m->isid, m->vlan); bridge_aa_mapping_destroy(m); } } /* Add new mappings and reconfigure existing ones. */ for (i = 0; i < auto_attach->n_mappings; ++i) { m = bridge_aa_mapping_find(br, auto_attach->key_mappings[i]); if (!m) { VLOG_INFO("Adding isid=%"PRId64", vlan=%"PRId64, auto_attach->key_mappings[i], auto_attach->value_mappings[i]); m = bridge_aa_mapping_create(br, auto_attach->key_mappings[i], auto_attach->value_mappings[i]); if (!bridge_aa_mapping_configure(m)) { bridge_aa_mapping_destroy(m); } } } } static bool bridge_aa_need_refresh(struct bridge *br) { return ofproto_aa_vlan_get_queue_size(br->ofproto) > 0; } static void bridge_aa_update_trunks(struct port *port, struct bridge_aa_vlan *m) { int64_t *trunks = NULL; unsigned int i = 0; bool found = false, reconfigure = false; for (i = 0; i < port->cfg->n_trunks; i++) { if (port->cfg->trunks[i] == m->vlan) { found = true; break; } } switch (m->oper) { case BRIDGE_AA_VLAN_OPER_ADD: if (!found) { trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks + 1)); for (i = 0; i < port->cfg->n_trunks; i++) { trunks[i] = port->cfg->trunks[i]; } trunks[i++] = m->vlan; reconfigure = true; } break; case BRIDGE_AA_VLAN_OPER_REMOVE: if (found) { unsigned int j = 0; trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks - 1)); for (i = 0; i < port->cfg->n_trunks; i++) { if (port->cfg->trunks[i] != m->vlan) { trunks[j++] = port->cfg->trunks[i]; } } i = j; reconfigure = true; } break; case BRIDGE_AA_VLAN_OPER_UNDEF: default: VLOG_WARN("unrecognized operation %u", m->oper); break; } if (reconfigure) { /* VLAN switching under trunk mode cause the trunk port to switch all * VLANs, see ovs-vswitchd.conf.db */ if (i == 0) { static char *vlan_mode_access = "access"; ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_access); } if (i == 1) { static char *vlan_mode_trunk = "trunk"; ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_trunk); } ovsrec_port_set_trunks(port->cfg, trunks, i); /* Force reconfigure of the port. */ port_configure(port); } free(trunks); } static void bridge_aa_refresh_queued(struct bridge *br) { struct ovs_list *list = xmalloc(sizeof *list); struct bridge_aa_vlan *node; ovs_list_init(list); ofproto_aa_vlan_get_queued(br->ofproto, list); LIST_FOR_EACH_SAFE (node, list_node, list) { struct port *port; VLOG_INFO("ifname=%s, vlan=%u, oper=%u", node->port_name, node->vlan, node->oper); port = port_lookup(br, node->port_name); if (port) { bridge_aa_update_trunks(port, node); } ovs_list_remove(&node->list_node); free(node->port_name); free(node); } free(list); } /* Port functions. */ static struct port * port_create(struct bridge *br, const struct ovsrec_port *cfg) { struct port *port; port = xzalloc(sizeof *port); port->bridge = br; port->name = xstrdup(cfg->name); port->cfg = cfg; ovs_list_init(&port->ifaces); hmap_insert(&br->ports, &port->hmap_node, hash_string(port->name, 0)); return port; } /* Deletes interfaces from 'port' that are no longer configured for it. */ static void port_del_ifaces(struct port *port) { struct iface *iface; struct sset new_ifaces; size_t i; /* Collect list of new interfaces. */ sset_init(&new_ifaces); for (i = 0; i < port->cfg->n_interfaces; i++) { sset_add(&new_ifaces, port->cfg->interfaces[i]->name); } /* Get rid of deleted interfaces. */ LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) { if (!sset_contains(&new_ifaces, iface->name)) { iface_destroy(iface); } } sset_destroy(&new_ifaces); } static void port_destroy(struct port *port) { if (port) { struct bridge *br = port->bridge; struct iface *iface; if (br->ofproto) { ofproto_bundle_unregister(br->ofproto, port); } LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) { iface_destroy__(iface); } hmap_remove(&br->ports, &port->hmap_node); free(port->name); free(port); } } static struct port * port_lookup(const struct bridge *br, const char *name) { struct port *port; HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_string(name, 0), &br->ports) { if (!strcmp(port->name, name)) { return port; } } return NULL; } static bool enable_lacp(struct port *port, bool *activep) { if (!port->cfg->lacp) { /* XXX when LACP implementation has been sufficiently tested, enable by * default and make active on bonded ports. */ return false; } else if (!strcmp(port->cfg->lacp, "off")) { return false; } else if (!strcmp(port->cfg->lacp, "active")) { *activep = true; return true; } else if (!strcmp(port->cfg->lacp, "passive")) { *activep = false; return true; } else { VLOG_WARN("port %s: unknown LACP mode %s", port->name, port->cfg->lacp); return false; } } static struct lacp_settings * port_configure_lacp(struct port *port, struct lacp_settings *s) { const char *lacp_time, *system_id; int priority; if (!enable_lacp(port, &s->active)) { return NULL; } s->name = port->name; system_id = smap_get(&port->cfg->other_config, "lacp-system-id"); if (system_id) { if (!ovs_scan(system_id, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(s->id))) { VLOG_WARN("port %s: LACP system ID (%s) must be an Ethernet" " address.", port->name, system_id); return NULL; } } else { s->id = port->bridge->ea; } if (eth_addr_is_zero(s->id)) { VLOG_WARN("port %s: Invalid zero LACP system ID.", port->name); return NULL; } /* Prefer bondable links if unspecified. */ priority = smap_get_int(&port->cfg->other_config, "lacp-system-priority", 0); s->priority = (priority > 0 && priority <= UINT16_MAX ? priority : UINT16_MAX - !ovs_list_is_short(&port->ifaces)); lacp_time = smap_get_def(&port->cfg->other_config, "lacp-time", ""); s->fast = !strcasecmp(lacp_time, "fast"); s->fallback_ab_cfg = smap_get_bool(&port->cfg->other_config, "lacp-fallback-ab", false); return s; } static void iface_configure_lacp(struct iface *iface, struct lacp_member_settings *s) { int priority, portid, key; portid = smap_get_int(&iface->cfg->other_config, "lacp-port-id", 0); priority = smap_get_int(&iface->cfg->other_config, "lacp-port-priority", 0); key = smap_get_int(&iface->cfg->other_config, "lacp-aggregation-key", 0); if (portid <= 0 || portid > UINT16_MAX) { portid = ofp_to_u16(iface->ofp_port); } if (priority <= 0 || priority > UINT16_MAX) { priority = UINT16_MAX; } if (key < 0 || key > UINT16_MAX) { key = 0; } s->name = iface->name; s->id = portid; s->priority = priority; s->key = key; } static void port_configure_bond(struct port *port, struct bond_settings *s) { const char *detect_s; struct iface *iface; const char *mac_s; int miimon_interval; s->name = port->name; s->balance = BM_AB; if (port->cfg->bond_mode) { if (!bond_mode_from_string(&s->balance, port->cfg->bond_mode)) { VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s", port->name, port->cfg->bond_mode, bond_mode_to_string(s->balance)); } } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); /* XXX: Post version 1.5.*, the default bond_mode changed from SLB to * active-backup. At some point we should remove this warning. */ VLOG_WARN_RL(&rl, "port %s: Using the default bond_mode %s. Note that" " in previous versions, the default bond_mode was" " balance-slb", port->name, bond_mode_to_string(s->balance)); } if (s->balance == BM_SLB && port->bridge->cfg->n_flood_vlans) { VLOG_WARN("port %s: SLB bonds are incompatible with flood_vlans, " "please use another bond type or disable flood_vlans", port->name); } miimon_interval = smap_get_int(&port->cfg->other_config, "bond-miimon-interval", 0); if (miimon_interval <= 0) { miimon_interval = 200; } detect_s = smap_get(&port->cfg->other_config, "bond-detect-mode"); if (!detect_s || !strcmp(detect_s, "carrier")) { miimon_interval = 0; } else if (strcmp(detect_s, "miimon")) { VLOG_WARN("port %s: unsupported bond-detect-mode %s, " "defaulting to carrier", port->name, detect_s); miimon_interval = 0; } s->up_delay = MAX(0, port->cfg->bond_updelay); s->down_delay = MAX(0, port->cfg->bond_downdelay); s->basis = smap_get_int(&port->cfg->other_config, "bond-hash-basis", 0); s->rebalance_interval = smap_get_int(&port->cfg->other_config, "bond-rebalance-interval", 10000); if (s->rebalance_interval && s->rebalance_interval < 1000) { s->rebalance_interval = 1000; } s->lacp_fallback_ab_cfg = smap_get_bool(&port->cfg->other_config, "lacp-fallback-ab", false); s->primary = NULL; if (s->balance == BM_AB || s->lacp_fallback_ab_cfg) { s->primary = smap_get(&port->cfg->other_config, "bond-primary"); } LIST_FOR_EACH (iface, port_elem, &port->ifaces) { netdev_set_miimon_interval(iface->netdev, miimon_interval); } mac_s = port->cfg->bond_active_slave; if (!mac_s || !ovs_scan(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(s->active_member_mac))) { /* OVSDB did not store the last active interface */ s->active_member_mac = eth_addr_zero; } /* lb_output action is disabled by default. */ s->use_lb_output_action = (s->balance == BM_TCP) && smap_get_bool(&port->cfg->other_config, "lb-output-action", false); /* all_members_active is disabled by default. */ s->all_members_active = smap_get_bool(&port->cfg->other_config, "all-members-active", false); } /* Returns true if 'port' is synthetic, that is, if we constructed it locally * instead of obtaining it from the database. */ static bool port_is_synthetic(const struct port *port) { return ovsdb_idl_row_is_synthetic(&port->cfg->header_); } /* Interface functions. */ static bool iface_is_internal(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br) { /* The local port and "internal" ports are always "internal". */ return !strcmp(iface->type, "internal") || !strcmp(iface->name, br->name); } /* Returns the correct network device type for interface 'iface' in bridge * 'br'. */ static const char * iface_get_type(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br) { const char *type; /* The local port always has type "internal". Other ports take * their type from the database and default to "system" if none is * specified. */ if (iface_is_internal(iface, br)) { type = "internal"; } else { type = iface->type[0] ? iface->type : "system"; } return type; } static void iface_destroy__(struct iface *iface) { if (iface) { struct port *port = iface->port; struct bridge *br = port->bridge; VLOG_INFO("bridge %s: deleted interface %s on port %d", br->name, iface->name, iface->ofp_port); if (br->ofproto && iface->ofp_port != OFPP_NONE) { ofproto_port_unregister(br->ofproto, iface->ofp_port); } if (iface->ofp_port != OFPP_NONE) { hmap_remove(&br->ifaces, &iface->ofp_port_node); } ovs_list_remove(&iface->port_elem); hmap_remove(&br->iface_by_name, &iface->name_node); tnl_port_map_delete_ipdev(netdev_get_name(iface->netdev)); /* The user is changing configuration here, so netdev_remove needs to be * used as opposed to netdev_close */ netdev_remove(iface->netdev); free(iface->name); free(iface); } } static void iface_destroy(struct iface *iface) { if (iface) { struct port *port = iface->port; iface_destroy__(iface); if (ovs_list_is_empty(&port->ifaces)) { port_destroy(port); } } } static struct iface * iface_lookup(const struct bridge *br, const char *name) { struct iface *iface; HMAP_FOR_EACH_WITH_HASH (iface, name_node, hash_string(name, 0), &br->iface_by_name) { if (!strcmp(iface->name, name)) { return iface; } } return NULL; } static struct iface * iface_find(const char *name) { const struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { struct iface *iface = iface_lookup(br, name); if (iface) { return iface; } } return NULL; } static struct iface * iface_from_ofp_port(const struct bridge *br, ofp_port_t ofp_port) { struct iface *iface; HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node, hash_ofp_port(ofp_port), &br->ifaces) { if (iface->ofp_port == ofp_port) { return iface; } } return NULL; } /* Set Ethernet address of 'iface', if one is specified in the configuration * file. */ static void iface_set_mac(const struct bridge *br, const struct port *port, struct iface *iface) { struct eth_addr ea, *mac = NULL; struct iface *hw_addr_iface; if (strcmp(iface->type, "internal")) { return; } if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, &ea)) { mac = &ea; } else if (port->cfg->fake_bridge) { /* Fake bridge and no MAC set in the configuration. Pick a local one. */ find_local_hw_addr(br, &ea, port, &hw_addr_iface); mac = &ea; } if (mac) { if (iface->ofp_port == OFPP_LOCAL) { VLOG_ERR("interface %s: ignoring mac in Interface record " "(use Bridge record to set local port's mac)", iface->name); } else if (eth_addr_is_multicast(*mac)) { VLOG_ERR("interface %s: cannot set MAC to multicast address", iface->name); } else if (eth_addr_is_zero(*mac)) { VLOG_ERR("interface %s: cannot set MAC to all zero address", iface->name); } else { int error = netdev_set_etheraddr(iface->netdev, *mac); if (error) { VLOG_ERR("interface %s: setting MAC failed (%s)", iface->name, ovs_strerror(error)); } } } } /* Sets the ofport column of 'if_cfg' to 'ofport'. */ static void iface_set_ofport(const struct ovsrec_interface *if_cfg, ofp_port_t ofport) { if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) { int64_t port = ofport == OFPP_NONE ? -1 : ofp_to_u16(ofport); ovsrec_interface_set_ofport(if_cfg, &port, 1); } } /* Clears all of the fields in 'if_cfg' that indicate interface status, and * sets the "ofport" field to -1. * * This is appropriate when 'if_cfg''s interface cannot be created or is * otherwise invalid. */ static void iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp) { if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) { iface_set_ofport(if_cfg, OFPP_NONE); ovsrec_interface_set_error(if_cfg, errp); ovsrec_interface_set_status(if_cfg, NULL); ovsrec_interface_set_admin_state(if_cfg, NULL); ovsrec_interface_set_duplex(if_cfg, NULL); ovsrec_interface_set_link_speed(if_cfg, NULL, 0); ovsrec_interface_set_link_state(if_cfg, NULL); ovsrec_interface_set_mac_in_use(if_cfg, NULL); ovsrec_interface_set_mtu(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(if_cfg, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(if_cfg, NULL, 0); ovsrec_interface_set_lacp_current(if_cfg, NULL, 0); ovsrec_interface_set_statistics(if_cfg, NULL, NULL, 0); ovsrec_interface_set_ifindex(if_cfg, NULL, 0); } } static bool queue_ids_include(const struct ovsdb_datum *queues, int64_t target) { union ovsdb_atom atom; atom.integer = target; return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER, NULL); } static void iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos) { struct ofpbuf queues_buf; ofpbuf_init(&queues_buf, 0); if (!qos || qos->type[0] == '\0') { netdev_set_qos(iface->netdev, NULL, NULL); } else { const struct ovsdb_datum *queues; struct netdev_queue_dump dump; unsigned int queue_id; struct smap details; bool queue_zero; size_t i; /* Configure top-level Qos for 'iface'. */ netdev_set_qos(iface->netdev, qos->type, &qos->other_config); /* Deconfigure queues that were deleted. */ queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER, OVSDB_TYPE_UUID); smap_init(&details); NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) { if (!queue_ids_include(queues, queue_id)) { netdev_delete_queue(iface->netdev, queue_id); } } smap_destroy(&details); /* Configure queues for 'iface'. */ queue_zero = false; for (i = 0; i < qos->n_queues; i++) { const struct ovsrec_queue *queue = qos->value_queues[i]; queue_id = qos->key_queues[i]; if (queue_id == 0) { queue_zero = true; } if (queue->n_dscp == 1) { struct ofproto_port_queue *port_queue; port_queue = ofpbuf_put_uninit(&queues_buf, sizeof *port_queue); port_queue->queue = queue_id; port_queue->dscp = queue->dscp[0]; } netdev_set_queue(iface->netdev, queue_id, &queue->other_config); } if (!queue_zero) { smap_init(&details); netdev_set_queue(iface->netdev, 0, &details); smap_destroy(&details); } } if (iface->ofp_port != OFPP_NONE) { const struct ofproto_port_queue *port_queues = queues_buf.data; size_t n_queues = queues_buf.size / sizeof *port_queues; ofproto_port_set_queues(iface->port->bridge->ofproto, iface->ofp_port, port_queues, n_queues); } netdev_set_policing(iface->netdev, MIN(UINT32_MAX, iface->cfg->ingress_policing_rate), MIN(UINT32_MAX, iface->cfg->ingress_policing_burst), MIN(UINT32_MAX, iface->cfg->ingress_policing_kpkts_rate), MIN(UINT32_MAX, iface->cfg->ingress_policing_kpkts_burst)); ofpbuf_uninit(&queues_buf); } static void iface_configure_cfm(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; const char *opstate_str; const char *cfm_ccm_vlan; struct cfm_settings s; struct smap netdev_args; if (!cfg->n_cfm_mpid) { ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port); return; } s.check_tnl_key = false; smap_init(&netdev_args); if (!netdev_get_config(iface->netdev, &netdev_args)) { const char *key = smap_get(&netdev_args, "key"); const char *in_key = smap_get(&netdev_args, "in_key"); s.check_tnl_key = (key && !strcmp(key, "flow")) || (in_key && !strcmp(in_key, "flow")); } smap_destroy(&netdev_args); s.mpid = *cfg->cfm_mpid; s.interval = smap_get_int(&iface->cfg->other_config, "cfm_interval", 0); cfm_ccm_vlan = smap_get(&iface->cfg->other_config, "cfm_ccm_vlan"); s.ccm_pcp = smap_get_int(&iface->cfg->other_config, "cfm_ccm_pcp", 0); if (s.interval <= 0) { s.interval = 1000; } if (!cfm_ccm_vlan) { s.ccm_vlan = 0; } else if (!strcasecmp("random", cfm_ccm_vlan)) { s.ccm_vlan = CFM_RANDOM_VLAN; } else { s.ccm_vlan = atoi(cfm_ccm_vlan); if (s.ccm_vlan == CFM_RANDOM_VLAN) { s.ccm_vlan = 0; } } s.extended = smap_get_bool(&iface->cfg->other_config, "cfm_extended", false); s.demand = smap_get_bool(&iface->cfg->other_config, "cfm_demand", false); opstate_str = smap_get(&iface->cfg->other_config, "cfm_opstate"); s.opup = !opstate_str || !strcasecmp("up", opstate_str); ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port, &s); } /* Returns true if 'iface' is synthetic, that is, if we constructed it locally * instead of obtaining it from the database. */ static bool iface_is_synthetic(const struct iface *iface) { return ovsdb_idl_row_is_synthetic(&iface->cfg->header_); } static ofp_port_t iface_validate_ofport__(size_t n, int64_t *ofport) { return (n && *ofport >= 1 && *ofport < ofp_to_u16(OFPP_MAX) ? u16_to_ofp(*ofport) : OFPP_NONE); } static ofp_port_t iface_get_requested_ofp_port(const struct ovsrec_interface *cfg) { return iface_validate_ofport__(cfg->n_ofport_request, cfg->ofport_request); } static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *cfg) { ofp_port_t requested_ofport = iface_get_requested_ofp_port(cfg); return (requested_ofport != OFPP_NONE ? requested_ofport : iface_validate_ofport__(cfg->n_ofport, cfg->ofport)); } /* Port mirroring. */ static struct mirror * mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid) { struct mirror *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, uuid_hash(uuid), &br->mirrors) { if (uuid_equals(uuid, &m->uuid)) { return m; } } return NULL; } static void bridge_configure_mirrors(struct bridge *br) { const struct ovsdb_datum *mc; unsigned long *flood_vlans; struct mirror *m; size_t i; /* Get rid of deleted mirrors. */ mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID); HMAP_FOR_EACH_SAFE (m, hmap_node, &br->mirrors) { union ovsdb_atom atom; atom.uuid = m->uuid; if (!ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID, NULL)) { mirror_destroy(m); } } /* Add new mirrors and reconfigure existing ones. */ for (i = 0; i < br->cfg->n_mirrors; i++) { const struct ovsrec_mirror *cfg = br->cfg->mirrors[i]; m = mirror_find_by_uuid(br, &cfg->header_.uuid); if (!m) { m = mirror_create(br, cfg); } m->cfg = cfg; if (!mirror_configure(m)) { mirror_destroy(m); } } /* Update flooded vlans (for RSPAN). */ flood_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans, br->cfg->n_flood_vlans); ofproto_set_flood_vlans(br->ofproto, flood_vlans); bitmap_free(flood_vlans); } static struct mirror * mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg) { struct mirror *m; m = xzalloc(sizeof *m); m->uuid = cfg->header_.uuid; hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid)); m->bridge = br; m->name = xstrdup(cfg->name); return m; } static void mirror_destroy(struct mirror *m) { if (m) { struct bridge *br = m->bridge; if (br->ofproto) { ofproto_mirror_unregister(br->ofproto, m); } hmap_remove(&br->mirrors, &m->hmap_node); free(m->name); free(m); } } static void mirror_collect_ports(struct mirror *m, struct ovsrec_port **in_ports, int n_in_ports, void ***out_portsp, size_t *n_out_portsp) { void **out_ports = xmalloc(n_in_ports * sizeof *out_ports); size_t n_out_ports = 0; size_t i; for (i = 0; i < n_in_ports; i++) { const char *name = in_ports[i]->name; struct port *port = port_lookup(m->bridge, name); if (port) { out_ports[n_out_ports++] = port; } else { VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent " "port %s", m->bridge->name, m->name, name); } } *out_portsp = out_ports; *n_out_portsp = n_out_ports; } static bool mirror_configure(struct mirror *m) { const struct ovsrec_mirror *cfg = m->cfg; struct ofproto_mirror_settings s; int ret; /* Set name. */ if (strcmp(cfg->name, m->name)) { free(m->name); m->name = xstrdup(cfg->name); } s.name = m->name; /* Get output port or VLAN. */ if (cfg->output_port) { s.out_bundle = port_lookup(m->bridge, cfg->output_port->name); if (!s.out_bundle) { VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge", m->bridge->name, m->name); return false; } s.out_vlan = UINT16_MAX; if (cfg->output_vlan) { VLOG_ERR("bridge %s: mirror %s specifies both output port and " "output vlan; ignoring output vlan", m->bridge->name, m->name); } } else if (cfg->output_vlan) { /* The database should prevent invalid VLAN values. */ s.out_bundle = NULL; s.out_vlan = *cfg->output_vlan; } else { VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring", m->bridge->name, m->name); return false; } if (cfg->snaplen) { s.snaplen = *cfg->snaplen; } else { s.snaplen = 0; } /* Get port selection. */ if (cfg->select_all) { size_t n_ports = hmap_count(&m->bridge->ports); void **ports = xmalloc(n_ports * sizeof *ports); struct port *port; size_t i; i = 0; HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) { ports[i++] = port; } s.srcs = ports; s.n_srcs = n_ports; s.dsts = ports; s.n_dsts = n_ports; } else { /* Get ports, dropping ports that don't exist. * The IDL ensures that there are no duplicates. */ mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port, &s.srcs, &s.n_srcs); mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port, &s.dsts, &s.n_dsts); } /* Get VLAN selection. */ s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, cfg->n_select_vlan); /* Set the filter, mirror_set() will strdup this pointer. */ s.filter = cfg->filter; /* Configure. */ ret = ofproto_mirror_register(m->bridge->ofproto, m, &s); if (ret == EOPNOTSUPP) { VLOG_ERR("ofproto %s: does not support mirroring", m->bridge->ofproto->name); } else if (ret) { VLOG_ERR("bridge %s: mirror %s configuration is invalid", m->bridge->name, m->name); } /* Clean up. */ if (s.srcs != s.dsts) { free(s.dsts); } free(s.srcs); free(s.src_vlans); return true; } static void mirror_refresh_stats(struct mirror *m) { struct ofproto *ofproto = m->bridge->ofproto; uint64_t tx_packets, tx_bytes; const char *keys[2]; int64_t values[2]; size_t stat_cnt = 0; if (ofproto_mirror_get_stats(ofproto, m, &tx_packets, &tx_bytes)) { ovsrec_mirror_set_statistics(m->cfg, NULL, NULL, 0); return; } if (tx_packets != UINT64_MAX) { keys[stat_cnt] = "tx_packets"; values[stat_cnt] = tx_packets; stat_cnt++; } if (tx_bytes != UINT64_MAX) { keys[stat_cnt] = "tx_bytes"; values[stat_cnt] = tx_bytes; stat_cnt++; } ovsrec_mirror_set_statistics(m->cfg, keys, values, stat_cnt); } /* * Add registered netdev and dpif types to ovsdb to allow external * applications to query the capabilities of the Open vSwitch instance * running on the node. */ static void discover_types(const struct ovsrec_open_vswitch *cfg) { struct sset types; /* Datapath types. */ sset_init(&types); dp_enumerate_types(&types); const char **datapath_types = sset_array(&types); ovsrec_open_vswitch_set_datapath_types(cfg, datapath_types, sset_count(&types)); free(datapath_types); sset_destroy(&types); /* Port types. */ sset_init(&types); netdev_enumerate_types(&types); const char **iface_types = sset_array(&types); ovsrec_open_vswitch_set_iface_types(cfg, iface_types, sset_count(&types)); free(iface_types); sset_destroy(&types); } openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/bridge.h000066400000000000000000000016321514270232600222220ustar00rootroot00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * 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 VSWITCHD_BRIDGE_H #define VSWITCHD_BRIDGE_H 1 #include struct simap; void bridge_init(const char *remote); void bridge_exit(bool delete_datapath); void bridge_run(void); void bridge_wait(void); void bridge_get_memory_usage(struct simap *usage); #endif /* bridge.h */ openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/ovs-vswitchd.8.in000066400000000000000000000375051514270232600237630ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-vswitchd 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-vswitchd . .SH NAME ovs\-vswitchd \- Open vSwitch daemon . .SH SYNOPSIS \fBovs\-vswitchd \fR[\fIdatabase\fR] . .SH DESCRIPTION A daemon that manages and controls any number of Open vSwitch switches on the local machine. .PP The \fIdatabase\fR argument specifies how \fBovs\-vswitchd\fR connects to \fBovsdb\-server\fR. \fIdatabase\fR may be an OVSDB active or passive connection method, as described in \fBovsdb\fR(7). The default is \fBunix:@RUNDIR@/db.sock\fR. .PP \fBovs\-vswitchd\fR retrieves its configuration from \fIdatabase\fR at startup. It sets up Open vSwitch datapaths and then operates switching across each bridge described in its configuration files. As the database changes, \fBovs\-vswitchd\fR automatically updates its configuration to match. .PP \fBovs\-vswitchd\fR switches may be configured with any of the following features: . .IP \(bu L2 switching with MAC learning. . .IP \(bu NIC bonding with automatic fail-over and source MAC-based TX load balancing ("SLB"). . .IP \(bu 802.1Q VLAN support. . .IP \(bu Port mirroring, with optional VLAN tagging. . .IP \(bu NetFlow v5 flow logging. . .IP \(bu sFlow(R) monitoring. . .IP \(bu Connectivity to an external OpenFlow controller, such as NOX. . .PP Only a single instance of \fBovs\-vswitchd\fR is intended to run at a time. A single \fBovs\-vswitchd\fR can manage any number of switch instances, up to the maximum number of supported Open vSwitch datapaths. .PP \fBovs\-vswitchd\fR does all the necessary management of Open vSwitch datapaths itself. Thus, \fBovs\-dpctl\fR(8) (and its userspace datapath counterparts accessible via \fBovs\-appctl dpctl/\fIcommand\fR) are not needed with \fBovs\-vswitchd\fR and should not be used because they can interfere with its operation. These tools are still useful for diagnostics. .PP An Open vSwitch datapath kernel module must be loaded for \fBovs\-vswitchd\fR to be useful. Refer to the documentation for instructions on how to build and load the Open vSwitch kernel module. .PP .SH OPTIONS .IP "\fB\-\-mlockall\fR" Causes \fBovs\-vswitchd\fR to call the \fBmlockall()\fR function, to attempt to lock all of its process memory into physical RAM on page faults (on allocation, when running on Linux kernel 4.4 or older), preventing the kernel from paging any of its memory to disk. This helps to avoid networking interruptions due to system memory pressure. .IP Some systems do not support \fBmlockall()\fR at all, and other systems only allow privileged users, such as the superuser, to use it. \fBovs\-vswitchd\fR emits a log message if \fBmlockall()\fR is unavailable or unsuccessful. . .SS "DPDK Options" For details on initializing \fBovs\-vswitchd\fR to use DPDK ports, refer to the documentation or \fBovs\-vswitchd.conf.db\fR(5). .SS "DPDK HW Access Options" .IP "\fB\-\-hw\-rawio\-access\fR" Tells \fBovs\-vswitchd\fR to retain the \fBCAP_SYS_RAWIO\fR capability, to allow userspace drivers access to raw hardware memory. This will also allow the \fBovs\-vswitchd\fR daemon to call \fBiopl()\fR and \fBioperm()\fR functions as well as access memory devices to set port access. This is a \fBvery\fR powerful capability, so generally only enable as needed for specific hardware (for example mlx5 with full hardware offload via rte_flow). .SS "Daemon Options" .ds DD \ \fBovs\-vswitchd\fR detaches only after it has connected to the \ database, retrieved the initial configuration, and set up that \ configuration. .so lib/daemon.man .SS "Service Options" .so lib/service.man .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .SS "Logging Options" .so lib/vlog.man .SS "Other Options" .so lib/unixctl.man .so lib/common.man . .SH "RUNTIME MANAGEMENT COMMANDS" \fBovs\-appctl\fR(8) can send commands to a running \fBovs\-vswitchd\fR process. The currently supported commands are described below. The command descriptions assume an understanding of how to configure Open vSwitch. .SS "GENERAL COMMANDS" .IP "\fBexit\fR \fI--cleanup\fR" Causes \fBovs\-vswitchd\fR to gracefully terminate. If \fI--cleanup\fR is specified, deletes flows from datapaths and releases other datapath resources configured by \fBovs\-vswitchd\fR. Otherwise, datapath flows and other resources remains undeleted. Resources of datapaths that are integrated into \fBovs\-vswitchd\fR (e.g. the \fBnetdev\fR datapath type) are always released regardless of \fI--cleanup\fR except for ports with \fBinternal\fR type. Use \fI--cleanup\fR to release \fBinternal\fR ports too. . .IP "\fBqos/show-types\fR \fIinterface\fR" Queries the interface for a list of Quality of Service types that are configurable via Open vSwitch for the given \fIinterface\fR. .IP "\fBqos/show\fR \fIinterface\fR" Queries the kernel for Quality of Service configuration and statistics associated with the given \fIinterface\fR. .IP "\fBbfd/show\fR [\fIinterface\fR]" Displays detailed information about Bidirectional Forwarding Detection configured on \fIinterface\fR. If \fIinterface\fR is not specified, then displays detailed information about all interfaces with BFD enabled. .IP "\fBbfd/set-forwarding\fR [\fIinterface\fR] \fIstatus\fR" Force the fault status of the BFD module on \fIinterface\fR (or all interfaces if none is given) to be \fIstatus\fR. \fIstatus\fR can be "true", "false", or "normal" which reverts to the standard behavior. .IP "\fBcfm/show\fR [\fIinterface\fR]" Displays detailed information about Connectivity Fault Management configured on \fIinterface\fR. If \fIinterface\fR is not specified, then displays detailed information about all interfaces with CFM enabled. .IP "\fBcfm/set-fault\fR [\fIinterface\fR] \fIstatus\fR" Force the fault status of the CFM module on \fIinterface\fR (or all interfaces if none is given) to be \fIstatus\fR. \fIstatus\fR can be "true", "false", or "normal" which reverts to the standard behavior. .IP "\fBstp/tcn\fR [\fIbridge\fR]" Forces a topology change event on \fIbridge\fR if it's running STP. This may cause it to send Topology Change Notifications to its peers and flush its MAC table. If no \fIbridge\fR is given, forces a topology change event on all bridges. .IP "\fBstp/show\fR [\fIbridge\fR]" Displays detailed information about spanning tree on the \fIbridge\fR. If \fIbridge\fR is not specified, then displays detailed information about all bridges with STP enabled. .IP "\fBrstp/tcn\fR [\fIbridge\fR]" Forces a topology change event on \fIbridge\fR if it's running RSTP. This may cause it to send Topology Change Notifications to its peers and flush its MAC table. If no \fIbridge\fR is given, forces a topology change event on all bridges. .IP "\fBrstp/show\fR [\fIbridge\fR]" Displays detailed information about rapid spanning tree on the \fIbridge\fR. If \fIbridge\fR is not specified, then displays detailed information about all bridges with RSTP enabled. .SS "BRIDGE COMMANDS" These commands manage bridges. .IP "\fBfdb/add\fR \fIbridge\fR \fIport\fR \fIvlan\fR \fImac\fR" Adds \fImac\fR address to a \fIport\fR and \fIvlan\fR on a \fIbridge\fR. This utility can be used to pre-populate fdb table without relying on dynamic mac learning. .IP "\fBfdb/del\fR \fIbridge\fR \fIvlan\fR \fImac\fR" Deletes \fImac\fR address from a \fIport\fR and \fIvlan\fR on a \fIbridge\fR. .IP "\fBfdb/flush\fR [\fIbridge\fR]" Flushes \fIbridge\fR MAC address learning table, or all learning tables if no \fIbridge\fR is given. .IP "\fBfdb/show\fR \fIbridge\fR" Lists each MAC address/VLAN pair learned by the specified \fIbridge\fR, along with the port on which it was learned and the age of the entry, in seconds. .IP "\fBfdb/stats-clear\fR [\fIbridge\fR]" Clear \fIbridge\fR MAC address learning table statistics, or all statistics if no \fIbridge\fR is given. .IP "\fBfdb/stats-show\fR \fIbridge\fR" Show MAC address learning table statistics for the specified \fIbridge\fR. .IP "\fBmdb/flush\fR [\fIbridge\fR]" Flushes \fIbridge\fR multicast snooping table, or all snooping tables if no \fIbridge\fR is given. .IP "\fBmdb/show\fR \fIbridge\fR" Lists each multicast group/VLAN pair learned by the specified \fIbridge\fR, along with the port on which it was learned and the age of the entry, in seconds. .IP "\fBbridge/reconnect\fR [\fIbridge\fR]" Makes \fIbridge\fR drop all of its OpenFlow controller connections and reconnect. If \fIbridge\fR is not specified, then all bridges drop their controller connections and reconnect. .IP This command might be useful for debugging OpenFlow controller issues. . .IP "\fBbridge/dump\-flows\fR [\fB\-\-offload-stats\fR] \fIbridge\fR" Lists all flows in \fIbridge\fR, including those normally hidden to commands such as \fBovs\-ofctl dump\-flows\fR. Flows set up by mechanisms such as in-band control and fail-open are hidden from the controller since it is not allowed to modify or override them. If \fB\-\-offload-stats\fR are specified then also list statistics for offloaded packets and bytes, which are a subset of the total packets and bytes. .SS "BOND COMMANDS" These commands manage bonded ports on an Open vSwitch's bridges. To understand some of these commands, it is important to understand a detail of the bonding implementation called ``source load balancing'' (SLB). Instead of directly assigning Ethernet source addresses to members, the bonding implementation computes a function that maps an 48-bit Ethernet source addresses into an 8-bit value (a ``MAC hash'' value). All of the Ethernet addresses that map to a single 8-bit value are then assigned to a single member. .IP "\fBbond/list\fR" Lists all of the bonds, and their members, on each bridge. . .IP "\fBbond/show\fR [\fIport\fR]" Lists all of the bond-specific information (updelay, downdelay, time until the next rebalance) about the given bonded \fIport\fR, or all bonded ports if no \fIport\fR is given. Also lists information about each members: whether it is enabled or disabled, the time to completion of an updelay or downdelay if one is in progress, whether it is the active member, the hashes assigned to the member. Any LACP information related to this bond may be found using the \fBlacp/show\fR command. . .IP "\fBbond/migrate\fR \fIport\fR \fIhash\fR \fImember\fR" Only valid for SLB bonds. Assigns a given MAC hash to a new member. \fIport\fR specifies the bond port, \fIhash\fR the MAC hash to be migrated (as a decimal number between 0 and 255), and \fImember\fR the new member to be assigned. .IP The reassignment is not permanent: rebalancing or fail-over will cause the MAC hash to be shifted to a new member in the usual manner. .IP A MAC hash cannot be migrated to a disabled member. .IP "\fBbond/set\-active\-member\fR \fIport\fR \fImember\fR" Sets \fImember\fR as the active member on \fIport\fR. \fImember\fR must currently be enabled. .IP The setting is not permanent: a new active member will be selected if \fImember\fR becomes disabled. .IP "\fBbond/enable\-member\fR \fIport\fR \fImember\fR" .IQ "\fBbond/disable\-member\fR \fIport\fR \fImember\fR" Enables (or disables) \fImember\fR on the given bond \fIport\fR, skipping any updelay (or downdelay). .IP This setting is not permanent: it persists only until the carrier status of \fImember\fR changes. .IP "\fBbond/hash\fR \fImac\fR [\fIvlan\fR] [\fIbasis\fR]" Returns the hash value which would be used for \fImac\fR with \fIvlan\fR and \fIbasis\fR if specified. . .IP "\fBlacp/show\fR [\fIport\fR]" Lists all of the LACP related information about the given \fIport\fR: active or passive, aggregation key, system id, and system priority. Also lists information about each member: whether it is enabled or disabled, whether it is attached or detached, port id and priority, actor information, and partner information. If \fIport\fR is not specified, then displays detailed information about all interfaces with CFM enabled. . .IP "\fBlacp/stats-show\fR [\fIport\fR]" Lists various stats about LACP PDUs (number of RX/TX PDUs, bad PDUs received) and member state (number of times its state expired/defaulted and carrier status changed) for the given \fIport\fR. If \fIport\fR is not specified, then displays stats of all interfaces with LACP enabled. .SS "DPCTL DATAPATH DEBUGGING COMMANDS" The primary way to configure \fBovs\-vswitchd\fR is through the Open vSwitch database, e.g. using \fBovs\-vsctl\fR(8). These commands provide a debugging interface for managing datapaths. They implement the same features (and syntax) as \fBovs\-dpctl\fR(8). Unlike \fBovs\-dpctl\fR(8), these commands work with datapaths that are integrated into \fBovs\-vswitchd\fR (e.g. the \fBnetdev\fR datapath type). .PP . .ds DX \fBdpctl/\fR .de DO \\$2 \\$1 \\$3 .. .so lib/dpctl.man . .so lib/dpdk-unixctl.man .so lib/dpif-netdev-unixctl.man .so lib/dpif-netlink-unixctl.man .so lib/netdev-dpdk-unixctl.man .so lib/odp-execute-unixctl.man .so ofproto/ofproto-dpif-unixctl.man .so ofproto/ofproto-unixctl.man .so lib/vlog-unixctl.man .so lib/memory-unixctl.man .so lib/coverage-unixctl.man .so ofproto/ofproto-tnl-unixctl.man . .SH "OPENFLOW IMPLEMENTATION" . .PP This section documents aspects of OpenFlow for which the OpenFlow specification requires documentation. . .SS "Packet buffering." The OpenFlow specification, version 1.2, says: . .IP Switches that implement buffering are expected to expose, through documentation, both the amount of available buffering, and the length of time before buffers may be reused. . .PP Open vSwitch does not maintains any packet buffers. . .SS "Bundle lifetime" The OpenFlow specification, version 1.4, says: . .IP If the switch does not receive any OFPT_BUNDLE_CONTROL or OFPT_BUNDLE_ADD_MESSAGE message for an opened bundle_id for a switch defined time greater than 1s, it may send an ofp_error_msg with OFPET_BUNDLE_FAILED type and OFPBFC_TIMEOUT code. If the switch does not receive any new message in a bundle apart from echo request and replies for a switch defined time greater than 1s, it may send an ofp_error_msg with OFPET_BUNDLE_FAILED type and OFPBFC_TIMEOUT code. . .PP Open vSwitch implements default idle bundle lifetime of 10 seconds. (This is configurable via \fBother-config:bundle-idle-timeout\fR in the \fBOpen_vSwitch\fR table. See \fBovs-vswitchd.conf.db\fR(5) for details.) . .SH "LIMITS" . .PP We believe these limits to be accurate as of this writing. These limits assume the use of the Linux kernel datapath. . .IP \(bu \fBovs\-vswitchd\fR started through \fBovs\-ctl\fR(8) provides a limit of 65535 file descriptors. The limits on the number of bridges and ports is decided by the availability of file descriptors. With the Linux kernel datapath, creation of a single bridge consumes three file descriptors and each port consumes one additional file descriptor. Other platforms may have different limitations. . .IP \(bu 8,192 MAC learning entries per bridge, by default. (This is configurable via \fBother\-config:mac\-table\-size\fR in the \fBBridge\fR table. See \fBovs\-vswitchd.conf.db\fR(5) for details.) . .IP \(bu Kernel flows are limited only by memory available to the kernel. Performance will degrade beyond 1,048,576 kernel flows per bridge with a 32-bit kernel, beyond 262,144 with a 64-bit kernel. (\fBovs\-vswitchd\fR should never install anywhere near that many flows.) . .IP \(bu OpenFlow flows are limited only by available memory. Performance is linear in the number of unique wildcard patterns. That is, an OpenFlow table that contains many flows that all match on the same fields in the same way has a constant-time lookup, but a table that contains many flows that match on different fields requires lookup time linear in the number of flows. . .IP \(bu 255 ports per bridge participating in 802.1D Spanning Tree Protocol. . .IP \(bu 32 mirrors per bridge. . .IP \(bu 15 bytes for the name of a port, for ports implemented in the Linux kernel. Ports implemented in userspace, such as patch ports, do not have an arbitrary length limitation. OpenFlow also limit port names to 15 bytes. . .SH "SEE ALSO" .BR ovs\-appctl (8), .BR ovsdb\-server (1). openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/ovs-vswitchd.c000066400000000000000000000216001514270232600234160ustar00rootroot00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #ifdef HAVE_MLOCKALL #include #endif #include "bridge.h" #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "dirs.h" #include "dpif.h" #include "dummy.h" #include "fatal-signal.h" #include "memory.h" #include "netdev.h" #include "openflow/openflow.h" #include "ovsdb-idl.h" #include "ovs-rcu.h" #include "ovs-router.h" #include "ovs-thread.h" #include "openvswitch/poll-loop.h" #include "simap.h" #include "stream-ssl.h" #include "stream.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/usdt-probes.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "lib/vswitch-idl.h" #include "lib/dns-resolve.h" VLOG_DEFINE_THIS_MODULE(vswitchd); /* --mlockall: If set, locks all present process memory pages into physical * RAM and all the new pages the moment they are faulted in, preventing * the kernel from paging any of its memory to disk. */ static bool want_mlockall; /* --hw-rawio-access: If set, retains CAP_SYS_RAWIO privileges. */ static bool hw_rawio_access; static unixctl_cb_func ovs_vswitchd_exit; static char *parse_options(int argc, char *argv[], char **unixctl_path); OVS_NO_RETURN static void usage(void); static struct ovs_vswitchd_exit_args { struct unixctl_conn **conns; size_t n_conns; bool exiting; bool cleanup; } exit_args; int main(int argc, char *argv[]) { struct unixctl_server *unixctl; char *unixctl_path = NULL; char *remote; int retval; set_program_name(argv[0]); ovsthread_id_init(); dns_resolve_init(true); ovs_cmdl_proctitle_init(argc, argv); service_start(&argc, &argv); remote = parse_options(argc, argv, &unixctl_path); fatal_ignore_sigpipe(); daemonize_start(true, hw_rawio_access); if (want_mlockall) { #ifdef HAVE_MLOCKALL /* MCL_ONFAULT introduced in Linux kernel 4.4. */ #ifndef MCL_ONFAULT #define MCL_ONFAULT 4 #endif if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT)) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) { VLOG_ERR("mlockall failed: %s", ovs_strerror(errno)); } else { set_all_memory_locked(); } } #else VLOG_ERR("mlockall not supported on this system"); #endif } retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "[--cleanup]", 0, 1, ovs_vswitchd_exit, NULL); bridge_init(remote); free(remote); while (!exit_args.exiting) { OVS_USDT_PROBE(main, run_start); memory_run(); if (memory_should_report()) { struct simap usage; simap_init(&usage); bridge_get_memory_usage(&usage); memory_report(&usage); simap_destroy(&usage); } bridge_run(); unixctl_server_run(unixctl); netdev_run(); memory_wait(); bridge_wait(); unixctl_server_wait(unixctl); netdev_wait(); if (exit_args.exiting) { poll_immediate_wake(); } OVS_USDT_PROBE(main, poll_block); poll_block(); if (should_service_stop()) { exit_args.exiting = true; } } bridge_exit(exit_args.cleanup); for (size_t i = 0; i < exit_args.n_conns; i++) { unixctl_command_reply(exit_args.conns[i], NULL); } free(exit_args.conns); unixctl_server_destroy(unixctl); service_stop(); vlog_disable_async(); ovsrcu_exit(); dns_resolve_destroy(); return 0; } static char * parse_options(int argc, char *argv[], char **unixctl_pathp) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, OPT_MLOCKALL, OPT_UNIXCTL, VLOG_OPTION_ENUMS, OPT_BOOTSTRAP_CA_CERT, OPT_ENABLE_DUMMY, OPT_DISABLE_SYSTEM, OPT_DISABLE_SYSTEM_ROUTE, DAEMON_OPTION_ENUMS, OPT_DPDK, SSL_OPTION_ENUMS, OPT_DUMMY_NUMA, OPT_HW_RAWIO_ACCESS, }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"mlockall", no_argument, NULL, OPT_MLOCKALL}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY}, {"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM}, {"disable-system-route", no_argument, NULL, OPT_DISABLE_SYSTEM_ROUTE}, {"dpdk", optional_argument, NULL, OPT_DPDK}, {"dummy-numa", required_argument, NULL, OPT_DUMMY_NUMA}, {"hw-rawio-access", no_argument, NULL, OPT_HW_RAWIO_ACCESS}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(0, 0); print_dpdk_version(); exit(EXIT_SUCCESS); case OPT_MLOCKALL: want_mlockall = true; break; case OPT_UNIXCTL: *unixctl_pathp = optarg; break; VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case OPT_ENABLE_DUMMY: dummy_enable(optarg); break; case OPT_DISABLE_SYSTEM: dp_disallow_provider("system"); break; case OPT_DISABLE_SYSTEM_ROUTE: ovs_router_disable_system_routing_table(); break; case '?': exit(EXIT_FAILURE); case OPT_DPDK: ovs_fatal(0, "Using --dpdk to configure DPDK is not supported."); break; case OPT_DUMMY_NUMA: ovs_numa_set_dummy(optarg); break; case OPT_HW_RAWIO_ACCESS: hw_rawio_access = true; break; default: abort(); } } free(short_options); argc -= optind; argv += optind; switch (argc) { case 0: return xasprintf("unix:%s/db.sock", ovs_rundir()); case 1: return xstrdup(argv[0]); default: VLOG_FATAL("at most one non-option argument accepted; " "use --help for usage"); } } static void usage(void) { printf("%s: Open vSwitch daemon\n" "usage: %s [OPTIONS] [DATABASE]\n" "where DATABASE is a socket on which ovsdb-server is listening\n" " (default: \"unix:%s/db.sock\").\n", program_name, program_name, ovs_rundir()); stream_usage("DATABASE", true, false, true); daemon_usage(); vlog_usage(); printf("\nDPDK options:\n" "Configuration of DPDK via command-line is removed from this\n" "version of Open vSwitch. DPDK is configured through ovsdb.\n" ); printf("\nOther options:\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ovs_vswitchd_exit(struct unixctl_conn *conn, int argc, const char *argv[], void *args OVS_UNUSED) { exit_args.n_conns++; exit_args.conns = xrealloc(exit_args.conns, exit_args.n_conns * sizeof *exit_args.conns); exit_args.conns[exit_args.n_conns - 1] = conn; exit_args.exiting = true; if (!exit_args.cleanup) { exit_args.cleanup = argc == 2 && !strcmp(argv[1], "--cleanup"); } } openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/system-stats.c000066400000000000000000000253111514270232600234410ustar00rootroot00000000000000/* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc. * * 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. */ #include #include "system-stats.h" #include #include #include #if HAVE_MNTENT_H #include #endif #include #include #include #if HAVE_SYS_STATVFS_H #include #endif #include #include "daemon.h" #include "dirs.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/json.h" #include "latch.h" #include "openvswitch/ofpbuf.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "openvswitch/poll-loop.h" #include "openvswitch/shash.h" #include "process.h" #include "smap.h" #include "timeval.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(system_stats); /* #ifdefs make it a pain to maintain code: you have to try to build both ways. * Thus, this file tries to compile as much of the code as possible regardless * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where * this is possible. */ #ifdef __linux__ #define LINUX 1 #include #else #define LINUX 0 #endif static void get_cpu_cores(struct smap *stats) { long int n_cores = count_cpu_cores(); if (n_cores > 0) { smap_add_format(stats, "cpu", "%ld", n_cores); } } static void get_load_average(struct smap *stats OVS_UNUSED) { #if HAVE_GETLOADAVG double loadavg[3]; if (getloadavg(loadavg, 3) == 3) { smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f", loadavg[0], loadavg[1], loadavg[2]); } #endif } static void get_memory_stats(struct smap *stats) { if (!LINUX) { unsigned int pagesize = get_page_size(); #ifdef _SC_PHYS_PAGES long int phys_pages = sysconf(_SC_PHYS_PAGES); #else long int phys_pages = 0; #endif #ifdef _SC_AVPHYS_PAGES long int avphys_pages = sysconf(_SC_AVPHYS_PAGES); #else long int avphys_pages = 0; #endif int mem_total, mem_used; #ifndef _WIN32 if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) { return; } mem_total = phys_pages * (pagesize / 1024); mem_used = (phys_pages - avphys_pages) * (pagesize / 1024); #else MEMORYSTATUS memory_status; GlobalMemoryStatus(&memory_status); mem_total = memory_status.dwTotalPhys; mem_used = memory_status.dwTotalPhys - memory_status.dwAvailPhys; #endif smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used); } else { static const char file_name[] = "/proc/meminfo"; int mem_used, mem_cache, swap_used; int mem_free = 0; int buffers = 0; int cached = 0; int swap_free = 0; int mem_total = 0; int swap_total = 0; struct shash dict; char line[128]; FILE *stream; stream = fopen(file_name, "r"); if (!stream) { VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); return; } shash_init(&dict); shash_add(&dict, "MemTotal", &mem_total); shash_add(&dict, "MemFree", &mem_free); shash_add(&dict, "Buffers", &buffers); shash_add(&dict, "Cached", &cached); shash_add(&dict, "SwapTotal", &swap_total); shash_add(&dict, "SwapFree", &swap_free); while (fgets(line, sizeof line, stream)) { char key[16]; int value; if (ovs_scan(line, "%15[^:]: %u", key, &value)) { int *valuep = shash_find_data(&dict, key); if (valuep) { *valuep = value; } } } fclose(stream); shash_destroy(&dict); mem_used = mem_total - mem_free; mem_cache = buffers + cached; swap_used = swap_total - swap_free; smap_add_format(stats, "memory", "%d,%d,%d,%d,%d", mem_total, mem_used, mem_cache, swap_total, swap_used); } } static void get_process_stats(struct smap *stats) { #ifndef _WIN32 struct dirent *de; DIR *dir; dir = opendir(ovs_rundir()); if (!dir) { VLOG_ERR_ONCE("%s: open failed (%s)", ovs_rundir(), ovs_strerror(errno)); return; } while ((de = readdir(dir)) != NULL) { struct process_info pinfo; char *file_name; char *extension; char *key; pid_t pid; #ifdef _DIRENT_HAVE_D_TYPE if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) { continue; } #endif extension = strrchr(de->d_name, '.'); if (!extension || strcmp(extension, ".pid")) { continue; } file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name); pid = read_pidfile(file_name); free(file_name); if (pid < 0) { continue; } key = xasprintf("process_%.*s", (int) (extension - de->d_name), de->d_name); if (!smap_get(stats, key)) { if (LINUX && get_process_info(pid, &pinfo)) { smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld,%d", pinfo.vsz, pinfo.rss, pinfo.cputime, pinfo.crashes, pinfo.booted, pinfo.uptime, pinfo.core_id); } else { smap_add(stats, key, ""); } } free(key); } closedir(dir); #endif /* _WIN32 */ } static void get_filesys_stats(struct smap *stats OVS_UNUSED) { #if HAVE_GETMNTENT_R && HAVE_STATVFS static const char file_name[] = "/etc/mtab"; struct mntent mntent; struct mntent *me; char buf[4096]; FILE *stream; struct ds s; stream = setmntent(file_name, "r"); if (!stream) { VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); return; } ds_init(&s); while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) { unsigned long long int total, free; struct statvfs vfs; char *p; /* Skip non-local and read-only filesystems. */ if (strncmp(me->mnt_fsname, "/dev", 4) || !strstr(me->mnt_opts, "rw")) { continue; } /* Given the mount point we can stat the file system. */ if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) { /* That's odd... */ continue; } /* Now format the data. */ if (s.length) { ds_put_char(&s, ' '); } for (p = me->mnt_dir; *p != '\0'; p++) { ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p); } total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024; free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024; ds_put_format(&s, ",%llu,%llu", total, total - free); } endmntent(stream); if (s.length) { smap_add(stats, "file_systems", ds_cstr(&s)); } ds_destroy(&s); #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */ } #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */ static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static struct latch latch OVS_GUARDED_BY(mutex); static bool enabled; static bool started OVS_GUARDED_BY(mutex); static struct smap *system_stats OVS_GUARDED_BY(mutex); OVS_NO_RETURN static void *system_stats_thread_func(void *); static void discard_stats(void); /* Enables or disables system stats collection, according to 'enable'. */ void system_stats_enable(bool enable) { if (enabled != enable) { ovs_mutex_lock(&mutex); if (enable) { if (!started) { ovs_thread_create("system_stats", system_stats_thread_func, NULL); latch_init(&latch); started = true; } discard_stats(); xpthread_cond_signal(&cond); } enabled = enable; ovs_mutex_unlock(&mutex); } } /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL * milliseconds. * * When a new snapshot is available (which only occurs if system stats are * enabled), returns it as an smap owned by the caller. The caller must use * both smap_destroy() and free() to completely free the returned data. * * When no new snapshot is available, returns NULL. */ struct smap * system_stats_run(void) { struct smap *stats = NULL; ovs_mutex_lock(&mutex); if (system_stats) { latch_poll(&latch); if (enabled) { stats = system_stats; system_stats = NULL; } else { discard_stats(); } } ovs_mutex_unlock(&mutex); return stats; } /* Causes poll_block() to wake up when system_stats_run() needs to be * called. */ void system_stats_wait(void) { if (enabled) { latch_wait(&latch); } } static void discard_stats(void) OVS_REQUIRES(mutex) { if (system_stats) { smap_destroy(system_stats); free(system_stats); system_stats = NULL; } } static void * system_stats_thread_func(void *arg OVS_UNUSED) { pthread_detach(pthread_self()); for (;;) { long long int next_refresh; struct smap *stats; ovs_mutex_lock(&mutex); while (!enabled) { /* The thread is sleeping, potentially for a long time, and it's * not holding RCU protected references, so it makes sense to * quiesce */ ovsrcu_quiesce_start(); ovs_mutex_cond_wait(&cond, &mutex); ovsrcu_quiesce_end(); } ovs_mutex_unlock(&mutex); stats = xmalloc(sizeof *stats); smap_init(stats); get_cpu_cores(stats); get_load_average(stats); get_memory_stats(stats); get_process_stats(stats); get_filesys_stats(stats); ovs_mutex_lock(&mutex); discard_stats(); system_stats = stats; latch_set(&latch); ovs_mutex_unlock(&mutex); next_refresh = time_msec() + SYSTEM_STATS_INTERVAL; do { poll_timer_wait_until(next_refresh); poll_block(); } while (time_msec() < next_refresh); } } openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/system-stats.h000066400000000000000000000015021514270232600234420ustar00rootroot00000000000000/* Copyright (c) 2010, 2012 Nicira, Inc. * * 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 VSWITCHD_SYSTEM_STATS #define VSWITCHD_SYSTEM_STATS 1 #include void system_stats_enable(bool enable); struct smap *system_stats_run(void); void system_stats_wait(void); #endif /* vswitchd/system-stats.h */ openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/vswitch.ovsschema000066400000000000000000000663731514270232600242330ustar00rootroot00000000000000{"name": "Open_vSwitch", "version": "8.8.0", "cksum": "2823623553 27869", "tables": { "Open_vSwitch": { "columns": { "datapaths": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "Datapath"}, "min": 0, "max": "unlimited"}}, "bridges": { "type": {"key": {"type": "uuid", "refTable": "Bridge"}, "min": 0, "max": "unlimited"}}, "manager_options": { "type": {"key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}, "ssl": { "type": {"key": {"type": "uuid", "refTable": "SSL"}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "next_cfg": { "type": "integer"}, "cur_cfg": { "type": "integer"}, "statistics": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "ovs_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "db_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "system_type": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "system_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "datapath_types": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "iface_types": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "dpdk_initialized": { "type": "boolean"}, "dpdk_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}}, "isRoot": true, "maxRows": 1}, "Bridge": { "columns": { "name": { "type": "string", "mutable": false}, "datapath_type": { "type": "string"}, "datapath_version": { "type": "string"}, "datapath_id": { "type": {"key": "string", "min": 0, "max": 1}, "ephemeral": true}, "stp_enable": { "type": "boolean"}, "rstp_enable": { "type": "boolean"}, "mcast_snooping_enable": { "type": "boolean"}, "ports": { "type": {"key": {"type": "uuid", "refTable": "Port"}, "min": 0, "max": "unlimited"}}, "mirrors": { "type": {"key": {"type": "uuid", "refTable": "Mirror"}, "min": 0, "max": "unlimited"}}, "netflow": { "type": {"key": {"type": "uuid", "refTable": "NetFlow"}, "min": 0, "max": 1}}, "sflow": { "type": {"key": {"type": "uuid", "refTable": "sFlow"}, "min": 0, "max": 1}}, "ipfix": { "type": {"key": {"type": "uuid", "refTable": "IPFIX"}, "min": 0, "max": 1}}, "controller": { "type": {"key": {"type": "uuid", "refTable": "Controller"}, "min": 0, "max": "unlimited"}}, "protocols": { "type": {"key": {"type": "string", "enum": ["set", ["OpenFlow10", "OpenFlow11", "OpenFlow12", "OpenFlow13", "OpenFlow14", "OpenFlow15"]]}, "min": 0, "max": "unlimited"}}, "fail_mode": { "type": {"key": {"type": "string", "enum": ["set", ["standalone", "secure"]]}, "min": 0, "max": 1}}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "flood_vlans": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "flow_tables": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 254}, "value": {"type": "uuid", "refTable": "Flow_Table"}, "min": 0, "max": "unlimited"}}, "auto_attach": { "type": {"key": {"type": "uuid", "refTable": "AutoAttach"}, "min": 0, "max": 1}}}, "indexes": [["name"]]}, "Port": { "columns": { "name": { "type": "string", "mutable": false}, "interfaces": { "type": {"key": {"type": "uuid", "refTable": "Interface"}, "min": 1, "max": "unlimited"}}, "trunks": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "cvlans": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "tag": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 1}}, "vlan_mode": { "type": {"key": {"type": "string", "enum": ["set", ["trunk", "access", "native-tagged", "native-untagged", "dot1q-tunnel"]]}, "min": 0, "max": 1}}, "qos": { "type": {"key": {"type": "uuid", "refTable": "QoS"}, "min": 0, "max": 1}}, "mac": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "bond_mode": { "type": {"key": {"type": "string", "enum": ["set", ["balance-tcp", "balance-slb", "active-backup"]]}, "min": 0, "max": 1}}, "lacp": { "type": {"key": {"type": "string", "enum": ["set", ["active", "passive", "off"]]}, "min": 0, "max": 1}}, "bond_updelay": { "type": "integer"}, "bond_downdelay": { "type": "integer"}, "bond_active_slave": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "bond_fake_iface": { "type": "boolean"}, "fake_bridge": { "type": "boolean"}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "protected": { "type": "boolean"}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["name"]]}, "Interface": { "columns": { "name": { "type": "string", "mutable": false}, "type": { "type": "string"}, "options": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ingress_policing_rate": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "ingress_policing_burst": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "ingress_policing_kpkts_rate": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "ingress_policing_kpkts_burst": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "mac_in_use": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}, "ephemeral": true}, "mac": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "ifindex": { "type": { "key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}, "ephemeral": true}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ofport": { "type": {"key": "integer", "min": 0, "max": 1}}, "ofport_request": { "type": { "key": {"type": "integer", "minInteger": 1, "maxInteger": 65279}, "min": 0, "max": 1}}, "bfd": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "cfm_mpid": { "type": { "key": {"type": "integer"}, "min": 0, "max": 1}}, "cfm_remote_mpids": { "type": { "key": {"type": "integer"}, "min": 0, "max": "unlimited"}, "ephemeral": true}, "cfm_flap_count": { "type": { "key": {"type": "integer"}, "min": 0, "max": 1}}, "cfm_fault": { "type": { "key": { "type": "boolean"}, "min": 0, "max": 1}, "ephemeral": true}, "cfm_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "cfm_remote_opstate": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "cfm_health": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 100}, "min": 0, "max": 1}, "ephemeral": true}, "lacp_current": { "type": {"key": {"type": "boolean"}, "min": 0, "max": 1}, "ephemeral": true}, "lldp": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "admin_state": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "link_state": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "link_resets": { "type": {"key": {"type": "integer"}, "min": 0, "max": 1}, "ephemeral": true}, "link_speed": { "type": {"key": "integer", "min": 0, "max": 1}, "ephemeral": true}, "duplex": { "type": {"key": {"type": "string", "enum": ["set", ["half", "full"]]}, "min": 0, "max": 1}, "ephemeral": true}, "mtu": { "type": {"key": "integer", "min": 0, "max": 1}, "ephemeral": true}, "mtu_request": { "type": { "key": {"type": "integer", "minInteger": 1}, "min": 0, "max": 1}}, "error": { "type": {"key": "string", "min": 0, "max": 1}}}, "indexes": [["name"]]}, "Flow_Table": { "columns": { "name": { "type": {"key": "string", "min": 0, "max": 1}}, "flow_limit": { "type": {"key": {"type": "integer", "minInteger": 0}, "min": 0, "max": 1}}, "overflow_policy": { "type": {"key": {"type": "string", "enum": ["set", ["refuse", "evict"]]}, "min": 0, "max": 1}}, "groups": { "type": {"key": "string", "min": 0, "max": "unlimited"}}, "prefixes": { "type": {"key": "string", "min": 0, "max": 4}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "QoS": { "columns": { "type": { "type": "string"}, "queues": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "value": {"type": "uuid", "refTable": "Queue"}, "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Queue": { "columns": { "dscp": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 63}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Mirror": { "columns": { "name": { "type": "string"}, "select_all": { "type": "boolean"}, "select_src_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "select_dst_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "select_vlan": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "output_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": 1}}, "output_vlan": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4095}, "min": 0, "max": 1}}, "snaplen": { "type": {"key": {"type": "integer", "minInteger": 14, "maxInteger": 65535}, "min": 0, "max": 1}}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "filter": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "NetFlow": { "columns": { "targets": { "type": {"key": {"type": "string"}, "min": 1, "max": "unlimited"}}, "engine_type": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 255}, "min": 0, "max": 1}}, "engine_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 255}, "min": 0, "max": 1}}, "add_id_to_interface": { "type": "boolean"}, "active_timeout": { "type": {"key": {"type": "integer", "minInteger": -1}}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "sFlow": { "columns": { "targets": { "type": {"key": "string", "min": 1, "max": "unlimited"}}, "sampling": { "type": {"key": "integer", "min": 0, "max": 1}}, "polling": { "type": {"key": "integer", "min": 0, "max": 1}}, "header": { "type": {"key": "integer", "min": 0, "max": 1}}, "agent": { "type": {"key": "string", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "IPFIX": { "columns": { "targets": { "type": {"key": "string", "min": 0, "max": "unlimited"}}, "sampling": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "obs_domain_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "obs_point_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "cache_active_timeout": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4200}, "min": 0, "max": 1}}, "cache_max_flows": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "stats_interval": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 3600}, "min": 0, "max": 1}}, "template_interval": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 3600}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "Flow_Sample_Collector_Set": { "columns": { "id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 1, "max": 1}}, "bridge": { "type": {"key": {"type": "uuid", "refTable": "Bridge"}, "min": 1, "max": 1}}, "ipfix": { "type": {"key": {"type": "uuid", "refTable": "IPFIX"}, "min": 0, "max": 1}}, "local_group_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true, "indexes": [["id", "bridge"]]}, "Controller": { "columns": { "type": { "type": {"key": {"type": "string", "enum": ["set", ["primary", "service"]]}, "min": 0, "max": 1}}, "target": { "type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "connection_mode": { "type": {"key": {"type": "string", "enum": ["set", ["in-band", "out-of-band"]]}, "min": 0, "max": 1}}, "local_ip": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "local_netmask": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "local_gateway": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "enable_async_messages": { "type": {"key": {"type": "boolean"}, "min": 0, "max": 1}}, "controller_queue_size": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 512}, "min": 0, "max": 1}}, "controller_rate_limit": { "type": {"key": {"type": "integer", "minInteger": 100}, "min": 0, "max": 1}}, "controller_burst_limit": { "type": {"key": {"type": "integer", "minInteger": 25}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "role": { "type": {"key": {"type": "string", "enum": ["set", ["other", "master", "slave"]]}, "min": 0, "max": 1}, "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Manager": { "columns": { "target": { "type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "connection_mode": { "type": {"key": {"type": "string", "enum": ["set", ["in-band", "out-of-band"]]}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["target"]]}, "Datapath": { "columns": { "datapath_version": { "type": "string"}, "ct_zones": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 65535}, "value": {"type": "uuid", "refTable": "CT_Zone"}, "min": 0, "max": "unlimited"}}, "capabilities": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ct_zone_default_limit": { "type": { "key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "CT_Zone": { "columns": { "timeout_policy": { "type": {"key": {"type": "uuid", "refTable": "CT_Timeout_Policy"}, "min": 0, "max": 1}}, "limit": { "type": { "key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "CT_Timeout_Policy": { "columns": { "timeouts": { "type": {"key": {"type" : "string", "enum": ["set", ["tcp_syn_sent", "tcp_syn_recv", "tcp_established", "tcp_fin_wait", "tcp_close_wait", "tcp_last_ack", "tcp_time_wait", "tcp_close", "tcp_syn_sent2", "tcp_retransmit", "tcp_unack", "udp_first", "udp_single", "udp_multiple", "icmp_first", "icmp_reply"]]}, "value": {"type" : "integer", "minInteger" : 0, "maxInteger" : 4294967295}, "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "SSL": { "columns": { "private_key": { "type": "string"}, "certificate": { "type": "string"}, "ca_cert": { "type": "string"}, "bootstrap_ca_cert": { "type": "boolean"}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "maxRows": 1}, "AutoAttach": { "columns": { "system_name": { "type": "string"}, "system_description": { "type": "string"}, "mappings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 16777215}, "value": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": "unlimited"}}}}}} openvswitch-3.7.0~git20260211.8c6ebf8/vswitchd/vswitch.xml000066400000000000000000011205331514270232600230310ustar00rootroot00000000000000

    A database with this schema holds the configuration for one Open vSwitch daemon. The top-level configuration for the daemon is the table, which must have exactly one record. Records in other tables are significant only when they can be reached directly or indirectly from the table. Records that are not reachable from the table are automatically deleted from the database, except for records in a few distinguished ``root set'' tables.

    Common Columns

    Most tables contain two special columns, named other_config and external_ids. These columns have the same form and purpose each place that they appear, so we describe them here to save space later.

    other_config: map of string-string pairs

    Key-value pairs for configuring rarely used features. Supported keys, along with the forms taken by their values, are documented individually for each table.

    A few tables do not have other_config columns because no key-value pairs have yet been defined for them.

    external_ids: map of string-string pairs
    Key-value pairs for use by external frameworks that integrate with Open vSwitch, rather than by Open vSwitch itself. System integrators should either use the Open vSwitch development mailing list to coordinate on common key-value definitions, or choose key names that are likely to be unique. In some cases, where key-value pairs have been defined that are likely to be widely useful, they are documented individually for each table.
    Configuration for an Open vSwitch daemon. There must be exactly one record in the table. Map of datapath types to datapaths. The column of the table is used as a key for this map. The value points to a row in the table. Set of bridges managed by the daemon. SSL/TLS used globally by the daemon. A unique identifier for the Open vSwitch's physical host. The form of the identifier depends on the type of the host. The hostname for the host running Open vSwitch. This is a fully qualified domain name since version 2.6.2. In Open vSwitch 2.8 and later, the run directory of the running Open vSwitch daemon. This directory is used for runtime state such as control and management sockets. The value of is relative to this directory.

    Interval for updating statistics to the database, in milliseconds. This option will affect the update of the statistics column in the following tables: Port, Interface , Mirror.

    Default value is 5000 ms.

    Getting statistics more frequently can be achieved via OpenFlow.

    When ovs-vswitchd starts up, it has an empty flow table and therefore it handles all arriving packets in its default fashion according to its configuration, by dropping them or sending them to an OpenFlow controller or switching them as a standalone switch. This behavior is ordinarily desirable. However, if ovs-vswitchd is restarting as part of a ``hot-upgrade,'' then this leads to a relatively long period during which packets are mishandled.

    This option allows for improvement. When ovs-vswitchd starts with this value set as true, it will neither flush or expire previously set datapath flows nor will it send and receive any packets to or from the datapath. When this value is later set to false, ovs-vswitchd will start receiving packets from the datapath and re-setup the flows.

    Additionally, ovs-vswitchd is prevented from connecting to controllers when this value is set to true. This prevents controllers from making changes to the flow table in the middle of flow restoration, which could result in undesirable intermediate states. Once this value has been set to false and the desired flow state has been restored, ovs-vswitchd will be able to reconnect to controllers and process any new flow table modifications.

    Thus, with this option, the procedure for a hot-upgrade of ovs-vswitchd becomes roughly the following:

    1. Stop ovs-vswitchd.
    2. Set to true.
    3. Start ovs-vswitchd.
    4. Use ovs-ofctl (or some other program, such as an OpenFlow controller) to restore the OpenFlow flow table to the desired state.
    5. Set to false (or remove it entirely from the database).

    The ovs-ctl's ``restart'' and ``force-reload-kmod'' functions use the above config option during hot upgrades.

    The maximum number of flows allowed in the datapath flow table. Internally OVS will choose a flow limit which will likely be lower than this number, based on real time network conditions. Tweaking this value is discouraged unless you know exactly what you're doing.

    The default is 200000.

    The maximum time (in ms) that idle flows will remain cached in the datapath. Internally OVS will check the validity and activity for datapath flows regularly and may expire flows quicker than this number, based on real time network conditions. Tweaking this value is discouraged unless you know exactly what you're doing.

    The default is 10000.

    The maximum time (in ms) that revalidator threads will wait before executing flow revalidation. Note that this is maximum allowed value. Actual timeout used by OVS is minimum of max-idle and max-revalidator values. Tweaking this value is discouraged unless you know exactly what you're doing.

    The default is 500.

    Set minimum pps that flow must have in order to be revalidated when revalidation duration exceeds half of max-revalidator config variable. Setting to 0 means always revalidate flows regardless of pps.

    The default is 5.

    Set worst case delay (in ms) it might take before statistics of offloaded flows are updated. Offloaded flows younger than this delay will always be revalidated regardless of .

    The default is 2000.

    Set this value to true to enable netdev flow offload.

    The default value is false. Changing this value requires restarting the daemon

    Currently Open vSwitch supports hardware offloading on Linux systems. On other systems, this value is ignored. This functionality is considered 'experimental'. Depending on which OpenFlow matches and actions are configured, which kernel version is used, and what hardware is available, Open vSwitch may not be able to offload functionality to hardware.

    In order to dump HW offloaded flows use ovs-appctl dpctl/dump-flows, ovs-dpctl doesn't support this functionality. See ovs-vswitchd(8) for details.

    This configuration sets the order of the hardware offload providers to try when multiple exist for a given datapath implementation. The argument provided should be a list of comma-separated hardware offload provider names.

    If implementations are missing from the list, they are added at the end in undefined order.

    The default value is tc,dpdk. Changing this value requires restarting the daemon if hardware offload is already enabled.

    Set this value to the number of threads created to manage hardware offloads.

    The default value is 1. Changing this value requires restarting the daemon.

    This is only relevant for userspace datapath and only if is enabled.

    Specified the policy used with HW offloading. Options:

    none
    Add software rule and offload rule to HW.
    skip_sw
    Offload rule to HW only.
    skip_hw
    Add software rule without offloading rule to HW.

    This is only relevant if is enabled.

    The default value is none.

    Set this value to true or try to enable runtime support for DPDK ports. The vswitch must have compile-time support for DPDK as well.

    A value of true will cause the ovs-vswitchd process to abort if DPDK cannot be initialized. A value of try will allow the ovs-vswitchd process to continue running even if DPDK cannot be initialized.

    The default value is false. Changing this value requires restarting the daemon

    If this value is false at startup, any dpdk ports which are configured in the bridge will fail due to memory errors.

    Specifies the CPU cores where dpdk lcore threads should be spawned. The DPDK lcore threads are used for DPDK library tasks, such as library internal message processing, logging, etc. Value should be in the form of a hex string (so '0x123') similar to the 'taskset' mask input.

    The lowest order bit corresponds to the first CPU core. A set bit means the corresponding core is available and an lcore thread will be created and pinned to it. If the input does not cover all cores, those uncovered cores are considered not set.

    The lowest set core will be assigned in DPDK as the DPDK main thread. Other set bits will be assigned by DPDK as DPDK worker threads. As OVS implements its own threading for the datapath (see ), DPDK worker threads will be unused.

    If this option is specified, all OVS main, handler, and revalidator threads will be pinned to the selected DPDK main thread core. This means that potentially many threads will share a single CPU core, which may not be desirable for performance reasons.

    If this option is not specified the value for DPDK initialization will be auto-determined and all OVS main, revalidator and handler threads will float on the OVS cpu affinity list.

    Specifies CPU mask for setting the cpu affinity of PMD (Poll Mode Driver) threads. Value should be in the form of hex string, similar to the dpdk EAL '-c COREMASK' option input or the 'taskset' mask input.

    The lowest order bit corresponds to the first CPU core. A set bit means the corresponding core is available and a pmd thread will be created and pinned to it. If the input does not cover all cores, those uncovered cores are considered not set.

    If not specified, one pmd thread will be created for each numa node and pinned to any available core on the numa node by default.

    Specifies the amount of memory to preallocate from the hugepage pool, regardless of socket. It is recommended that dpdk-socket-mem is used instead.

    Specifies the amount of memory to preallocate from the hugepage pool, on a per-socket basis.

    The specifier is a comma-separated string, in ascending order of CPU socket. E.g. On a four socket system 1024,0,2048 would set socket 0 to preallocate 1024MB, socket 1 to preallocate 0MB, socket 2 to preallocate 2048MB and socket 3 (no value given) to preallocate 0MB.

    If and are not specified, neither will be used and there will be no default value for each numa node. DPDK defaults will be used instead. If and are specified at the same time, will be used as default. Changing this value requires restarting the daemon.

    Limits the maximum amount of memory that can be used from the hugepage pool, on a per-socket basis.

    The specifier is a comma-separated list of memory limits per socket. 0 will disable the limit for a particular socket.

    If not specified, OVS will not configure limits by default. Changing this value requires restarting the daemon.

    Specifies the path to the hugetlbfs mount point.

    If not specified, this will be guessed by the DPDK library (default is /dev/hugepages). Changing this value requires restarting the daemon.

    Specifies additional eal command line arguments for DPDK.

    The default is empty. Changing this value requires restarting the daemon

    Specifies a relative path from to the vhost-user unix domain socket files. If this value is unset, the sockets are put directly in .

    Changing this value requires restarting the daemon.

    vHost IOMMU is a security feature, which restricts the vhost memory that a virtio device may access. vHost IOMMU support is disabled by default, due to a bug in QEMU implementations of the vhost REPLY_ACK protocol, (on which vHost IOMMU relies) prior to v2.9.1. Setting this value to true enables vHost IOMMU support for vHost User Client ports in OvS-DPDK, starting from DPDK v17.11.

    Changing this value requires restarting the daemon.

    vHost post-copy is a feature which allows switching live migration of VM attached to dpdkvhostuserclient port to post-copy mode if default pre-copy migration can not be converged or takes too long to converge. Setting this value to true enables vHost post-copy support for all dpdkvhostuserclient ports. Available starting from DPDK v18.11 and QEMU 2.12.

    Changing this value requires restarting the daemon.

    By default OVS DPDK uses a shared memory model wherein devices that have the same MTU and socket values can share the same mempool. Setting this value to true changes this behaviour. Per port memory allow DPDK devices to use private memory per device. This can provide greater transparency as regards memory usage but potentially at the cost of greater memory requirements.

    Changing this value requires restarting the daemon if dpdk-init has already been set to true.

    Specifies dpdk shared mempool config.

    Value should be set in the following form:

    other_config:shared-mempool-config=< user-shared-mempool-mtu-list>

    where

    • <user-shared-mempool-mtu-list> ::= NULL | <non-empty-list>
    • <non-empty-list> ::= <user-mtus> | <user-mtus> , <non-empty-list>
    • <user-mtus> ::= <mtu-all-socket> | <mtu-socket-pair>
    • <mtu-all-socket> ::= <mtu>
    • <mtu-socket-pair> ::= <mtu> : <socket-id>

    Changing this value requires restarting the daemon if dpdk-init has already been set to true.

    Specifies the time in microseconds that a packet can wait in output batch for sending i.e. amount of time that packet can spend in an intermediate output queue before sending to netdev. This option can be used to configure balance between throughput and latency. Lower values decreases latency while higher values may be useful to achieve higher performance.

    Defaults to 0 i.e. instant packet sending (latency optimized).

    Enables recording of detailed PMD performance metrics for analysis and trouble-shooting. This can have a performance impact in the order of 1%.

    Defaults to false but can be changed at any time.

    Signature match cache or SMC is a cache between EMC and megaflow cache. It does not store the full key of the flow, so it is more memory efficient comparing to EMC cache. SMC is especially useful when flow count is larger than EMC capacity.

    Defaults to false but can be changed at any time.

    Specifies how RX queues will be automatically assigned to CPU cores. Options:

    cycles
    Rxqs will be sorted by order of measured processing cycles before being assigned to CPU cores.
    roundrobin
    Rxqs will be round-robined across CPU cores.
    group
    Rxqs will be sorted by order of measured processing cycles before being assigned to CPU cores with lowest estimated load.

    The default value is cycles.

    Changing this value will affect an automatic re-assignment of Rxqs to CPUs. Note: Rxqs mapped to CPU cores with pmd-rxq-affinity are unaffected.

    Specifies if a CPU core will be isolated after being pinned with an Rx queue.

    Set this value to false to non-isolate a CPU core after it is pinned with an Rxq using pmd-rxq-affinity. This will allow OVS to assign other Rxqs to that CPU core.

    The default value is true.

    This can only be false when pmd-rxq-assign is set to group.

    Attempts to specify the number of threads for software datapaths to use for handling new flows. Some datapaths may choose to ignore this and it will be set to a sensible option for the datapath type.

    This configuration is per datapath. If you have more than one software datapath (e.g. some system bridges and some netdev bridges), then the total number of threads is n-handler-threads times the number of software datapaths.

    Attempts to specify the number of threads for software datapaths to use for revalidating flows in the datapath. Some datapaths may choose to ignore this and will set to a sensible option for the datapath type.

    Typically, there is a direct correlation between the number of revalidator threads, and the number of flows allowed in the datapath. The default is the number of cpu cores divided by four plus one. If n-handler-threads is set, the default changes to the number of cpu cores minus the number of handler threads.

    This configuration is per datapath. If you have more than one software datapath (e.g. some system bridges and some netdev bridges), then the total number of threads is n-handler-threads times the number of software datapaths.

    Specifies the inverse probability (1/emc-insert-inv-prob) of a flow being inserted into the Exact Match Cache (EMC). On average one in every emc-insert-inv-prob packets that generate a unique flow will cause an insertion into the EMC. A value of 1 will result in an insertion for every flow (1/1 = 100%) whereas a value of zero will result in no insertions and essentially disable the EMC.

    Defaults to 100 ie. there is (1/100 =) 1% chance of EMC insertion.

    Limits the number of VLAN headers that can be matched to the specified number. Further VLAN headers will be treated as payload, e.g. a packet with more 802.1q headers will match Ethernet type 0x8100.

    Open vSwitch userspace currently supports at most 2 VLANs, and each datapath has its own limit. If vlan-limit is nonzero, it acts as a further limit.

    If this value is absent, the default is currently 1. This maintains backward compatibility with controllers that were designed for use with Open vSwitch versions earlier than 2.8, which only supported one VLAN.

    The maximum time (in seconds) that idle bundles will wait to be expired since it was either opened, modified or closed.

    OpenFlow specification mandates the timeout to be at least one second. The default is 10 seconds.

    Configures HW offload rebalancing, that allows to dynamically offload and un-offload flows while an offload-device is out of resources (OOR). This policy allows flows to be selected for offloading based on the packets-per-second (pps) rate of flows.

    Set this value to true to enable this option.

    The default value is false. Changing this value requires restarting the daemon.

    This is only relevant if HW offloading is enabled (hw-offload). When this policy is enabled, it also requires 'tc-policy' to be set to 'skip_sw'.

    Configures PMD Auto Load Balancing that allows automatic assignment of RX queues to PMDs if any of PMDs is overloaded (i.e. a processing cycles > ).

    It uses current scheme of cycle based assignment of RX queues that are not statically pinned to PMDs.

    The default value is false.

    Set this value to true to enable this option. It is currently disabled by default and an experimental feature.

    This only comes in effect if cycle based assignment is enabled and there are more than one non-isolated PMDs present and at least one of it polls more than one queue.

    The minimum time (in minutes) 2 consecutive PMD Auto Load Balancing iterations.

    The default value is 1 min. If configured to 0 then it would be converted to default value i.e. 1 min

    This option can be configured to avoid frequent trigger of auto load balancing of PMDs. For e.g. set the value (in min) such that it occurs once in few hours or a day or a week.

    Specifies the minimum PMD thread load threshold (% of used cycles) of any non-isolated PMD threads when a PMD Auto Load Balance may be triggered.

    The default value is 95%.

    Specifies the minimum evaluated % improvement in load distribution across the non-isolated PMD threads that will allow a PMD Auto Load Balance to occur.

    Note, setting this parameter to 0 will always allow an auto load balance to occur regardless of estimated improvement or not.

    The default value is 25%.

    Specifies the maximum sleep time that will be requested in microseconds per iteration for a PMD thread which has received zero or a small amount of packets from the Rx queues it is polling.

    The actual sleep time requested is based on the load of the Rx queues that the PMD polls and may be less than the maximum value.

    The default value is 0 microseconds, which means that the PMD will not sleep regardless of the load from the Rx queues that it polls.

    The maximum value is 10000 microseconds.

    other_config:pmd-sleep-max=<pmd-sleep-list>

    where

    • <pmd-sleep-list> ::= NULL | <non-empty-list>
    • <non-empty-list> ::= <pmd-sleep-value> | <pmd-sleep-value> , <non-empty-list>
    • <pmd-sleep-value> ::= <global-default-sleep-value> | <pmd-core-sleep-pair>
    • <global-default-sleep-value> ::= <max-sleep-time>
    • <pmd-core-sleep-pair> ::= <core> : <max-sleep-time>

    Set this value to true to enable userspace support for TCP Segmentation Offloading (TSO). When it is enabled, the interfaces can provide an oversized TCP segment to the datapath and the datapath will offload the TCP segmentation and checksum calculation to the interfaces when necessary.

    The default value is false. Changing this value requires restarting the daemon.

    The feature only works if Open vSwitch is built with DPDK support.

    The feature is considered experimental.

    When a flow is installed in the datapath with an empty action list, it indicates an implicit "drop" action. Most datapaths report this for event for statistics and monitoring (in datapath-specific ways).

    However, if any of the per-bridge or per-flow sampling functionalities are enabled (e.g: sFlow, IPFIX, local sampling), the action list might not be empty, but contain an action to implement such functionality. This makes the datapaths not report the packet drop.

    This knob makes Open vSwitch detect when the last datapath action comes from these sampling features and add an explicit drop action at the end to keep drop statistics accurate.

    The default value is false.

    Sequence number for client to increment. When a client modifies any part of the database configuration and wishes to wait for Open vSwitch to finish applying the changes, it may increment this sequence number. Sequence number that Open vSwitch sets to the current value of after it finishes applying a set of configuration changes. True if is set to true and the DPDK library is successfully initialized.

    The statistics column contains key-value pairs that report statistics about a system running an Open vSwitch. These are updated periodically (currently, every 5 seconds). Key-value pairs that cannot be determined or that do not apply to a platform are omitted.

    Statistics are disabled by default to avoid overhead in the common case when statistics gathering is not useful. Set this value to true to enable populating the column or to false to explicitly disable it.

    Number of CPU processors, threads, or cores currently online and available to the operating system on which Open vSwitch is running, as an integer. This may be less than the number installed, if some are not online or if they are not available to the operating system.

    Open vSwitch userspace processes are not multithreaded, but the Linux kernel-based datapath is.

    A comma-separated list of three floating-point numbers, representing the system load average over the last 1, 5, and 15 minutes, respectively.

    A comma-separated list of integers, each of which represents a quantity of memory in kilobytes that describes the operating system on which Open vSwitch is running. In respective order, these values are:

    1. Total amount of RAM allocated to the OS.
    2. RAM allocated to the OS that is in use.
    3. RAM that can be flushed out to disk or otherwise discarded if that space is needed for another purpose. This number is necessarily less than or equal to the previous value.
    4. Total disk space allocated for swap.
    5. Swap space currently in use.

    On Linux, all five values can be determined and are included. On other operating systems, only the first two values can be determined, so the list will only have two values.

    One such key-value pair, with NAME replaced by a process name, will exist for each running Open vSwitch daemon process, with name replaced by the daemon's name (e.g. process_ovs-vswitchd). The value is a comma-separated list of integers. The integers represent the following, with memory measured in kilobytes and durations in milliseconds:

    1. The process's virtual memory size.
    2. The process's resident set size.
    3. The amount of user and system CPU time consumed by the process.
    4. The number of times that the process has crashed and been automatically restarted by the monitor.
    5. The duration since the process was started.
    6. The duration for which the process has been running.

    The interpretation of some of these values depends on whether the process was started with the . If it was not, then the crash count will always be 0 and the two durations will always be the same. If was given, then the crash count may be positive; if it is, the latter duration is the amount of time since the most recent crash and restart.

    There will be one key-value pair for each file in Open vSwitch's ``run directory'' (usually /var/run/openvswitch) whose name ends in .pid, whose contents are a process ID, and which is locked by a running process. The name is taken from the pidfile's name.

    Currently Open vSwitch is only able to obtain all of the above detail on Linux systems. On other systems, the same key-value pairs will be present but the values will always be the empty string.

    A space-separated list of information on local, writable file systems. Each item in the list describes one file system and consists in turn of a comma-separated list of the following:

    1. Mount point, e.g. / or /var/log. Any spaces or commas in the mount point are replaced by underscores.
    2. Total size, in kilobytes, as an integer.
    3. Amount of storage in use, in kilobytes, as an integer.

    This key-value pair is omitted if there are no local, writable file systems or if Open vSwitch cannot obtain the needed information.

    These columns report the types and versions of the hardware and software running Open vSwitch. We recommend in general that software should test whether specific features are supported instead of relying on version number checks. These values are primarily intended for reporting to human administrators.

    The Open vSwitch version number, e.g. 1.1.0.

    The database schema version number, e.g. 1.2.3. See ovsdb-tool(1) for an explanation of the numbering scheme.

    The schema version is part of the database schema, so it can also be retrieved by fetching the schema using the Open vSwitch database protocol.

    An identifier for the type of system on top of which Open vSwitch runs, e.g. KVM.

    System integrators are responsible for choosing and setting an appropriate value for this column.

    The version of the system identified by , e.g. 4.18.0-372.19.1.el8_6 on RHEL 8.6 with kernel 4.18.0-372.19.1.

    System integrators are responsible for choosing and setting an appropriate value for this column.

    The version of the linked DPDK library.

    These columns report capabilities of the Open vSwitch instance.

    This column reports the different dpifs registered with the system. These are the values that this instance supports in the column of the table.

    This column reports the different netdevs registered with the system. These are the values that this instance supports in the column of the table.

    These columns primarily configure the Open vSwitch database (ovsdb-server), not the Open vSwitch switch (ovs-vswitchd). The OVSDB database also uses the settings.

    The Open vSwitch switch does read the database configuration to determine remote IP addresses to which in-band control should apply.

    Database clients to which the Open vSwitch database server should connect or to which it should listen, along with options for how these connections should be configured. See the table for more information.

    For this column to serve its purpose, ovsdb-server must be configured to honor it. The easiest way to do this is to invoke ovsdb-server with the option The startup scripts that accompany Open vSwitch do this by default.

    These settings control the global configuration of IPsec tunnels. The options column of the Interface table configures IPsec for individual tunnels. The options column also allows for custom options prefixed with ipsec_ to be passed to the individual connections.

    OVS IPsec supports the following three forms of authentication. Currently, all IPsec tunnels must use the same form:

    1. Pre-shared keys: Omit the global settings. On each tunnel, set .
    2. Self-signed certificates: Set the private_key and certificate global settings. On each tunnel, set . The remote certificate can be self-signed.
    3. CA-signed certificates: Set all of the global settings. On each tunnel, set to the common name (CN) of the remote certificate. The remote certificate must be signed by the CA.

    Name of a PEM file containing the private key used as the switch's identity for IPsec tunnels.

    Name of a PEM file containing a certificate that certifies the switch's private key, and identifies a trustworthy switch for IPsec tunnels. The certificate must be x.509 version 3 and with the string in common name (CN) also set in the subject alternative name (SAN).

    Name of a PEM file containing the CA certificate used to verify that a remote switch of the IPsec tunnel is trustworthy.

    When an IPsec tunnel is configured in this database, multiple independent components take responsibility for implementing it. ovs-vswitchd and its datapath handle packet forwarding to the tunnel and a separate daemon pushes the tunnel's IPsec policy configuration to the kernel or other entity that implements it. There is a race: if the former configuration completes before the latter, then packets sent by the local host over the tunnel can be transmitted in plaintext. Using this setting, OVS users can avoid this undesirable situation.

    This setting takes the form value/mask. If it is specified, then the skb_mark field in every outgoing tunneled packet sent in plaintext is compared against it and, if it matches, the packet is dropped. This is a global setting that is applied to every tunneled packet, regardless of whether IPsec encryption is enabled for the tunnel, the type of tunnel, or whether OVS is involved.

    Example policies:

    1/1
    Drop all unencrypted tunneled packets in which the least-significant bit of skb_mark is 1. This would be a useful policy given an OpenFlow flow table that sets skb_mark to 1 for traffic that should be encrypted. The default skb_mark is 0, so this would not affect other traffic.
    0/1
    Drop all unencrypted tunneled packets in which the least-significant bit of skb_mark is 0. This would be a useful policy if no unencrypted tunneled traffic should exit the system without being specially permitted by setting skb_mark to 1.
    (empty)
    If this setting is empty or unset, then all unencrypted tunneled packets are transmitted in the usual way.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a bridge within an .

    A record represents an Ethernet switch with one or more ``ports,'' which are the records pointed to by the 's column.

    Bridge identifier. Must be unique among the names of ports, interfaces, and bridges on a host.

    The name must be alphanumeric and must not contain forward or backward slashes. The name of a bridge is also the name of an (and a ) within the bridge, so the restrictions on the column in the table, particularly on length, also apply to bridge names. Refer to the documentation for names for details.

    Ports included in the bridge. Port mirroring configuration. NetFlow configuration. sFlow(R) configuration. IPFIX configuration.

    VLAN IDs of VLANs on which MAC address learning should be disabled, so that packets are flooded instead of being sent to specific ports that are believed to contain packets' destination MACs. This should ordinarily be used to disable MAC learning on VLANs used for mirroring (RSPAN VLANs). It may also be useful for debugging.

    SLB bonding (see the column in the table) is incompatible with flood_vlans. Consider using another bonding mode or a different type of mirror instead.

    Auto Attach configuration.

    OpenFlow controller set. If unset, then no OpenFlow controllers will be used.

    If there are primary controllers, removing all of them clears the OpenFlow flow tables, group table, and meter table. If there are no primary controllers, adding one also clears these tables. Other changes to the set of controllers, such as adding or removing a service controller, adding another primary controller to supplement an existing primary controller, or removing only one of two primary controllers, have no effect on these tables.

    Configuration for OpenFlow tables. Each pair maps from an OpenFlow table ID to configuration for that table.

    When a controller is configured, it is, ordinarily, responsible for setting up all flows on the switch. Thus, if the connection to the controller fails, no new network connections can be set up. If the connection to the controller stays down long enough, no packets can pass through the switch at all. This setting determines the switch's response to such a situation. It may be set to one of the following:

    standalone
    If no message is received from the controller for three times the inactivity probe interval (see ), then Open vSwitch will take over responsibility for setting up flows. In this mode, Open vSwitch causes the bridge to act like an ordinary MAC-learning switch. Open vSwitch will continue to retry connecting to the controller in the background and, when the connection succeeds, it will discontinue its standalone behavior.
    secure
    Open vSwitch will not set up flows on its own when the controller connection fails or when no controllers are defined. The bridge will continue to retry connecting to any defined controllers forever.

    The default is standalone if the value is unset, but future versions of Open vSwitch may change the default.

    The standalone mode can create forwarding loops on a bridge that has more than one uplink port unless STP is enabled. To avoid loops on such a bridge, configure secure mode or enable STP (see ).

    The setting applies only to primary controllers. When more than one primary controller is configured, is considered only when none of the configured controllers can be contacted.

    Changing when no primary controllers are configured clears the OpenFlow flow tables, group table, and meter table.

    Reports the OpenFlow datapath ID in use. Exactly 16 hex digits. (Setting this column has no useful effect. Set instead.) Reports the datapath version. This column is maintained for backwards compatibility. The preferred locatation is the column of the table. The full documentation for this column is there. Overrides the default OpenFlow datapath ID, setting it to the specified value specified in hex. The value must either have a 0x prefix or be exactly 16 hex digits long. May not be all-zero. Human readable description of datapath. It is a maximum 256 byte-long free-form string to describe the datapath for debugging purposes, e.g. switch3 in room 3120. The value is returned by the switch as a part of reply to OFPMP_DESC request (ofp_desc). The OpenFlow specification (e.g. 1.3.5) describes the ofp_desc structure to contaion "NULL terminated ASCII strings". For the compatibility reasons no more than 255 ASCII characters should be used. Serial number. It is a maximum 32 byte-long free-form string to provide an additional switch identification. The value is returned by the switch as a part of reply to OFPMP_DESC request (ofp_desc). Same as mentioned in the description of , the string should be no more than 31 ASCII characters for the compatibility. If set to true, disable in-band control on the bridge regardless of controller and manager settings. A queue ID as a nonnegative integer. This sets the OpenFlow queue ID that will be used by flows set up by in-band control on this bridge. If unset, or if the port used by an in-band control flow does not have QoS configured, or if the port does not have a queue with the specified ID, the default queue is used instead. This sets the maximum size of the queue of packets that need to be sent to the OpenFlow management controller. The value must be less than 512. If not specified the queue size is limited to 100 packets by default. Note: increasing the queue size might have a negative impact on latency. List of OpenFlow protocols that may be used when negotiating a connection with a controller. OpenFlow 1.0, 1.1, 1.2, 1.3, 1.4, and 1.5 are enabled by default if this column is empty.

    The IEEE 802.1D Spanning Tree Protocol (STP) is a network protocol that ensures loop-free topologies. It allows redundant links to be included in the network to provide automatic backup paths if the active links fails.

    These settings configure the slower-to-converge but still widely supported version of Spanning Tree Protocol, sometimes known as 802.1D-1998. Open vSwitch also supports the newer Rapid Spanning Tree Protocol (RSTP), documented later in the section titled Rapid Spanning Tree Configuration.

    Enable spanning tree on the bridge. By default, STP is disabled on bridges. Bond, internal, and mirror ports are not supported and will not participate in the spanning tree.

    STP and RSTP are mutually exclusive. If both are enabled, RSTP will be used.

    The bridge's STP identifier (the lower 48 bits of the bridge-id) in the form xx:xx:xx:xx:xx:xx. By default, the identifier is the MAC address of the bridge. The bridge's relative priority value for determining the root bridge (the upper 16 bits of the bridge-id). A bridge with the lowest bridge-id is elected the root. By default, the priority is 0x8000. The interval between transmissions of hello messages by designated ports, in seconds. By default the hello interval is 2 seconds. The maximum age of the information transmitted by the bridge when it is the root bridge, in seconds. By default, the maximum age is 20 seconds. The delay to wait between transitioning root and designated ports to forwarding, in seconds. By default, the forwarding delay is 15 seconds.

    The maximum number of seconds to retain a multicast snooping entry for which no packets have been seen. The default is currently 300 seconds (5 minutes). The value, if specified, is forced into a reasonable range, currently 15 to 3600 seconds.

    The maximum number of multicast snooping addresses to learn. The default is currently 2048. The value, if specified, is forced into a reasonable range, currently 10 to 1,000,000.

    If set to false, unregistered multicast packets are forwarded to all ports. If set to true, unregistered multicast packets are forwarded to ports connected to multicast routers.

    These key-value pairs report the status of 802.1D-1998. They are present only if STP is enabled (via the column).

    The bridge ID used in spanning tree advertisements, in the form xxxx.yyyyyyyyyyyy where the xs are the STP priority, the ys are the STP system ID, and each x and y is a hex digit. The designated root for this spanning tree, in the same form as . If this bridge is the root, this will have the same value as , otherwise it will differ. The path cost of reaching the designated bridge. A lower number is better. The value is 0 if this bridge is the root, otherwise it is higher.

    Rapid Spanning Tree Protocol (RSTP), like STP, is a network protocol that ensures loop-free topologies. RSTP superseded STP with the publication of 802.1D-2004. Compared to STP, RSTP converges more quickly and recovers more quickly from failures.

    Enable Rapid Spanning Tree on the bridge. By default, RSTP is disabled on bridges. Bond, internal, and mirror ports are not supported and will not participate in the spanning tree.

    STP and RSTP are mutually exclusive. If both are enabled, RSTP will be used.

    The bridge's RSTP address (the lower 48 bits of the bridge-id) in the form xx:xx:xx:xx:xx:xx. By default, the address is the MAC address of the bridge. The bridge's relative priority value for determining the root bridge (the upper 16 bits of the bridge-id). A bridge with the lowest bridge-id is elected the root. By default, the priority is 0x8000 (32768). This value needs to be a multiple of 4096, otherwise it's rounded to the nearest inferior one. The Ageing Time parameter for the Bridge. The default value is 300 seconds. The Force Protocol Version parameter for the Bridge. This can take the value 0 (STP Compatibility mode) or 2 (the default, normal operation). The maximum age of the information transmitted by the Bridge when it is the Root Bridge. The default value is 20. The delay used by STP Bridges to transition Root and Designated Ports to Forwarding. The default value is 15. The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate. The default value is 6.

    These key-value pairs report the status of 802.1D-2004. They are present only if RSTP is enabled (via the column).

    The bridge ID used in rapid spanning tree advertisements, in the form x.yyy.zzzzzzzzzzzz where x is the RSTP priority, the ys are a locally assigned system ID extension, the zs are the STP system ID, and each x, y, or z is a hex digit. The root of this spanning tree, in the same form as . If this bridge is the root, this will have the same value as , otherwise it will differ. The path cost of reaching the root. A lower number is better. The value is 0 if this bridge is the root, otherwise it is higher. The RSTP designated ID, in the same form as . The RSTP designated port ID, as a 4-digit hex number. The RSTP bridge port ID, as a 4-digit hex number.
    Multicast snooping (RFC 4541) monitors the Internet Group Management Protocol (IGMP) and Multicast Listener Discovery traffic between hosts and multicast routers. The switch uses what IGMP and MLD snooping learns to forward multicast traffic only to interfaces that are connected to interested receivers. Currently it supports IGMPv1, IGMPv2, IGMPv3, MLDv1 and MLDv2 protocols. Enable multicast snooping on the bridge. For now, the default is disabled. Name of datapath provider. The kernel datapath has type system. The userspace datapath has type netdev. A manager may refer to the column of the table for a list of the types accepted by this Open vSwitch instance. A unique identifier of the bridge. An Ethernet address in the form xx:xx:xx:xx:xx:xx to set the hardware address of the local port and influence the datapath ID.

    Controls forwarding of BPDUs and other network control frames when NORMAL action is invoked. When this option is false or unset, frames with reserved Ethernet addresses (see table below) will not be forwarded. When this option is true, such frames will not be treated specially.

    The above general rule has the following exceptions:

    • If STP is enabled on the bridge (see the column in the table), the bridge processes all received STP packets and never passes them to OpenFlow or forwards them. This is true even if STP is disabled on an individual port.
    • If LLDP is enabled on an interface (see the column in the table), the interface processes received LLDP packets and never passes them to OpenFlow or forwards them.

    Set this option to true if the Open vSwitch bridge connects different Ethernet networks and is not configured to participate in STP.

    This option affects packets with the following destination MAC addresses:

    01:80:c2:00:00:00
    IEEE 802.1D Spanning Tree Protocol (STP).
    01:80:c2:00:00:01
    IEEE Pause frame.
    01:80:c2:00:00:0x
    Other reserved protocols.
    00:e0:2b:00:00:00
    Extreme Discovery Protocol (EDP).
    00:e0:2b:00:00:04 and 00:e0:2b:00:00:06
    Ethernet Automatic Protection Switching (EAPS).
    01:00:0c:cc:cc:cc
    Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP), Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP), and others.
    01:00:0c:cc:cc:cd
    Cisco Shared Spanning Tree Protocol PVSTP+.
    01:00:0c:cd:cd:cd
    Cisco STP Uplink Fast.
    01:00:0c:00:00:00
    Cisco Inter Switch Link.
    01:00:0c:cc:cc:cx
    Cisco CFM.

    The maximum number of seconds to retain a MAC learning entry for which no packets have been seen. The default is currently 300 seconds (5 minutes). The value, if specified, is forced into a reasonable range, currently 15 to 3600 seconds.

    A short MAC aging time allows a network to more quickly detect that a host is no longer connected to a switch port. However, it also makes it more likely that packets will be flooded unnecessarily, when they are addressed to a connected host that rarely transmits packets. To reduce the incidence of unnecessary flooding, use a MAC aging time longer than the maximum interval at which a host will ordinarily transmit packets.

    The maximum number of MAC addresses to learn. The default is currently 8192. The value, if specified, is forced into a reasonable range, currently 10 to 1,000,000.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A port within a .

    Most commonly, a port has exactly one ``interface,'' pointed to by its column. Such a port logically corresponds to a port on a physical Ethernet switch. A port with more than one interface is a ``bonded port'' (see ).

    Some properties that one might think as belonging to a port are actually part of the port's members.

    Port name. For a non-bonded port, this should be the same as its interface's name. Port names must otherwise be unique among the names of ports, interfaces, and bridges on a host. Because port and interfaces names are usually the same, the restrictions on the column in the table, particularly on length, also apply to port names. Refer to the documentation for names for details. The port's interfaces. If there is more than one, this is a bonded Port.

    In short, a VLAN (short for ``virtual LAN'') is a way to partition a single switch into multiple switches. VLANs can be confusing, so for an introduction, please refer to the question ``What's a VLAN?'' in the Open vSwitch FAQ.

    A VLAN is sometimes encoded into a packet using a 802.1Q or 802.1ad VLAN header, but every packet is part of some VLAN whether or not it is encoded in the packet. (A packet that appears to have no VLAN is part of VLAN 0, by default.) As a result, it's useful to think of a VLAN as a metadata property of a packet, separate from how the VLAN is encoded. For a given port, this column determines how the encoding of a packet that ingresses or egresses the port maps to the packet's VLAN. When a packet enters the switch, its VLAN is determined based on its setting in this column and its VLAN headers, if any, and then, conceptually, the VLAN headers are then stripped off. Conversely, when a packet exits the switch, its VLAN and the settings in this column determine what VLAN headers, if any, are pushed onto the packet before it egresses the port.

    The VLAN configuration in this column affects Open vSwitch only when it is doing ``normal switching.'' It does not affect flows set up by an OpenFlow controller, outside of the OpenFlow ``normal action.''

    Bridge ports support the following types of VLAN configuration:

    trunk

    A trunk port carries packets on one or more specified VLANs specified in the column (often, on every VLAN). A packet that ingresses on a trunk port is in the VLAN specified in its 802.1Q header, or VLAN 0 if the packet has no 802.1Q header. A packet that egresses through a trunk port will have an 802.1Q header if it has a nonzero VLAN ID.

    Any packet that ingresses on a trunk port tagged with a VLAN that the port does not trunk is dropped.

    access

    An access port carries packets on exactly one VLAN specified in the column. Packets egressing on an access port have no 802.1Q header.

    Any packet with an 802.1Q header with a nonzero VLAN ID that ingresses on an access port is dropped, regardless of whether the VLAN ID in the header is the access port's VLAN ID.

    native-tagged
    A native-tagged port resembles a trunk port, with the exception that a packet without an 802.1Q header that ingresses on a native-tagged port is in the ``native VLAN'' (specified in the column).
    native-untagged
    A native-untagged port resembles a native-tagged port, with the exception that a packet that egresses on a native-untagged port in the native VLAN will not have an 802.1Q header.
    dot1q-tunnel

    A dot1q-tunnel port is somewhat like an access port. Like an access port, it carries packets on the single VLAN specified in the column and this VLAN, called the service VLAN, does not appear in an 802.1Q header for packets that ingress or egress on the port. The main difference lies in the behavior when packets that include a 802.1Q header ingress on the port. Whereas an access port drops such packets, a dot1q-tunnel port treats these as double-tagged with the outer service VLAN and the inner customer VLAN taken from the 802.1Q header. Correspondingly, to egress on the port, a packet outer VLAN (or only VLAN) must be , which is removed before egress, which exposes the inner (customer) VLAN if one is present.

    If is set, only allows packets in the specified customer VLANs.

    A packet will only egress through bridge ports that carry the VLAN of the packet, as described by the rules above.

    The VLAN mode of the port, as described above. When this column is empty, a default mode is selected as follows:

    • If contains a value, the port is an access port. The column should be empty.
    • Otherwise, the port is a trunk port. The column value is honored if it is present.

    For an access port, the port's implicitly tagged VLAN. For a native-tagged or native-untagged port, the port's native VLAN. Must be empty if this is a trunk port.

    For a trunk, native-tagged, or native-untagged port, the 802.1Q VLAN or VLANs that this port trunks; if it is empty, then the port trunks all VLANs. Must be empty if this is an access port.

    A native-tagged or native-untagged port always trunks its native VLAN, regardless of whether includes that VLAN.

    For a dot1q-tunnel port, the customer VLANs that this port includes. If this is empty, the port includes all customer VLANs.

    For other kinds of ports, this setting is ignored.

    For a dot1q-tunnel port, this is the TPID for the service tag, that is, for the 802.1Q header that contains the service VLAN ID. Because packets that actually ingress and egress a dot1q-tunnel port do not include an 802.1Q header for the service VLAN, this does not affect packets on the dot1q-tunnel port itself. Rather, it determines the service VLAN for a packet that ingresses on a dot1q-tunnel port and egresses on a trunk port.

    The value 802.1ad specifies TPID 0x88a8, which is also the default if the setting is omitted. The value 802.1q specifies TPID 0x8100.

    For other kinds of ports, this setting is ignored.

    An 802.1Q header contains two important pieces of information: a VLAN ID and a priority. A frame with a zero VLAN ID, called a ``priority-tagged'' frame, is supposed to be treated the same way as a frame without an 802.1Q header at all (except for the priority).

    However, some network elements ignore any frame that has 802.1Q header at all, even when the VLAN ID is zero. Therefore, by default Open vSwitch does not output priority-tagged frames, instead omitting the 802.1Q header entirely if the VLAN ID is zero. Set this key to if-nonzero to enable priority-tagged frames on a port.

    For if-nonzero Open vSwitch omits the 802.1Q header on output if both the VLAN ID and priority would be zero. Set to always to retain the 802.1Q header in such frames as well.

    All frames output to native-tagged ports have a nonzero VLAN ID, so this setting is not meaningful on native-tagged ports.

    A port that has more than one interface is a ``bonded port.'' Bonding allows for load balancing and fail-over.

    The following types of bonding will work with any kind of upstream switch. On the upstream switch, do not configure the interfaces as a bond:

    balance-slb
    Balances flows among members based on source MAC address and output VLAN, with periodic rebalancing as traffic patterns change.
    active-backup
    Assigns all flows to one member, failing over to a backup member when the active member is disabled. This is the only bonding mode in which interfaces may be plugged into different upstream switches.

    The following modes require the upstream switch to support 802.3ad with successful LACP negotiation. If LACP negotiation fails and other-config:lacp-fallback-ab is true, then active-backup mode is used:

    balance-tcp
    Balances flows among members based on L3 and L4 protocol information such as IP addresses and TCP/UDP ports.

    These columns apply only to bonded ports. Their values are otherwise ignored.

    The type of bonding used for a bonded port. Defaults to active-backup if unset.

    An integer hashed along with flows when choosing output members in load balanced bonds. When changed, all flows will be assigned different hash values possibly causing member selection decisions to change. Does not affect bonding modes which do not employ load balancing such as active-backup. Enable/disable usage of optimized lb_output action for balancing flows among output members in load balanced bonds in balance-tcp. When enabled, it uses optimized path for balance-tcp mode by using rss hash and avoids recirculation. This knob does not affect other balancing modes. If a member interface with this name exists in the bond and is up, it will be made active. Relevant only when is active-backup or if balance-tcp falls back to active-backup (e.g., LACP negotiation fails and is true).

    Enable/Disable delivery of broadcast/multicast packets on secondary interface of a balance-slb bond. Relevant only when is off.

    This parameter is identical to all_slaves_active for Linux kernel bonds. Disabled by default as it is not a desirable configuration for most users.

    An important part of link bonding is detecting that links are down so that they may be disabled. These settings determine how Open vSwitch detects link failure.

    The means used to detect link failures. Defaults to carrier which uses each interface's carrier to detect failures. When set to miimon, will check for failures by polling each interface's MII. The interval, in milliseconds, between successive attempts to poll each interface's MII. Relevant only when is miimon.

    The number of milliseconds for which the link must stay up on an interface before the interface is considered to be up. Specify 0 to enable the interface immediately.

    This setting is honored only when at least one bonded interface is already enabled. When no interfaces are enabled, then the first bond interface to come up is enabled immediately.

    The number of milliseconds for which the link must stay down on an interface before the interface is considered to be down. Specify 0 to disable the interface immediately.

    LACP, the Link Aggregation Control Protocol, is an IEEE standard that allows switches to automatically detect that they are connected by multiple links and aggregate across those links. These settings control LACP behavior.

    Configures LACP on this port. LACP allows directly connected switches to negotiate which links may be bonded. LACP may be enabled on non-bonded ports for the benefit of any switches they may be connected to. active ports are allowed to initiate LACP negotiations. passive ports are allowed to participate in LACP negotiations initiated by a remote switch, but not allowed to initiate such negotiations themselves. If LACP is enabled on a port whose partner switch does not support LACP, the bond will be disabled, unless other-config:lacp-fallback-ab is set to true. Defaults to off if unset. The LACP system ID of this . The system ID of a LACP bond is used to identify itself to its partners. Must be a nonzero MAC address. Defaults to the bridge Ethernet address if unset. The LACP system priority of this . In LACP negotiations, link status decisions are made by the system with the numerically lower priority.

    The LACP timing which should be used on this . By default slow is used. When configured to be fast LACP heartbeats are requested at a rate of once per second causing connectivity problems to be detected more quickly. In slow mode, heartbeats are requested at a rate of once every 30 seconds.

    Determines the behavior of openvswitch bond in LACP mode. If the partner switch does not support LACP, setting this option to true allows openvswitch to fallback to active-backup. If the option is set to false, the bond will be disabled. In both the cases, once the partner switch is configured to LACP mode, the bond will use LACP.

    These settings control behavior when a bond is in balance-slb or balance-tcp mode.

    For a load balanced bonded port, the number of milliseconds between successive attempts to rebalance the bond, that is, to move flows from one interface on the bond to another in an attempt to keep usage of each interface roughly equal. If zero, load balancing is disabled on the bond (link failure still cause flows to move). If less than 1000ms, the rebalance interval will be 1000ms.
    For a bonded port, whether to create a fake internal interface with the name of the port. Use only for compatibility with legacy software that requires this.

    The configuration here is only meaningful, and the status is only populated, when 802.1D-1998 Spanning Tree Protocol is enabled on the port's with its column.

    When STP is enabled on a bridge, it is enabled by default on all of the bridge's ports except bond, internal, and mirror ports (which do not work with STP). If this column's value is false, STP is disabled on the port. The port number used for the lower 8 bits of the port-id. By default, the numbers will be assigned automatically. If any port's number is manually configured on a bridge, then they must all be. The port's relative priority value for determining the root port (the upper 8 bits of the port-id). A port with a lower port-id will be chosen as the root port. By default, the priority is 0x80. Spanning tree path cost for the port. A lower number indicates a faster link. By default, the cost is based on the maximum speed of the link. The port ID used in spanning tree advertisements for this port, as 4 hex digits. Configuring the port ID is described in the stp-port-num and stp-port-priority keys of the other_config section earlier. STP state of the port. The amount of time this port has been in the current STP state, in seconds. STP role of the port.

    The configuration here is only meaningful, and the status and statistics are only populated, when 802.1D-1998 Spanning Tree Protocol is enabled on the port's with its column.

    When RSTP is enabled on a bridge, it is enabled by default on all of the bridge's ports except bond, internal, and mirror ports (which do not work with RSTP). If this column's value is false, RSTP is disabled on the port. The port's relative priority value for determining the root port, in multiples of 16. By default, the port priority is 0x80 (128). Any value in the lower 4 bits is rounded off. The significant upper 4 bits become the upper 4 bits of the port-id. A port with the lowest port-id is elected as the root. The local RSTP port number, used as the lower 12 bits of the port-id. By default the port numbers are assigned automatically, and typically may not correspond to the OpenFlow port numbers. A port with the lowest port-id is elected as the root. The port path cost. The Port's contribution, when it is the Root Port, to the Root Path Cost for the Bridge. By default the cost is automatically calculated from the port's speed. The admin edge port parameter for the Port. Default is false. The auto edge port parameter for the Port. Default is true.

    The mcheck port parameter for the Port. Default is false. May be set to force the Port Protocol Migration state machine to transmit RST BPDUs for a MigrateTime period, to test whether all STP Bridges on the attached LAN have been removed and the Port can continue to transmit RSTP BPDUs. Setting mcheck has no effect if the Bridge is operating in STP Compatibility mode.

    Changing the value from true to false has no effect, but needs to be done if this behavior is to be triggered again by subsequently changing the value from false to true.

    The port ID used in spanning tree advertisements for this port, as 4 hex digits. Configuring the port ID is described in the rstp-port-num and rstp-port-priority keys of the other_config section earlier. RSTP role of the port. RSTP state of the port. The port's RSTP designated bridge ID, in the same form as in the table. The port's RSTP designated port ID, as 4 hex digits. The port's RSTP designated path cost. Lower is better. Number of RSTP BPDUs transmitted through this port. Number of valid RSTP BPDUs received by this port. Number of invalid RSTP BPDUs received by this port. The duration covered by the other RSTP statistics, in seconds.

    If set to true, multicast packets (except Reports) are unconditionally forwarded to the specific port.

    If set to true, multicast Reports are unconditionally forwarded to the specific port.

    Quality of Service configuration for this port. The MAC address to use for this port for the purpose of choosing the bridge's MAC address. This column does not necessarily reflect the port's actual MAC address, nor will setting it change the port's actual MAC address. Does this port represent a sub-bridge for its tagged VLAN within the Bridge? See ovs-vsctl(8) for more information. The protected ports feature allows certain ports to be designated as protected. Traffic between protected ports is blocked. Protected ports can send traffic to unprotected ports. Unprotected ports can send traffic to any port. Default is false. External IDs for a fake bridge (see the column) are defined by prefixing a key with fake-bridge-, e.g. fake-bridge-bridge-id.

    If set to true, the port will be removed when ovs-ctl start --delete-transient-ports is used.

    For a bonded port, record the MAC address of the current active member.

    Key-value pairs that report port statistics. The update period is controlled by in the Open_vSwitch table.

    Number of STP BPDUs sent on this port by the spanning tree library. Number of STP BPDUs received on this port and accepted by the spanning tree library. Number of bad STP BPDUs received on this port. Bad BPDUs include runt packets and those with an unexpected protocol ID.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.
    An interface within a .

    Interface name. Should be alphanumeric. For non-bonded port, this should be the same as the port name. It must otherwise be unique among the names of ports, interfaces, and bridges on a host.

    The maximum length of an interface name depends on the underlying datapath:

    • The names of interfaces implemented as Linux and BSD network devices, including interfaces with type internal, tap, or system plus the different types of tunnel ports, are limited to 15 bytes. Windows limits these names to 255 bytes.
    • The names of patch ports are not used in the underlying datapath, so operating system restrictions do not apply. Thus, they may have arbitrary length.

    Regardless of other restrictions, OpenFlow only supports 15-byte names, which means that ovs-ofctl and OpenFlow controllers will show names truncated to 15 bytes.

    A positive interface index as defined for SNMP MIB-II in RFCs 1213 and 2863, if the interface has one, otherwise 0. The ifindex is useful for seamless integration with protocols such as SNMP and sFlow. The MAC address in use by this interface.

    Ethernet address to set for this interface. If unset then the default MAC address is used:

    • For the local interface, the default is the lowest-numbered MAC address among the other bridge ports, either the value of the in its record, if set, or its actual MAC (for bonded ports, the MAC of its member whose name is first in alphabetical order). Internal ports and bridge ports that are used as port mirroring destinations (see the table) are ignored.
    • For other internal interfaces, the default MAC is randomly generated.
    • External interfaces typically have a MAC address associated with their hardware.

    Some interfaces may not have a software-controllable MAC address. This option only affects internal ports. For other type ports, you can change the MAC address outside Open vSwitch, using ip command.

    If the configuration of the port failed, as indicated by -1 in , Open vSwitch sets this column to an error description in human readable form. Otherwise, Open vSwitch clears this column.

    When a client adds a new interface, Open vSwitch chooses an OpenFlow port number for the new port. If the client that adds the port fills in , then Open vSwitch tries to use its value as the OpenFlow port number. Otherwise, or if the requested port number is already in use or cannot be used for another reason, Open vSwitch automatically assigns a free port number. Regardless of how the port number was obtained, Open vSwitch then reports in the port number actually assigned.

    Open vSwitch limits the port numbers that it automatically assigns to the range 1 through 32,767, inclusive. Controllers therefore have free use of ports 32,768 and up.

    OpenFlow port number for this interface. Open vSwitch sets this column's value, so other clients should treat it as read-only.

    The OpenFlow ``local'' port (OFPP_LOCAL) is 65,534. The other valid port numbers are in the range 1 to 65,279, inclusive. Value -1 indicates an error adding the interface.

    Requested OpenFlow port number for this interface.

    A client should ideally set this column's value in the same database transaction that it uses to create the interface. Open vSwitch version 2.1 and later will honor a later request for a specific port number, althuogh it might confuse some controllers: OpenFlow does not have a way to announce a port number change, so Open vSwitch represents it over OpenFlow as a port deletion followed immediately by a port addition.

    If is set or changed to some other port's automatically assigned port number, Open vSwitch chooses a new port number for the latter port.

    The interface type. The types supported by a particular instance of Open vSwitch are listed in the column in the table. The following types are defined:

    system
    An ordinary network device, e.g. eth0 on Linux. Sometimes referred to as ``external interfaces'' since they are generally connected to hardware external to that on which the Open vSwitch is running. The empty string is a synonym for system.
    internal
    A simulated network device that sends and receives traffic. An internal interface whose is the same as its bridge's is called the ``local interface.'' It does not make sense to bond an internal interface, so the terms ``port'' and ``interface'' are often used imprecisely for internal interfaces.
    tap

    A TUN/TAP device managed by Open vSwitch.

    Open vSwitch checks the interface state before send packets to the device. When it is down, the packets are dropped and the tx_dropped statistic is updated accordingly. Older versions of Open vSwitch did not check the interface state and then the tx_packets was incremented along with tx_dropped.

    geneve
    An Ethernet over Geneve (http://tools.ietf.org/html/draft-ietf-nvo3-geneve) IPv4/IPv6 tunnel. A description of how to match and set Geneve options can be found in the ovs-ofctl manual page.
    gre
    Generic Routing Encapsulation (GRE) over IPv4 tunnel, configurable to encapsulate layer 2 or layer 3 traffic.
    ip6gre
    Generic Routing Encapsulation (GRE) over IPv6 tunnel, encapsulate layer 2 traffic.
    vxlan

    An Ethernet tunnel over the UDP-based VXLAN protocol described in RFC 7348.

    Open vSwitch uses IANA-assigned UDP destination port 4789. The source port used for VXLAN traffic varies on a per-flow basis and is in the ephemeral port range.

    patch
    A pair of virtual devices that act as a patch cable.
    gtpu

    GPRS Tunneling Protocol (GTP) is a group of IP-based communications protocols used to carry general packet radio service (GPRS) within GSM, UMTS and LTE networks. GTP-U is used for carrying user data within the GPRS core network and between the radio access network and the core network. The user data transported can be packets in any of IPv4, IPv6, or PPP formats.

    The protocol is documented at http://www.3gpp.org/DynaReport/29281.htm

    Open vSwitch uses UDP destination port 2152. The source port used for GTP traffic varies on a per-flow basis and is in the ephemeral port range.

    Bareudp

    The Bareudp tunnel provides a generic L3 encapsulation support for tunnelling different L3 protocols like MPLS, IP, NSH etc. inside a UDP tunnel.

    srv6

    Segment Routing IPv6 (SRv6) tunnel encapsulates L3 traffic as "IPv6 in IPv6" or "IPv4 in IPv6" with Segment Routing Header (SRH) defined in RFC 8754. The segment list in SRH can be set using a SRv6 specific option.

    These options apply to interfaces with of geneve, bareudp, gre, ip6gre, vxlan, and srv6.

    Each tunnel must be uniquely identified by the combination of , , , and . If two ports are defined that are the same except one has an optional identifier and the other does not, the more specific one is matched first. is considered more specific than if a port defines one and another port defines the other. is not applicable for bareudp and srv6 tunnels. Hence it is not considered while identifying bareudp or srv6 tunnels.

    Required. The remote tunnel endpoint, one of:

    • An IPv4 or IPv6 address (not a DNS name), e.g. 192.168.0.123. Only unicast endpoints are supported.
    • The word flow. The tunnel accepts packets from any remote tunnel endpoint. To process only packets from a specific remote tunnel endpoint, the flow entries may match on the tun_src or tun_ipv6_srcfield. When sending packets to a remote_ip=flow tunnel, the flow actions must explicitly set the tun_dst or tun_ipv6_dst field to the IP address of the desired remote tunnel endpoint, e.g. with a set_field action.

    The remote tunnel endpoint for any packet received from a tunnel is available in the tun_src field for matching in the flow table.

    Optional. The tunnel destination IP that received packets must match. Default is to match all addresses. If specified, may be one of:

    • An IPv4/IPv6 address (not a DNS name), e.g. 192.168.12.3.
    • The word flow. The tunnel accepts packets sent to any of the local IP addresses of the system running OVS. To process only packets sent to a specific IP address, the flow entries may match on the tun_dst or tun_ipv6_dst field. When sending packets to a local_ip=flow tunnel, the flow actions may explicitly set the tun_src or tun_ipv6_src field to the desired IP address, e.g. with a set_field action. However, while routing the tunneled packet out, the local system may override the specified address with the local IP address configured for the outgoing system interface.

      This option is valid only for tunnels also configured with the remote_ip=flow option.

    The tunnel destination IP address for any packet received from a tunnel is available in the tun_dst or tun_ipv6_dst field for matching in the flow table.

    Optional, not applicable for bareudp and srv6. The key that received packets must contain, one of:

    • 0. The tunnel receives packets with no key or with a key of 0. This is equivalent to specifying no at all.
    • A positive 24-bit (for Geneve and VXLAN) or 32-bit (for GRE) number. The tunnel receives only packets with the specified key.
    • The word flow. The tunnel accepts packets with any key. The key will be placed in the tun_id field for matching in the flow table. The ovs-fields(7) manual page contains additional information about matching fields in OpenFlow flows.

    Optional, not applicable for bareudp and srv6. The key to be set on outgoing packets, one of:

    • 0. Packets sent through the tunnel will have no key. This is equivalent to specifying no at all.
    • A positive 24-bit (for Geneve and VXLAN) or 32-bit (for GRE) number. Packets sent through the tunnel will have the specified key.
    • The word flow. Packets sent through the tunnel will have the key set using the set_tunnel Nicira OpenFlow vendor extension (0 is used in the absence of an action). The ovs-fields(7) manual page contains additional information about the Nicira OpenFlow vendor extensions.
    Optional. The tunnel transport layer destination port, for UDP based tunnel protocols (Geneve, VXLAN). Optional. Shorthand to set in_key and out_key at the same time. Optional. The value of the ToS bits to be set on the encapsulating packet. ToS is interpreted as DSCP and ECN bits, ECN part must be zero. It may also be the word inherit, in which case the ToS will be copied from the inner packet if it is IPv4 or IPv6 (otherwise it will be 0). The ECN fields are always inherited. Default is 0. Optional. The TTL to be set on the encapsulating packet. It may also be the word inherit, in which case the TTL will be copied from the inner packet if it is IPv4 or IPv6 (otherwise it will be the system default, typically 64). Default is the system default TTL. Optional. If enabled, the Don't Fragment bit will be set on tunnel outer headers to allow path MTU discovery. Default is enabled; set to false to disable. Optional. The pkt_mark to be set on the encapsulating packet. This option sets packet mark for the tunnel endpoint for all tunnel packets including tunnel monitoring.

    Optional. Comma separated list of optional VXLAN extensions to enable. The following extensions are supported:

    • gbp: VXLAN-GBP allows to transport the group policy context of a packet across the VXLAN tunnel to other network peers. See the description of tun_gbp_id and tun_gbp_flags in ovs-fields(7) for additional information. (https://tools.ietf.org/html/draft-smith-vxlan-group-policy)
    • gpe: Support for Generic Protocol Encapsulation in accordance with IETF draft https://tools.ietf.org/html/draft-ietf-nvo3-vxlan-gpe. Without this option, a VXLAN packet always encapsulates an Ethernet frame. With this option, an VXLAN packet may also encapsulate an IPv4, IPv6, NSH, or MPLS packet.

    This option controls what types of packets the tunnel sends and receives and how it represents them:

    • By default, or if this option is legacy_l2, the tunnel sends and receives only Ethernet frames.
    • If this option is legacy_l3, the tunnel sends and receives only non-Ethernet (L3) packet, but the packets are represented as Ethernet frames for compatibility with legacy OpenFlow controllers that expect this behavior. This requires enabling gpe in .
    • If this option is ptap, Open vSwitch represents packets in the tunnel using the packet_type mechanism introduced in OpenFlow 1.5. This mechanism supports any kind of packet, but actually sending and receiving non-Ethernet packets requires additionally enabling gpe in .

    gre interfaces support these options.

    This option controls what types of packets the tunnel sends and receives and how it represents them:

    • By default, or if this option is legacy_l2, the tunnel sends and receives only Ethernet frames.
    • If this option is legacy_l3, the tunnel sends and receives only non-Ethernet (L3) packet, but the packets are represented as Ethernet frames for compatibility with legacy OpenFlow controllers that expect this behavior.
    • The legacy_l3 option is only available via the user space datapath. The OVS kernel datapath does not support devices of type ARPHRD_IPGRE which is the requirement for legacy_l3 type packets.
    • If this option is ptap, the tunnel sends and receives any kind of packet. Open vSwitch represents packets in the tunnel using the packet_type mechanism introduced in OpenFlow 1.5.

    Optional. A 4-byte sequence number field for GRE tunnel only. Default is disabled, set to true to enable. Sequence number is incremented by one on each outgoing packet.

    gre, ip6gre, geneve, bareudp and vxlan interfaces support these options.

    Optional. Compute encapsulation header (either GRE or UDP) checksums on outgoing packets. When unset (the default value), checksum computing for outgoing packets is enabled for UDP IPv6 tunnels, and disabled for GRE and IPv4 UDP tunnels. When set to false, no checksums will be computed for outgoing tunnel encapsulation headers. When true, checksums will be computed for all outgoing tunnel encapsulation headers. Checksums present on incoming packets will be validated regardless of this setting. Incoming packets without a checksum will also be accepted regardless of this setting.

    When using the upstream Linux kernel module, computation of checksums for geneve and vxlan requires Linux kernel version 4.0 or higher. gre and ip6gre support checksums for all versions of Open vSwitch that support GRE. The out of tree kernel module distributed as part of OVS can compute all tunnel checksums on any kernel version that it is compatible with.

    Setting any of these options enables IPsec support for a given tunnel. gre, geneve and vxlan interfaces support these options. See the IPsec section in the table for a description of each mode.

    In PSK mode only, the preshared secret to negotiate tunnel. This value must match on both tunnel ends.

    In self-signed certificate mode only, name of a PEM file containing a certificate of the remote switch. The certificate must be x.509 version 3 and with the string in common name (CN) also set in the subject alternative name (SAN).

    In CA-signed certificate mode only, common name (CN) of the remote certificate.

    Only erspan interfaces support these options.

    20 bit index/port number associated with the ERSPAN traffic's source port and direction (ingress/egress). This field is platform dependent.

    ERSPAN version: 1 for version 1 (type II) or 2 for version 2 (type III).

    Specifies the ERSPAN v2 mirrored traffic's direction. 1 for egress traffic, and 0 for ingress traffic.

    ERSPAN hardware ID is a 6-bit unique identifier of an ERSPAN v2 engine within a system.

    Specifies the ethertype of the l3 protocol the bareudp device is tunnelling. For the tunnels which supports multiple ethertypes of a l3 protocol (IP, MPLS) this field specifies the protocol name as a string.

    Specifies the segment list in Segment Routing Header (SRH). It consists of a comma-separated list of segments represented in IPv6 format, e.g. "fc00:100::1,fc00:200::1,fc00:300::1". Note that the first segment must be the same as .

    Optional. This option controls how flowlabel in outer IPv6 header is configured. It gives the benefit of IPv6 flow label based load balancing, which is supported by some popular vendor appliances. Like net.ipv6.seg6_flowlabel sysconfig, it is one of the three values below:

    • By default, or if this option is copy, copy the flowlabel of inner IPv6 header to the flowlabel of outer IPv6 header. If inner header is not IPv6, it is set to 0.
    • If this option is zero, simply set flowlabel to 0.
    • If this option is compute, set flowlabel to a hash over the L3/L4 fields of the inner packet.

    These options apply only to patch ports, that is, interfaces whose column is patch. Patch ports are mainly a way to connect otherwise independent bridges to one another, similar to how one might plug an Ethernet cable (a ``patch cable'') into two physical switches to connect those switches. The effect of plugging a patch port into two switches is conceptually similar to that of plugging the two ends of a Linux veth device into those switches, but the implementation of patch ports makes them much more efficient.

    Patch ports may connect two different bridges (the usual case) or the same bridge. In the latter case, take special care to avoid loops, e.g. by programming appropriate flows with OpenFlow. Patch ports do not work if its ends are attached to bridges on different datapaths, e.g. to connect bridges in system and netdev datapaths.

    The following command creates and connects patch ports p0 and p1 and adds them to bridges br0 and br1, respectively:

    ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
           -- add-port br1 p1 -- set Interface p1 type=patch options:peer=p0
          
    The of the for the other side of the patch. The named 's own peer option must specify this 's name. That is, the two patch interfaces must have reversed and peer values.

    Only PMD netdevs support these options.

    Specifies the maximum number of rx queues to be created for PMD netdev. If not specified or specified to 0, one rx queue will be created by default. Not supported by DPDK vHost interfaces.

    Specifies the PCI address associated with the port for physical devices, or the virtual driver to be used for the port when a virtual PMD is intended to be used. For the latter, the argument string typically takes the form of eth_driver_namex, where driver_name is a valid virtual DPDK PMD driver name and x is a unique identifier of your choice for the given port. Only supported by the dpdk port type.

    Specifies mapping of RX queues of this interface to CPU cores.

    Value should be set in the following form:

    other_config:pmd-rxq-affinity=<rxq-affinity-list>

    where

    • <rxq-affinity-list> ::= NULL | <non-empty-list>
    • <non-empty-list> ::= <affinity-pair> | <affinity-pair> , <non-empty-list>
    • <affinity-pair> ::= <queue-id> : <core-id>

    Specifies the operational mode of the XDP program.

    In native-with-zerocopy mode the XDP program is loaded into the device driver with zero-copy RX and TX enabled. This mode requires device driver support and has the best performance because there should be no copying of packets.

    native is the same as native-with-zerocopy, but without zero-copy capability. This requires at least one copy between kernel and the userspace. This mode also requires support from device driver.

    In generic case the XDP program in kernel works after skb allocation on early stages of packet processing inside the network stack. This mode doesn't require driver support, but has much lower performance.

    best-effort tries to detect and choose the best (fastest) from the available modes for current interface.

    Note that this option is specific to netdev-afxdp. Defaults to best-effort mode.

    The value specifies the path to the socket associated with a vHost User client mode device that has been or will be created by QEMU. Only supported by dpdkvhostuserclient interfaces.

    The value specifies the maximum number of queue pairs supported by a vHost device. This is ignored for vhost-user backends, only VDUSE is supported. Only supported by dpdkvhostuserclient interfaces.

    Default value is 1.

    The value specifies the maximum amount of vhost tx retries that can be made while trying to send a batch of packets to an interface. Only supported by dpdkvhostuserclient interfaces.

    Default value is 8.

    Specifies the rx queue size (number rx descriptors) for dpdk ports. The value must be a power of 2 and supported by the hardware of the device being configured. If not specified or an incorrect value is specified, 2048 rx descriptors will be used by default.

    Specifies the tx queue size (number tx descriptors) for dpdk ports. The value must be a power of 2 and supported by the hardware of the device being configured. If not specified or an incorrect value is specified, 2048 tx descriptors will be used by default.

    Ethernet address to set for this VF interface. If unset then the default MAC address is used:

    • For most drivers, the default MAC address assigned by their hardware.
    • For bifurcated drivers, the MAC currently used by the kernel netdevice.

    This option may only be used with dpdk VF representors.

    Configure hardware Rx queue steering policy.

    This option takes one of the following values:

    rss
    Distribution of ingress packets in all Rx queues according to the RSS algorithm. This is the default behaviour.
    rss+lacp
    Distribution of ingress packets according to the RSS algorithm on all but the last Rx queue. An extra Rx queue is allocated for LACP packets.

    If the user has already configured multiple on the port, an additional one will be allocated for the specified protocols. Even if the hardware cannot satisfy the requested number of requested Rx queues, the last Rx queue will be used. If only one Rx queue is available or if the hardware does not support the rte_flow matchers/actions required to redirect the selected protocols, custom rx-steering will fall back to default rss mode.

    This feature is mutually exclusive with as it may conflict with the offloaded flows. If both are enabled, rx-steering will fall back to default rss mode.

    This option is only applicable to interfaces with type dpdk.

    Specifies the Tx steering mode for the interface.

    thread enables static (1:1) thread-to-txq mapping when the number of Tx queues is greater than number of PMD threads, and dynamic (N:1) mapping if equal or lower. In this mode a single thread can not use more than 1 transmit queue of a given port.

    hash enables hash-based Tx steering, which distributes the packets on all the transmit queues based on their 5-tuples hashes.

    Defaults to thread.

    These settings controls behaviour of EMC lookups/insertions for packets received from the interface.

    Specifies if Exact Match Cache (EMC) should be used while processing packets received from this interface. If true, will have effect on this interface.

    Defaults to true.

    The MTU (maximum transmission unit) is the largest amount of data that can fit into a single Ethernet frame. The standard Ethernet MTU is 1500 bytes. Some physical media and many kinds of virtual interfaces can be configured with higher MTUs.

    A client may change an interface MTU by filling in . Open vSwitch then reports in the currently configured value.

    The currently configured MTU for the interface.

    This column will be empty for an interface that does not have an MTU as, for example, some kinds of tunnels do not.

    Open vSwitch sets this column's value, so other clients should treat it as read-only.

    Requested MTU (Maximum Transmission Unit) for the interface. A client can fill this column to change the MTU of an interface.

    RFC 791 requires every internet module to be able to forward a datagram of 68 octets without further fragmentation. The maximum size of an IP packet is 65535 bytes.

    If this is not set and if the interface has internal type, Open vSwitch will change the MTU to match the minimum of the other interfaces in the bridge.

    Status information about interfaces attached to bridges, updated every 5 seconds. Not all interfaces have all of these properties; virtual interfaces don't have a link speed, for example. Non-applicable columns will have empty values.

    The administrative state of the physical network link.

    The observed state of the physical network link. This is ordinarily the link's carrier status. If the interface's is a bond configured for miimon monitoring, it is instead the network link's miimon status.

    The number of times Open vSwitch has observed the of this change.

    The negotiated speed of the physical network link. Valid values are positive integers greater than 0.

    The duplex mode of the physical network link.

    Boolean value indicating LACP status for this interface. If true, this interface has current LACP information about its LACP partner. This information may be used to monitor the health of interfaces in a LACP enabled port. This column will be empty if LACP is not enabled. Key-value pairs that report port status. Supported status values are -dependent; some interfaces may not have a valid , for example. The name of the device driver controlling the network adapter. The version string of the device driver controlling the network adapter. The version string of the network adapter's firmware, if available. The source IP address used for an IPv4/IPv6 tunnel end-point, such as gre. Egress interface for tunnels. Currently only relevant for tunnels on Linux systems, this column will show the name of the interface which is responsible for routing traffic destined for the configured . This could be an internal interface such as a bridge port. Whether carrier is detected on .

    DPDK specific interface status options.

    DPDK port ID. NUMA socket ID to which an Ethernet device is connected. Minimum size of RX buffer. Maximum configurable length of RX pkt. Maximum number of RX queues. Maximum number of TX queues. Maximum number of MAC addresses. Maximum number of hash MAC addresses for MTA and UTA. Maximum number of hash MAC addresses for MTA and UTA. Maximum number of VFs. Maximum number of VMDq pools. Number of Rx queues. Number of Tx queues. Whether Rx Checksum offload is enabled or not. Interface type ID according to IANA ifTYPE MIB definitions. Interface description string. Bus name and bus info such as Vendor ID and Device ID of PCI device. Ethernet address set for this VF interface. Only reported for dpdk VF representors. Hardware Rx queue steering policy in use. ID of rx steering queue. Only reported if rx-steering is supported by hardware. IDs of rss queues. Only reported if rx-steering is supported by hardware.

    dpdkvhostuser and dpdkvhostuserclient netdev specific interface status information.

    client (connecting) or server (listening) in the socket communication. virtio features bitmap as per virtio specification. The number of available virtqueues. The numa id of the device and guest memory. The path to the socket used for communication. Status of connection to the device. Each virtqueue will have it's size reported, where n is the virtqueue number from 0..(num_of_vrings-1). Whether userspace-tso is enabled or disabled.

    AF_XDP specific interface status options.

    XDP mode currently in use. See for description of possible values.

    Key-value pairs that report interface statistics. The current implementation updates these counters periodically. The update period is controlled by in the Open_vSwitch table. Future implementations may update them when an interface is created, when they are queried (e.g. using an OVSDB select operation), and just before an interface is deleted due to virtual interface hot-unplug or VM shutdown, and perhaps at other times, but not on any regular periodic basis.

    These are the same statistics reported by OpenFlow in its struct ofp_port_stats structure. If an interface does not support a given statistic, then that pair is omitted.

    Number of received packets. Number of received bytes. Number of transmitted packets. Number of transmitted bytes. Number of packets dropped by RX. Number of frame alignment errors. Number of packets with RX overrun. Number of CRC errors. Total number of receive errors, greater than or equal to the sum of the above. Number of packets dropped by TX. Number of collisions. Total number of transmit errors, greater than or equal to the sum of the above.

    These settings control ingress policing for packets received on this interface. On a physical interface, this limits the rate at which traffic is allowed into the system from the outside; on a virtual interface (one connected to a virtual machine), this limits the rate at which the VM is able to transmit.

    Policing is a simple form of quality-of-service that simply drops packets received in excess of the configured rate. Due to its simplicity, policing is usually less accurate and less effective than egress QoS (which is configured using the and tables).

    Policing settings can be set with byte rate or packet rate, and they can be configured together, in which case they take effect together, that means the smaller speed limit of them is in effect.

    Currently, byte rate policing is implemented on Linux and OVS with DPDK, while packet rate policing is only implemented on Linux. Both Linux and OVS DPDK implementations use a simple ``token bucket'' approach.

    Byte rate policing:

    • The size of the bucket corresponds to . Initially the bucket is full.
    • Whenever a packet is received, its size (converted to tokens) is compared to the number of tokens currently in the bucket. If the required number of tokens are available, they are removed and the packet is forwarded. Otherwise, the packet is dropped.
    • Whenever it is not full, the bucket is refilled with tokens at the rate specified by .

    Packet rate policing:

    • The size of the bucket corresponds to . Initially the bucket is full.
    • Whenever a packet is received, it will consume one token from the current bucket. If the token is available in the bucket, it's removed and the packet is forwarded. Otherwise, the packet is dropped.
    • Whenever it is not full, the bucket is refilled with tokens at the rate specified by .

    Policing interacts badly with some network protocols, and especially with fragmented IP packets. Suppose that there is enough network activity to keep the bucket nearly empty all the time. Then this token bucket algorithm will forward a single packet every so often, with the period depending on packet size and on the configured rate. All of the fragments of an IP packets are normally transmitted back-to-back, as a group. In such a situation, therefore, only one of these fragments will be forwarded and the rest will be dropped. IP does not provide any way for the intended recipient to ask for only the remaining fragments. In such a case there are two likely possibilities for what will happen next: either all of the fragments will eventually be retransmitted (as TCP will do), in which case the same problem will recur, or the sender will not realize that its packet has been dropped and data will simply be lost (as some UDP-based protocols will do). Either way, it is possible that no forward progress will ever occur.

    Maximum rate for data received on this interface, in kbps. Data received faster than this rate is dropped. Set to 0 (the default) to disable policing.

    Maximum rate for data received on this interface, in kpps (1 kpps is 1000 pps). Data received faster than this rate is dropped. Set to 0 (the default) to disable policing.

    Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 8000 kbit. This value has no effect if is 0.

    Specifying a larger burst size lets the algorithm be more forgiving, which is important for protocols like TCP that react severely to dropped packets. The burst size should be at least the size of the interface's MTU. Specifying a value that is numerically at least as large as 80% of helps TCP come closer to achieving the full rate.

    Maximum burst size for data received on this interface, in kpkts (1 kpkts is 1000 packets). The default burst size if set to 0 is 16 kpkts. This value has no effect if is 0.

    Specifying a larger burst size lets the algorithm be more forgiving, which is important for protocols like TCP that react severely to dropped packets. Specifying a value that is numerically at least as large as 80% of helps TCP come closer to achieving the full rate.

    BFD, defined in RFC 5880 and RFC 5881, allows point-to-point detection of connectivity failures by occasional transmission of BFD control messages. Open vSwitch implements BFD to serve as a more popular and standards compliant alternative to CFM.

    BFD operates by regularly transmitting BFD control messages at a rate negotiated independently in each direction. Each endpoint specifies the rate at which it expects to receive control messages, and the rate at which it is willing to transmit them. By default, Open vSwitch uses a detection multiplier of three, meaning that an endpoint signals a connectivity fault if three consecutive BFD control messages fail to arrive. In the case of a unidirectional connectivity issue, the system not receiving BFD control messages signals the problem to its peer in the messages it transmits.

    The Open vSwitch implementation of BFD aims to comply faithfully with RFC 5880 requirements. Open vSwitch does not implement the optional Authentication or ``Echo Mode'' features.

    OVS 2.13 and earlier intercepted and processed all BFD packets. OVS 2.14 and later only intercept and process BFD packets destined to a configured BFD instance, and other BFD packets are made available to the OVS flow table for forwarding.

    A controller sets up key-value pairs in the column to enable and configure BFD.

    True to enable BFD on this . If not specified, BFD will not be enabled by default. The shortest interval, in milliseconds, at which this BFD session offers to receive BFD control messages. The remote endpoint may choose to send messages at a slower rate. Defaults to 1000. The shortest interval, in milliseconds, at which this BFD session is willing to transmit BFD control messages. Messages will actually be transmitted at a slower rate if the remote endpoint is not willing to receive as quickly as specified. Defaults to 100. An alternate receive interval, in milliseconds, that must be greater than or equal to . The implementation switches from to when there is no obvious incoming data traffic at the interface, to reduce the CPU and bandwidth cost of monitoring an idle interface. This feature may be disabled by setting a value of 0. This feature is reset whenever or changes. When true, traffic received on the is used to indicate the capability of packet I/O. BFD control packets are still transmitted and received. At least one BFD control packet must be received every 100 * amount of time. Otherwise, even if traffic are received, the will be false. Set to true to notify the remote endpoint that traffic should not be forwarded to this system for some reason other than a connectivty failure on the interface being monitored. The typical underlying reason is ``concatenated path down,'' that is, that connectivity beyond the local system is down. Defaults to false. Set to true to make BFD accept only control messages with a tunnel key of zero. By default, BFD accepts control messages with any tunnel key. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used as source for transmitted BFD packets. The default is the mac address of the BFD enabled interface. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used as destination for transmitted BFD packets. The default is 00:23:20:00:00:01. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used for checking the destination of received BFD packets. Packets with different destination MAC will not be considered as BFD packets. If not specified the destination MAC address of received BFD packets are not checked. Set to an IPv4 address to set the IP address used as source for transmitted BFD packets. The default is 169.254.1.1. Set to an IPv4 address to set the IP address used as destination for transmitted BFD packets. The default is 169.254.1.0. Some tunnel protocols (such as Geneve) include a bit in the header to indicate that the encapsulated packet is an OAM frame. By setting this to true, BFD packets will be marked as OAM if encapsulated in one of these tunnels. The BFD detection multiplier, which defaults to 3. An endpoint signals a connectivity fault if the given number of consecutive BFD control messages fail to arrive.

    The switch sets key-value pairs in the column to report the status of BFD on this interface. When BFD is not enabled, with , the switch clears all key-value pairs from .

    Reports the state of the BFD session. The BFD session is fully healthy and negotiated if UP. Reports whether the BFD session believes this may be used to forward traffic. Typically this means the local session is signaling UP, and the remote system isn't signaling a problem such as concatenated path down. A diagnostic code specifying the local system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Reports the state of the remote endpoint's BFD session. A diagnostic code specifying the remote system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Counts the number of flaps since start. A flap is considered as a change of the value.

    802.1ag Connectivity Fault Management (CFM) allows a group of Maintenance Points (MPs) called a Maintenance Association (MA) to detect connectivity problems with each other. MPs within a MA should have complete and exclusive interconnectivity. This is verified by occasionally broadcasting Continuity Check Messages (CCMs) at a configurable transmission interval.

    According to the 802.1ag specification, each Maintenance Point should be configured out-of-band with a list of Remote Maintenance Points it should have connectivity to. Open vSwitch differs from the specification in this area. It simply assumes the link is faulted if no Remote Maintenance Points are reachable, and considers it not faulted otherwise.

    When operating over tunnels which have no in_key, or an in_key of flow. CFM will only accept CCMs with a tunnel key of zero.

    A Maintenance Point ID (MPID) uniquely identifies each endpoint within a Maintenance Association. The MPID is used to identify this endpoint to other Maintenance Points in the MA. Each end of a link being monitored should have a different MPID. Must be configured to enable CFM on this .

    According to the 802.1ag specification, MPIDs can only range between [1, 8191]. However, extended mode (see ) supports eight byte MPIDs.

    Counts the number of cfm fault flapps since boot. A flap is considered to be a change of the value.

    Indicates a connectivity fault triggered by an inability to receive heartbeats from any remote endpoint. When a fault is triggered on s participating in bonds, they will be disabled.

    Faults can be triggered for several reasons. Most importantly they are triggered when no CCMs are received for a period of 3.5 times the transmission interval. Faults are also triggered when any CCMs indicate that a Remote Maintenance Point is not receiving CCMs but able to send them. Finally, a fault is triggered if a CCM is received which indicates unexpected configuration. Notably, this case arises when a CCM is received which advertises the local MPID.

    Indicates a CFM fault was triggered due to a lack of CCMs received on the . Indicates a CFM fault was triggered due to the reception of a CCM with the RDI bit flagged. Endpoints set the RDI bit in their CCMs when they are not receiving CCMs themselves. This typically indicates a unidirectional connectivity failure. Indicates a CFM fault was triggered due to the reception of a CCM with a MAID other than the one Open vSwitch uses. CFM broadcasts are tagged with an identification number in addition to the MPID called the MAID. Open vSwitch only supports receiving CCM broadcasts tagged with the MAID it uses internally. Indicates a CFM fault was triggered due to the reception of a CCM advertising the same MPID configured in the column of this . This may indicate a loop in the network. Indicates a CFM fault was triggered because the CFM module received CCMs from more remote endpoints than it can keep track of. Indicates a CFM fault was manually triggered by an administrator using an ovs-appctl command. Indicates a CFM fault was triggered due to the reception of a CCM frame having an invalid interval.

    When in extended mode, indicates the operational state of the remote endpoint as either up or down. See .

    Indicates the health of the interface as a percentage of CCM frames received over 21 s. The health of an interface is undefined if it is communicating with more than one . It reduces if healthy heartbeats are not received at the expected rate, and gradually improves as healthy heartbeats are received at the desired rate. Every 21 s, the health of the interface is refreshed.

    As mentioned above, the faults can be triggered for several reasons. The link health will deteriorate even if heartbeats are received but they are reported to be unhealthy. An unhealthy heartbeat in this context is a heartbeat for which either some fault is set or is out of sequence. The interface health can be 100 only on receiving healthy heartbeats at the desired rate.

    When CFM is properly configured, Open vSwitch will occasionally receive CCM broadcasts. These broadcasts contain the MPID of the sending Maintenance Point. The list of MPIDs from which this is receiving broadcasts from is regularly collected and written to this column.

    The interval, in milliseconds, between transmissions of CFM heartbeats. Three missed heartbeat receptions indicate a connectivity fault.

    In standard operation only intervals of 3, 10, 100, 1,000, 10,000, 60,000, or 600,000 ms are supported. Other values will be rounded down to the nearest value on the list. Extended mode (see ) supports any interval up to 65,535 ms. In either mode, the default is 1000 ms.

    We do not recommend using intervals less than 100 ms.

    When true, the CFM module operates in extended mode. This causes it to use a nonstandard destination address to avoid conflicting with compliant implementations which may be running concurrently on the network. Furthermore, extended mode increases the accuracy of the cfm_interval configuration parameter by breaking wire compatibility with 802.1ag compliant implementations. And extended mode allows eight byte MPIDs. Defaults to false.

    When true, and is true, the CFM module operates in demand mode. When in demand mode, traffic received on the is used to indicate liveness. CCMs are still transmitted and received. At least one CCM must be received every 100 * amount of time. Otherwise, even if traffic are received, the CFM module will raise the connectivity fault.

    Demand mode has a couple of caveats:

    • To ensure that ovs-vswitchd has enough time to pull statistics from the datapath, the fault detection interval is set to 3.5 * MAX(, 500) ms.
    • To avoid ambiguity, demand mode disables itself when there are multiple remote maintenance points.
    • If the is heavily congested, CCMs containing the status may be dropped causing changes in the operational state to be delayed. Similarly, if CCMs containing the RDI bit are not received, unidirectional link failures may not be detected.

    When down, the CFM module marks all CCMs it generates as operationally down without triggering a fault. This allows remote maintenance points to choose not to forward traffic to the on which this CFM module is running. Currently, in Open vSwitch, the opdown bit of CCMs affects s participating in bonds, and the bundle OpenFlow action. This setting is ignored when CFM is not in extended mode. Defaults to up. When set, the CFM module will apply a VLAN tag to all CCMs it generates with the given value. May be the string random in which case each CCM will be tagged with a different randomly generated VLAN. When set, the CFM module will apply a VLAN tag to all CCMs it generates with the given PCP value, the VLAN ID of the tag is governed by the value of . If is unset, a VLAN ID of zero is used.
    The LACP port ID of this . Port IDs are used in LACP negotiations to identify individual ports participating in a bond. The LACP port priority of this . In LACP negotiations s with numerically lower priorities are preferred for aggregation. The LACP aggregation key of this . s with different aggregation keys may not be active within a given at the same time.

    These key-value pairs specifically apply to an interface that represents a virtual Ethernet interface connected to a virtual machine. These key-value pairs should not be present for other types of interfaces. Keys whose names end in -uuid have values that uniquely identify the entity in question.

    The MAC address programmed into the ``virtual hardware'' for this interface, in the form xx:xx:xx:xx:xx:xx. A system-unique identifier for the interface.

    Hypervisors may sometimes have more than one interface associated with a given , only one of which is actually in use at a given time. For example, in some circumstances hypervisor may have both a ``tap'' and a ``vif'' interface for a single , but only uses one of them at a time. A hypervisor that behaves this way must mark the currently in use interface active and the others inactive. A hypervisor that never has more than one interface for a given may mark that interface active or omit entirely.

    During VM migration, a given might transiently be marked active on two different hypervisors. That is, active means that this is the active instance within a single hypervisor, not in a broader scope. There is one exception: some hypervisors support ``migration'' from a given hypervisor to itself (most often for test purposes). During such a ``migration,'' two instances of a single might both be briefly marked active on a single hypervisor.

    The VM to which this interface belongs.

    Auto Attach configuration for a particular interface.

    True to enable LLDP on this . If not specified, LLDP will be disabled by default.

    Ethernet flow control defined in IEEE 802.1Qbb provides link level flow control using MAC pause frames. Implemented only for interfaces with type dpdk.

    Set to true to enable Rx flow control on physical ports. By default, Rx flow control is disabled. Set to true to enable Tx flow control on physical ports. By default, Tx flow control is disabled. Set to true to enable flow control auto negotiation on physical ports. By default, auto-neg is disabled.

    Set this value to false to configure poll mode for Link State Change (LSC) detection instead of interrupt mode for the DPDK interface.

    If this value is not set, interrupt mode is configured.

    This parameter has an effect only on netdev dpdk interfaces.

    This configuration sets an explicit list of hardware offload providers to try on this Interface. The argument should be a comma-separated list of hardware offload provider names, or the word none.

    If none is encountered in the list, further trying of offload providers is stopped. For example, if the list only contains none, hardware offload is disabled on this interface even if it is globally enabled. Note that unknown hardware offload provider names are ignored.

    If not set, uses the global configuration. Changing this value after offload enablement requires restarting the daemon. To avoid restarting the daemon, set this option in the same transaction that adds the port. For example:

    ovs-vsctl add-port br0 p1 -- \
              set Interface p1 other_config:hw-offload-priority=none
           
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a particular OpenFlow table.

    The table's name. Set this column to change the name that controllers will receive when they request table statistics, e.g. ovs-ofctl dump-tables. The name does not affect switch behavior.

    Open vSwitch supports limiting the number of flows that may be installed in a flow table, via the column. When adding a flow would exceed this limit, by default Open vSwitch reports an error, but there are two ways to configure Open vSwitch to instead delete (``evict'') a flow to make room for the new one:

    • Set the column to evict.
    • Send an OpenFlow 1.4+ ``table mod request'' to enable eviction for the flow table (e.g. ovs-ofctl -O OpenFlow14 mod-table br0 0 evict to enable eviction on flow table 0 of bridge br0).

    When a flow must be evicted due to overflow, the flow to evict is chosen through an approximation of the following algorithm. This algorithm is used regardless of how eviction was enabled:

    1. Divide the flows in the table into groups based on the values of the fields or subfields specified in the column, so that all of the flows in a given group have the same values for those fields. If a flow does not specify a given field, that field's value is treated as 0. If is empty, then all of the flows in the flow table are treated as a single group.
    2. Consider the flows in the largest group, that is, the group that contains the greatest number of flows. If two or more groups all have the same largest number of flows, consider the flows in all of those groups.
    3. If the flows under consideration have different importance values, eliminate from consideration any flows except those with the lowest importance. (``Importance,'' a 16-bit integer value attached to each flow, was introduced in OpenFlow 1.4. Flows inserted with older versions of OpenFlow always have an importance of 0.)
    4. Among the flows under consideration, choose the flow that expires soonest for eviction.

    The eviction process only considers flows that have an idle timeout or a hard timeout. That is, eviction never deletes permanent flows. (Permanent flows do count against .)

    If set, limits the number of flows that may be added to the table. Open vSwitch may limit the number of flows in a table for other reasons, e.g. due to hardware limitations or for resource availability or performance reasons.

    Controls the switch's behavior when an OpenFlow flow table modification request would add flows in excess of . The supported values are:

    refuse
    Refuse to add the flow or flows. This is also the default policy when is unset.
    evict
    Delete a flow chosen according to the algorithm described above.

    When is evict, this controls how flows are chosen for eviction when the flow table would otherwise exceed flows. Its value is a set of NXM fields or sub-fields, each of which takes one of the forms field[] or field[start..end], e.g. NXM_OF_IN_PORT[]. Please see meta-flow.h for a complete list of NXM field names.

    Open vSwitch ignores any invalid or unknown field specifications.

    When eviction is not enabled, via or an OpenFlow 1.4+ ``table mod,'' this column has no effect.

    This string set specifies which fields should be used for address prefix tracking. Prefix tracking allows the classifier to skip rules with longer than necessary prefixes, resulting in better wildcarding for datapath flows.

    Prefix tracking may be beneficial when a flow table contains matches on IP address fields with different prefix lengths. For example, when a flow table contains IP address matches on both full addresses and proper prefixes, the full address matches will typically cause the datapath flow to un-wildcard the whole address field (depending on flow entry priorities). In this case each packet with a different address gets handed to the userspace for flow processing and generates its own datapath flow. With prefix tracking enabled for the address field in question packets with addresses matching shorter prefixes would generate datapath flows where the irrelevant address bits are wildcarded, allowing the same datapath flow to handle all the packets within the prefix in question. In this case many userspace upcalls can be avoided and the overall performance can be better.

    This is a performance optimization only, so packets will receive the same treatment with or without prefix tracking.

    The supported fields are: tun_id, tun_src, tun_dst, tun_ipv6_src, tun_ipv6_dst, nw_src, nw_dst (or aliases ip_src and ip_dst), ipv6_src, and ipv6_dst. (Using this feature for tun_id would only make sense if the tunnel IDs have prefix structure similar to IP addresses.)

    By default, the prefixes=ip_dst,ip_src,ipv6_dst,ipv6_src are used on each flow table. This instructs the flow classifier to track the IPv4 and IPv6 destination and source addresses used by the rules in this specific flow table.

    The keyword none is recognized as an explicit override of the default values, causing no prefix fields to be tracked.

    To set the prefix fields, the flow table record needs to exist:

    ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=table0
    Creates a flow table record for the OpenFlow table number 0.
    ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src
    Enables prefix tracking for IPv4 source and destination address fields.

    There is a maximum number of fields that can be enabled for any one flow table. Currently this limit is 4.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Quality of Service (QoS) configuration for each Port that references it.

    The type of QoS to implement. The currently defined types are listed below:

    linux-htb
    Linux ``hierarchy token bucket'' classifier. See tc-htb(8) (also at http://linux.die.net/man/8/tc-htb) and the HTB manual (http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm) for information on how this classifier works and how to configure it.
    linux-hfsc
    Linux "Hierarchical Fair Service Curve" classifier. See http://linux-ip.net/articles/hfsc.en/ for information on how this classifier works.
    linux-sfq
    Linux ``Stochastic Fairness Queueing'' classifier. See tc-sfq(8) (also at http://linux.die.net/man/8/tc-sfq) for information on how this classifier works.
    linux-codel
    Linux ``Controlled Delay'' classifier. See tc-codel(8) (also at http://man7.org/linux/man-pages/man8/tc-codel.8.html) for information on how this classifier works.
    linux-fq_codel
    Linux ``Fair Queuing with Controlled Delay'' classifier. See tc-fq_codel(8) (also at http://man7.org/linux/man-pages/man8/tc-fq_codel.8.html) for information on how this classifier works.
    linux-netem
    Linux ``Network Emulator'' classifier. See tc-netem(8) (also at http://man7.org/linux/man-pages/man8/tc-netem.8.html) for information on how this classifier works.
    linux-noop
    Linux ``No operation.'' By default, Open vSwitch manages quality of service on all of its configured ports. This can be helpful, but sometimes administrators prefer to use other software to manage QoS. This prevents Open vSwitch from changing the QoS configuration for a port.
    egress-policer
    A DPDK egress policer algorithm using the DPDK rte_meter library. The rte_meter library provides an implementation which allows the metering and policing of traffic. The implementation in OVS essentially creates a single token bucket used to police traffic. It should be noted that when the rte_meter is configured as part of QoS there will be a performance overhead as the rte_meter itself will consume CPU cycles in order to police traffic. These CPU cycles ordinarily are used for packet proccessing. As such the drop in performance will be noticed in terms of overall aggregate traffic throughput.
    trtcm-policer
    A DPDK egress policer algorithm using RFC 4115's Two-Rate, Three-Color marker. It's a two-level hierarchical policer which first does a color-blind marking of the traffic at the queue level, followed by a color-aware marking at the port level. At the end traffic marked as Green or Yellow is forwarded, Red is dropped. For details on how traffic is marked, see RFC 4115. If the ``default queue'', 0, is not configured it's automatically created with the same other_config values as the physical port.

    A map from queue numbers to records. The supported range of queue numbers depend on . The queue numbers are the same as the queue_id used in OpenFlow in struct ofp_action_enqueue and other structures.

    Queue 0 is the ``default queue.'' It is used by OpenFlow output actions when no specific queue has been set. When no configuration for queue 0 is present, it is automatically configured as if a record with empty and columns had been specified. (Before version 1.6, Open vSwitch would leave queue 0 unconfigured in this case. With some queuing disciplines, this dropped all packets destined for the default queue.)

    The linux-htb and linux-hfsc classes support the following key-value pair:

    Maximum rate shared by all queued traffic, in bit/s. Optional. If not specified, for physical interfaces, the default is the link rate. For other interfaces or if the link rate cannot be determined, the default is currently 10 Gbps.

    egress-policer provides egress policing for userspace port types with DPDK. It has the following key-value pairs defined.

    The Committed Information Rate (CIR) is measured in bytes of IP packets per second, i.e. it includes the IP header, but not link specific (e.g. Ethernet) headers. This represents the bytes per second rate at which the token bucket will be updated. The cir value is calculated by (pps x packet data size). For example assuming a user wishes to limit a stream consisting of 64 byte packets to 1 million packets per second the CIR would be set to to to 46000000. This value can be broken into '1,000,000 x 46'. Where 1,000,000 is the policing rate for the number of packets per second and 46 represents the size of the packet data for a 64 bytes IP packet without 14 bytes Ethernet and 4 bytes FCS header. The Committed Burst Size (CBS) is measured in bytes and represents a token bucket. At a minimum this value should be be set to the expected largest size packet in the traffic stream. In practice larger values may be used to increase the size of the token bucket. If a packet can be transmitted then the cbs will be decremented by the number of bytes/tokens of the packet. If there are not enough tokens in the cbs bucket the packet will be dropped. The Excess Information Rate (EIR) is measured in bytes of IP packets per second, i.e. it includes the IP header, but not link specific (e.g. Ethernet) headers. This represents the bytes per second rate at which the token bucket will be updated. The eir value is calculated by (pps x packet data size). For example assuming a user wishes to limit a stream consisting of 64 byte packets to 1 million packets per second the EIR would be set to to to 46000000. This value can be broken into '1,000,000 x 46'. Where 1,000,000 is the policing rate for the number of packets per second and 46 represents the size of the packet data for a 64 bytes IP packet without 14 bytes Ethernet and 4 bytes FCS header. The Excess Burst Size (EBS) is measured in bytes and represents a token bucket. At a minimum this value should be be set to the expected largest size packet in the traffic stream. In practice larger values may be used to increase the size of the token bucket. If a packet can be transmitted then the ebs will be decremented by the number of bytes/tokens of the packet. If there are not enough tokens in the cbs bucket the packet might be dropped.

    The linux-sfq QoS supports the following key-value pairs:

    Number of seconds between consecutive perturbations in hashing algorithm. Different flows can end up in the same hash bucket causing unfairness. Perturbation's goal is to remove possible unfairness. The default and recommended value is 10. Too low a value is discouraged because each perturbation can cause packet reordering. Number of bytes linux-sfq QoS can dequeue in one turn in round-robin from one flow. The default and recommended value is equal to interface's MTU.

    The linux-netem QoS supports the following key-value pairs:

    Adds the chosen delay to the packets outgoing to chosen network interface. The latency value expressed in us. Maximum number of packets the qdisc may hold queued at a time. The default value is 1000. Adds an independent loss probability to the packets outgoing from the chosen network interface. Adds the provided jitter to the latency outgoing to the chosen network interface. The jitter value expressed in us.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A configuration for a port output queue, used in configuring Quality of Service (QoS) features. May be referenced by column in table.

    If set, Open vSwitch will mark all traffic egressing this with the given DSCP bits. Traffic egressing the default is only marked if it was explicitly selected as the at the time the packet was output. If unset, the DSCP bits of traffic egressing this will remain unchanged.

    linux-htb may use queue_ids less than 61440. It has the following key-value pairs defined.

    Minimum guaranteed bandwidth, in bit/s. Maximum allowed bandwidth, in bit/s. Optional. If specified, the queue's rate will not be allowed to exceed the specified value, even if excess bandwidth is available. If unspecified, defaults to no limit. Burst size, in bits. This is the maximum amount of ``credits'' that a queue can accumulate while it is idle. Optional. Details of the linux-htb implementation require a minimum burst size, so a too-small burst will be silently ignored. A queue with a smaller priority will receive all the excess bandwidth that it can use before a queue with a larger value receives any. Specific priority values are unimportant; only relative ordering matters. Defaults to 0 if unspecified.

    linux-hfsc may use queue_ids less than 61440. It has the following key-value pairs defined.

    Minimum guaranteed bandwidth, in bit/s. Maximum allowed bandwidth, in bit/s. Optional. If specified, the queue's rate will not be allowed to exceed the specified value, even if excess bandwidth is available. If unspecified, defaults to no limit.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A port mirror within a .

    A port mirror configures a bridge to send selected frames to special ``mirrored'' ports, in addition to their normal destinations. Mirroring traffic may also be referred to as SPAN or RSPAN, depending on how the mirrored traffic is sent.

    When a packet enters an Open vSwitch bridge, it becomes eligible for mirroring based on its ingress port and VLAN. As the packet travels through the flow tables, each time it is output to a port, it becomes eligible for mirroring based on the egress port and VLAN. In Open vSwitch 2.5 and later, mirroring occurs just after a packet first becomes eligible, using the packet as it exists at that point; in Open vSwitch 2.4 and earlier, mirroring occurs only after a packet has traversed all the flow tables, using the original packet as it entered the bridge. This makes a difference only when the flow table modifies the packet: in Open vSwitch 2.4, the modifications are never visible to mirrors, whereas in Open vSwitch 2.5 and later modifications made before the first output that makes it eligible for mirroring to a particular destination are visible.

    A packet that enters an Open vSwitch bridge is mirrored to a particular destination only once, even if it is eligible for multiple reasons. For example, a packet would be mirrored to a particular only once, even if it is selected for mirroring to that port by and in the same or different records.

    Arbitrary identifier for the .

    To be selected for mirroring, a given packet must enter or leave the bridge through a selected port and it must also be in one of the selected VLANs.

    If true, every packet arriving or departing on any port is selected for mirroring. Ports on which departing packets are selected for mirroring. Ports on which arriving packets are selected for mirroring. VLANs on which packets are selected for mirroring. An empty set selects packets on all VLANs.

    When set, only packets that match are selected for mirroring. Packets that do not match are ignored by thie mirror. The syntax is described in ovs-fields(7). However, the in_port field is not supported; should be used to limit the mirror to a source port.

    This filter is applied after , , , and .

    These columns are mutually exclusive. Exactly one of them must be nonempty.

    Output port for selected packets, if nonempty.

    Specifying a port for mirror output reserves that port exclusively for mirroring. No frames other than those selected for mirroring via this column will be forwarded to the port, and any frames received on the port will be discarded.

    The output port may be any kind of port supported by Open vSwitch. It may be, for example, a physical port (sometimes called SPAN) or a GRE tunnel.

    Output VLAN for selected packets, if nonempty.

    The frames will be sent out all ports that trunk , as well as any ports with implicit VLAN . When a mirrored frame is sent out a trunk port, the frame's VLAN tag will be set to , replacing any existing tag; when it is sent out an implicit VLAN port, the frame will not be tagged. This type of mirroring is sometimes called RSPAN.

    See the documentation for in the table for a list of destination MAC addresses which will not be mirrored to a VLAN to avoid confusing switches that interpret the protocols that they represent.

    Please note: Mirroring to a VLAN can disrupt a network that contains unmanaged switches. Consider an unmanaged physical switch with two ports: port 1, connected to an end host, and port 2, connected to an Open vSwitch configured to mirror received packets into VLAN 123 on port 2. Suppose that the end host sends a packet on port 1 that the physical switch forwards to port 2. The Open vSwitch forwards this packet to its destination and then reflects it back on port 2 in VLAN 123. This reflected packet causes the unmanaged physical switch to replace the MAC learning table entry, which correctly pointed to port 1, with one that incorrectly points to port 2. Afterward, the physical switch will direct packets destined for the end host to the Open vSwitch on port 2, instead of to the end host on port 1, disrupting connectivity. If mirroring to a VLAN is desired in this scenario, then the physical switch must be replaced by one that learns Ethernet addresses on a per-VLAN basis. In addition, learning should be disabled on the VLAN containing mirrored traffic. If this is not done then intermediate switches will learn the MAC address of each end host from the mirrored traffic. If packets being sent to that end host are also mirrored, then they will be dropped since the switch will attempt to send them out the input port. Disabling learning for the VLAN will cause the switch to correctly send the packet out all ports configured for that VLAN. If Open vSwitch is being used as an intermediate switch, learning can be disabled by adding the mirrored VLAN to in the appropriate table or tables.

    Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred.

    Maximum per-packet number of bytes to mirror.

    A mirrored packet with size larger than will be truncated in datapath to bytes before sending to the mirror output port. If omitted, packets are not truncated.

    Key-value pairs that report mirror statistics. The update period is controlled by in the Open_vSwitch table.

    Number of packets transmitted through this mirror. Number of bytes transmitted through this mirror.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    An OpenFlow controller.

    Open vSwitch supports two kinds of OpenFlow controllers. A bridge may have any number of each kind:

    Primary controllers

    This is the kind of controller envisioned by the OpenFlow specifications. Usually, a primary controller implements a network policy by taking charge of the switch's flow table.

    The column in the table applies to primary controllers.

    When multiple primary controllers are configured, Open vSwitch connects to all of them simultaneously. OpenFlow provides few facilities to allow multiple controllers to coordinate in interacting with a single switch, so more than one primary controller should be specified only if the controllers are themselves designed to coordinate with each other.

    Service controllers

    These kinds of OpenFlow controller connections are intended for occasional support and maintenance use, e.g. with ovs-ofctl. Usually a service controller connects only briefly to inspect or modify some of a switch's state.

    The column in the table does not apply to service controllers.

    By default, Open vSwitch treats controllers with active connection methods as primary controllers and those with passive connection methods as service controllers. Set this column to the desired type to override this default.

    Connection method for controller.

    The following active connection methods are currently supported:

    ssl:host[:port]

    The specified SSL/TLS port on the host at the given host, which can either be a DNS name (if built with unbound library) or an IP address. The column in the table must point to a valid SSL/TLS configuration when this form is used.

    If port is not specified, it defaults to 6653.

    SSL/TLS support is an optional feature that is not always built as part of Open vSwitch.

    tcp:host[:port]

    The specified TCP port on the host at the given host, which can either be a DNS name (if built with unbound library) or an IP address (IPv4 or IPv6). If host is an IPv6 address, wrap it in square brackets, e.g. tcp:[::1]:6653.

    If port is not specified, it defaults to 6653.

    The following passive connection methods are currently supported:

    pssl:[port][:host]

    Listens for SSL/TLS connections on the specified TCP port. If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address (either IPv4 or IPv6). If host is an IPv6 address, wrap it in square brackets, e.g. pssl:6653:[::1].

    If port is not specified, it defaults to 6653. If host is not specified then it listens only on IPv4 (but not IPv6) addresses. The column in the table must point to a valid SSL/TLS configuration when this form is used.

    If port is not specified, it currently to 6653.

    SSL/TLS support is an optional feature that is not always built as part of Open vSwitch.

    ptcp:[port][:host]

    Listens for connections on the specified TCP port. If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address (either IPv4 or IPv6). If host is an IPv6 address, wrap it in square brackets, e.g. ptcp:6653:[::1]. If host is not specified then it listens only on IPv4 addresses.

    If port is not specified, it defaults to 6653.

    When multiple controllers are configured for a single bridge, the values must be unique. Duplicate values yield unspecified results.

    If it is specified, this setting must be one of the following strings that describes how Open vSwitch contacts this OpenFlow controller over the network:

    in-band
    In this mode, this controller's OpenFlow traffic travels over the bridge associated with the controller. With this setting, Open vSwitch allows traffic to and from the controller regardless of the contents of the OpenFlow flow table. (Otherwise, Open vSwitch would never be able to connect to the controller, because it did not have a flow to enable it.) This is the most common connection mode because it is not necessary to maintain two independent networks.
    out-of-band
    In this mode, OpenFlow traffic uses a control network separate from the bridge associated with this controller, that is, the bridge does not use any of its own network devices to communicate with the controller. The control network must be configured separately, before or after ovs-vswitchd is started.

    If not specified, the default is implementation-specific.

    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. If Open vSwitch does not communicate with the controller for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, Open vSwitch assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes.

    OpenFlow switches send certain messages to controllers spontanenously, that is, not in response to any request from the controller. These messages are called ``asynchronous messages.'' These columns allow asynchronous messages to be limited or disabled to ensure the best use of network resources.

    The OpenFlow protocol enables asynchronous messages at time of connection establishment, which means that a controller can receive asynchronous messages, potentially many of them, even if it turns them off immediately after connecting. Set this column to false to change Open vSwitch behavior to disable, by default, all asynchronous messages. The controller can use the NXT_SET_ASYNC_CONFIG Nicira extension to OpenFlow to turn on any messages that it does want to receive, if any.

    A switch can forward packets to a controller over the OpenFlow protocol. Forwarding packets this way at too high a rate can overwhelm a controller, frustrate use of the OpenFlow connection for other purposes, increase the latency of flow setup, and use an unreasonable amount of bandwidth. Therefore, Open vSwitch supports limiting the rate of packet forwarding to a controller.

    There are two main reasons in OpenFlow for a packet to be sent to a controller: either the packet ``misses'' in the flow table, that is, there is no matching flow, or a flow table action says to send the packet to the controller. Open vSwitch limits the rate of each kind of packet separately at the configured rate. Therefore, the actual rate that packets are sent to the controller can be up to twice the configured rate, when packets are sent for both reasons.

    This feature is specific to forwarding packets over an OpenFlow connection. It is not general-purpose QoS. See the table for quality of service configuration, and in the table for ingress policing configuration.

    This sets the maximum size of the queue of packets that need to be sent to this OpenFlow controller. The value must be less than 512. If not specified the queue size is limited to the value set for the management controller in if present or 100 packets by default. Note: increasing the queue size might have a negative impact on latency.

    The maximum rate at which the switch will forward packets to the OpenFlow controller, in packets per second. If no value is specified, rate limiting is disabled.

    When a high rate triggers rate-limiting, Open vSwitch queues packets to the controller for each port and transmits them to the controller at the configured rate. This value limits the number of queued packets. Ports on a bridge share the packet queue fairly.

    This value has no effect unless is configured. The current default when this value is not specified is one-quarter of , meaning that queuing can delay forwarding a packet to the controller by up to 250 ms.

    These values report the effects of rate limiting. Their values are relative to establishment of the most recent OpenFlow connection, or since rate limiting was enabled, whichever happened more recently. Each consists of two values, one with TYPE replaced by miss for rate limiting flow table misses, and the other with TYPE replaced by action for rate limiting packets sent by OpenFlow actions.

    These statistics are reported only when controller rate limiting is enabled.

    Number of packets sent directly to the controller, without queuing, because the rate did not exceed the configured maximum. Number of packets added to the queue to send later. Number of packets added to the queue that were later dropped due to overflow. This value is less than or equal to . Number of packets currently queued. The other statistics increase monotonically, but this one fluctuates between 0 and the as conditions change.

    These values are considered only in in-band control mode (see ).

    When multiple controllers are configured on a single bridge, there should be only one set of unique values in these columns. If different values are set for these columns in different controllers, the effect is unspecified.

    The IP address to configure on the local port, e.g. 192.168.0.123. If this value is unset, then and are ignored. The IP netmask to configure on the local port, e.g. 255.255.255.0. If is set but this value is unset, then the default is chosen based on whether the IP address is class A, B, or C. The IP address of the gateway to configure on the local port, as a string, e.g. 192.168.0.1. Leave this column unset if this network has no gateway.
    true if currently connected to this controller, false otherwise.

    The level of authority this controller has on the associated bridge. Possible values are:

    other
    Allows the controller access to all OpenFlow features.
    master
    Equivalent to other, except that there may be at most one such controller at a time. If a given controller promotes itself to this role, ovs-vswitchd demotes any existing controller with the role to slave.
    slave
    Allows the controller read-only access to OpenFlow features. Attempts to modify the flow table will be rejected with an error. Such controllers do not receive OFPT_PACKET_IN or OFPT_FLOW_REMOVED messages, but they do receive OFPT_PORT_STATUS messages.
    A human-readable description of the last error on the connection to the controller; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the controller:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this controller last successfully connected to the switch (in seconds). Value is empty if controller has never successfully connected. The amount of time since this controller last disconnected from the switch (in seconds). Value is empty if controller has never disconnected.

    Additional configuration for a connection between the controller and the Open vSwitch.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the controller and the Open vSwitch. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a database connection to an Open vSwitch database (OVSDB) client.

    This table primarily configures the Open vSwitch database (ovsdb-server), not the Open vSwitch switch (ovs-vswitchd). The switch does read the table to determine what connections should be treated as in-band.

    The Open vSwitch database server can initiate and maintain active connections to remote clients. It can also listen for database connections.

    Connection method for managers.

    The following connection methods are currently supported:

    ssl:host[:port]

    The specified SSL/TLS port on the host at the given host, which can either be a DNS name (if built with unbound library) or an IP address. The column in the table must point to a valid SSL/TLS configuration when this form is used.

    If port is not specified, it defaults to 6640.

    SSL/TLS support is an optional feature that is not always built as part of Open vSwitch.

    tcp:host[:port]

    The specified TCP port on the host at the given host, which can either be a DNS name (if built with unbound library) or an IP address (IPv4 or IPv6). If host is an IPv6 address, wrap it in square brackets, e.g. tcp:[::1]:6640.

    If port is not specified, it defaults to 6640.

    pssl:[port][:host]

    Listens for SSL/TLS connections on the specified TCP port. Specify 0 for port to have the kernel automatically choose an available port. If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address (either IPv4 or IPv6 address). If host is an IPv6 address, wrap in square brackets, e.g. pssl:6640:[::1]. If host is not specified then it listens only on IPv4 (but not IPv6) addresses. The column in the table must point to a valid SSL/TLS configuration when this form is used.

    If port is not specified, it defaults to 6640.

    SSL/TLS support is an optional feature that is not always built as part of Open vSwitch.

    ptcp:[port][:host]

    Listens for connections on the specified TCP port. Specify 0 for port to have the kernel automatically choose an available port. If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address (either IPv4 or IPv6 address). If host is an IPv6 address, wrap it in square brackets, e.g. ptcp:6640:[::1]. If host is not specified then it listens only on IPv4 addresses.

    If port is not specified, it defaults to 6640.

    When multiple managers are configured, the values must be unique. Duplicate values yield unspecified results.

    If it is specified, this setting must be one of the following strings that describes how Open vSwitch contacts this OVSDB client over the network:

    in-band
    In this mode, this connection's traffic travels over a bridge managed by Open vSwitch. With this setting, Open vSwitch allows traffic to and from the client regardless of the contents of the OpenFlow flow table. (Otherwise, Open vSwitch would never be able to connect to the client, because it did not have a flow to enable it.) This is the most common connection mode because it is not necessary to maintain two independent networks.
    out-of-band
    In this mode, the client's traffic uses a control network separate from that managed by Open vSwitch, that is, Open vSwitch does not use any of its own network devices to communicate with the client. The control network must be configured separately, before or after ovs-vswitchd is started.

    If not specified, the default is implementation-specific.

    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to the client before sending an inactivity probe message. If Open vSwitch does not communicate with the client for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, Open vSwitch assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes.

    Key-value pair of is always updated. Other key-value pairs in the status columns may be updated depends on the type.

    When specifies a connection method that listens for inbound connections (e.g. ptcp: or punix:), both and may also be updated while the remaining key-value pairs are omitted.

    On the other hand, when specifies an outbound connection, all key-value pairs may be updated, except the above-mentioned two key-value pairs associated with inbound connection targets. They are omitted.

    true if currently connected to this manager, false otherwise. A human-readable description of the last error on the connection to the manager; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the manager:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this manager last successfully connected to the database (in seconds). Value is empty if manager has never successfully connected. The amount of time since this manager last disconnected from the database (in seconds). Value is empty if manager has never disconnected. Space-separated list of the names of OVSDB locks that the connection holds. Omitted if the connection does not hold any locks. Space-separated list of the names of OVSDB locks that the connection is currently waiting to acquire. Omitted if the connection is not waiting for any locks. Space-separated list of the names of OVSDB locks that the connection has had stolen by another OVSDB client. Omitted if no locks have been stolen from this connection. When specifies a connection method that listens for inbound connections (e.g. ptcp: or pssl:) and more than one connection is actually active, the value is the number of active connections. Otherwise, this key-value pair is omitted. When is ptcp: or pssl:, this is the TCP port on which the OVSDB server is listening. (This is particularly useful when specifies a port of 0, allowing the kernel to choose any available port.)

    Additional configuration for a connection between the manager and the Open vSwitch Database.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the manager and the Open vSwitch. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.
    A NetFlow target. NetFlow is a protocol that exports a number of details about terminating IP flows, such as the principals involved and duration. NetFlow targets in the form ip:port. The ip must be specified numerically, not as a DNS name. Engine ID to use in NetFlow messages. Defaults to datapath index if not specified. Engine type to use in NetFlow messages. Defaults to datapath index if not specified.

    The interval at which NetFlow records are sent for flows that are still active, in seconds. A value of 0 requests the default timeout (currently 600 seconds); a value of -1 disables active timeouts.

    The NetFlow passive timeout, for flows that become inactive, is not configurable. It will vary depending on the Open vSwitch version, the forms and contents of the OpenFlow flow tables, CPU and memory usage, and network activity. A typical passive timeout is about a second.

    If this column's value is false, the ingress and egress interface fields of NetFlow flow records are derived from OpenFlow port numbers. When it is true, the 7 most significant bits of these fields will be replaced by the least significant 7 bits of the engine id. This is useful because many NetFlow collectors do not expect multiple switches to be sending messages from the same host, so they do not store the engine information which could be used to disambiguate the traffic.

    When this option is enabled, a maximum of 508 ports are supported.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a datapath within .

    A datapath is responsible for providing the packet handling in Open vSwitch. There are two primary datapath implementations used by Open vSwitch: kernel and userspace. Kernel datapath implementations are available for Linux and Hyper-V, and selected as system in the column of the table. The userspace datapath is used by DPDK and AF-XDP, and is selected as netdev in the column of the table.

    A datapath of a particular type is shared by all the bridges that use that datapath. Thus, configurations applied to this table affect all bridges that use this datapath.

    Reports the version number of the Open vSwitch datapath in use. This allows management software to detect and report discrepancies between Open vSwitch userspace and datapath versions. (The column in the reports the Open vSwitch userspace version.) The version reported depends on the datapath in use:

    • When the kernel module included in the Open vSwitch source tree is used, this column reports the Open vSwitch version from which the module was taken.
    • When the kernel module that is part of the upstream Linux kernel is used, this column reports <unknown>.
    • When the datapath is built into the ovs-vswitchd binary, this column reports <built-in>. A built-in datapath is by definition the same version as the rest of the Open vSwitch userspace.
    • Other datapaths (such as the Hyper-V kernel datapath) currently report <unknown>.

    A version discrepancy between ovs-vswitchd and the datapath in use is not normally cause for alarm. The Open vSwitch kernel datapaths for Linux and Hyper-V, in particular, are designed for maximum inter-version compatibility: any userspace version works with with any kernel version. Some reasons do exist to insist on particular user/kernel pairings. First, newer kernel versions add new features, that can only be used by new-enough userspace, e.g. VXLAN tunneling requires certain minimal userspace and kernel versions. Second, as an extension to the first reason, some newer kernel versions add new features for enhancing performance that only new-enough userspace versions can take advantage of.

    Configuration for connection tracking zones. Each pair maps from a zone id to a configuration for that zone. Zone 0 applies to the default zone (ie, the one used if a zone is not specified in connection tracking-related OpenFlow matches and actions).

    The column reports a datapath's features. For the netdev datapath, the capabilities are fixed for a given version of Open vSwitch because this datapath is built into the ovs-vswitchd binary. The Linux kernel and Windows and other datapaths, which are external to OVS userspace, can vary in version and capabilities independently from ovs-vswitchd.

    Some of these features indicate whether higher-level Open vSwitch features are available. For example, OpenFlow features for connection-tracking are available only when is true. A controller that wishes to determine whether a feature is supported could, therefore, consult the relevant capabilities in this table. However, as a general rule, it is better for a controller to try to use the higher-level feature and use the result as an indication of support, since the low-level capabilities are more likely to shift over time than the high-level features that rely on them.

    Number of 802.1q VLAN headers supported by the datapath, as probed by the ovs-vswitchd slow path. If the datapath supports more VLAN headers than the slow path, this reports the slow path's limit. The value of in the table does not influence the number reported here. If this is true, then the datapath supports recirculation, specifically OVS_KEY_ATTR_RECIRC_ID. Recirculation enables higher performance for MPLS and active-active load balancing bonding modes. If this is true, then the datapath supports optimized balance-tcp bond mode. This capability replaces existing hash and recirc actions with new action lb_output and avoids recirculation of packet in datapath. It is supported only for balance-tcp bond mode in netdev datapath. The new action gives higher performance by using bond buckets instead of post recirculation flows for selection of member port from bond. By default this new action is disabled, however it can be enabled by setting in table.

    These capabilities are granular because Open vSwitch and its datapaths added support for connection tracking over several releases, with features added individually over that time.

    If true, datapath supports OVS_KEY_ATTR_CT_STATE, which indicates support for the bits in the OpenFlow ct_state field (see ovs-fields(7)) other than snat and dnat, which have a separate capability.

    If this is false, the datapath does not support connection-tracking at all and the remaining connection-tracking capabilities should all be false. In this case, Open vSwitch will reject flows that match on the ct_state field or use the ct action.

    If true, it means that the datapath supports the snat and dnat flags in the OpenFlow ct_state field. The ct_state capability must be true for this to make sense.

    If false, Open vSwitch will reject flows that match on the snat or dnat bits in ct_state or use nat in the ct action.

    If true, datapath supports OVS_KEY_ATTR_CT_ZONE. If false, Open vSwitch rejects flows that match on the ct_zone field or that specify a nonzero zone or a zone field on the ct action. If true, datapath supports OVS_KEY_ATTR_CT_MARK. If false, Open vSwitch rejects flows that match on the ct_mark field or that set ct_mark in the ct action. If true, datapath supports OVS_KEY_ATTR_CT_LABEL. If false, Open vSwitch rejects flows that match on the ct_label field or that set ct_label in the ct action.

    If true, the datapath supports matching the 5-tuple from the connection's original direction for IPv4 traffic. If false, Open vSwitch rejects flows that match on ct_nw_src or ct_nw_dst, that use the ct feature of the resubmit action, or the force keyword in the ct action. (The latter isn't tied to connection tracking support of original tuples in any technical way. They are conflated because all current datapaths implemented the two features at the same time.)

    If this and are both false, Open vSwitch rejects flows that match on ct_nw_proto, ct_tp_src, or ct_tp_dst.

    If true, the datapath supports matching the 5-tuple from the connection's original direction for IPv6 traffic. If false, Open vSwitch rejects flows that match on ct_ipv6_src or ct_ipv6_dst.
    True if the datapath supports masked data in OVS_ACTION_ATTR_SET actions. Masked data can improve performance by allowing megaflows to match on fewer fields. True if the datapath supports tnl_push and pop actions. This is a prerequisite for a datapath to support native tunneling. True if the datapath supports OVS_FLOW_ATTR_UFID. UFID support improves revalidation performance by transferring less data between the slow path and the datapath. True if the datapath supports OVS_ACTION_ATTR_TRUNC action. If false, the output action with packet truncation requires every packet to be sent to the Open vSwitch slow path, which is likely to make it too slow for mirroring traffic in bulk. True if the datapath supports OVS_KEY_ATTR_ND_EXTENSIONS to match on ICMPv6 "ND reserved" and "ND option type" header fields. If false, the datapath reports error if the feature is used.

    When Open vSwitch translates actions from OpenFlow into the datapath representation, some of the datapath actions may modify the packet or have other side effects that later datapath actions can't undo. The OpenFlow ct, meter, output with truncation, encap, decap, and dec_nsh_ttl actions fall into this category. Often, this is not a problem because nothing later on needs the original packet.

    Such actions can, however, occur in circumstances where the translation does require the original packet. For example, an OpenFlow output action might direct a packet to a patch port, which might in turn lead to a ct action that NATs the packet (which cannot be undone), and then afterward when control flow pops back across the patch port some other action might need to act on the original packet.

    Open vSwitch has two different ways to implement this ``save and restore'' via datapath actions. These capabilities indicate which one Open vSwitch will choose. When neither is available, Open vSwitch simply fails in situations that require this feature.

    True if the datapath supports OVS_ACTION_ATTR_CLONE action. This is the preferred option for saving and restoring packets, since it is intended for the purpose, but old datapaths do not support it. Open vSwitch will use it whenever it is available.

    (The OpenFlow clone action does not always yield a OVS_ACTION_ATTR_CLONE action. It only does so when the datapath supports it and the clone brackets actions that otherwise cannot be undone.)

    Maximum level of nesting allowed by OVS_ACTION_ATTR_SAMPLE action. Open vSwitch misuses this action for saving and restoring packets when the datapath supports more than 3 levels of nesting and OVS_ACTION_ATTR_CLONE is not available.
    True if the datapath's OVS_ACTION_ATTR_CT action implements the OVS_CT_ATTR_EVENTMASK attribute. When this is true, Open vSwitch uses the event mask feature to limit the kinds of events reported to conntrack update listeners. When Open vSwitch doesn't limit the event mask, listeners receive reports of numerous usually unimportant events, such as TCP state machine changes, which can waste CPU time. True if the datapath supports OVS_ACTION_ATTR_CT_CLEAR action. If false, the OpenFlow ct_clear action has no effect on the datapath. Highest supported dp_hash algorithm. This allows Open vSwitch to avoid requesting a packet hash that the datapath does not support. True if the datapath supports OVS_ACTION_ATTR_CHECK_PKT_LEN. If false, Open vSwitch implements the check_pkt_larger action by sending every packet through the Open vSwitch slow path, which is likely to make it too slow for handling traffic in bulk. True if the datapath supports OVS_CT_ATTR_TIMEOUT in the OVS_ACTION_ATTR_CT action. If false, Open vswitch cannot implement timeout policies based on connection tracking zones, as configured through the CT_Timeout_Policy table. True if the datapath supports OVS_ACTION_ATTR_DROP. If false, explicit drop action will not be sent to the datapath. True if the datapath supports all-zero SNAT. This is a special case if the src IP address is configured as all 0's, i.e., nat(src=0.0.0.0). In this case, when a source port collision is detected during the commit, the source port will be translated to an ephemeral port. If there is no collision, no SNAT is performed. True if the datapath supports CT flush OpenFlow Nicira extension called NXT_CT_FLUSH. The NXT_CT_FLUSH extensions allows to flush CT entries based on specified parameters. True if the datapath supports OVS_ACTION_ATTR_PSAMPLE. If false, local sampling will not be supported with the Linux kernel datapath.
    Default connection tracking zone limit that is applied to all zones that didn't specify the explicitly. If the limit is unspecified the default limit configuration for the datapath is left intact. The value 0 means unlimited. The overall purpose of these columns is described under Common Columns at the beginning of this document.
    Connection tracking zone configuration Connection tracking timeout policy for this zone. If a timeout policy is not specified, it defaults to the timeout policy in the system. Connection tracking limit for this zone. If the limit is unspecified the will be used. The value 0 means unlimited. The overall purpose of these columns is described under Common Columns at the beginning of this document.
    Connection tracking timeout policy configuration The timeouts column contains key-value pairs used to configure connection tracking timeouts in a datapath. Key-value pairs that are not supported by a datapath are ignored. The timeout value is in seconds. The timeout for the connection after the first TCP SYN packet has been seen by conntrack. The timeout of the connection after the first TCP SYN-ACK packet has been seen by conntrack. The timeout of the connection after the connection has been fully established. The timeout of the connection after the first TCP FIN packet has been seen by conntrack. The timeout of the connection after the first TCP ACK packet has been seen after it receives TCP FIN packet. This timeout is only supported by the Linux kernel datapath. The timeout of the connection after TCP FIN packets have been seen by conntrack from both directions. This timeout is only supported by the Linux kernel datapath. The timeout of the connection after conntrack has seen the TCP ACK packet for the second TCP FIN packet. The timeout of the connection after the first TCP RST packet has been seen by conntrack. The timeout of the connection when only a TCP SYN packet has been seen by conntrack from both directions (simultaneous open). This timeout is only supported by the Linux kernel datapath. The timeout of the connection when it exceeds the maximum number of retransmissions. This timeout is only supported by the Linux kernel datapath. The timeout of the connection when non-SYN packets create an established connection in TCP loose tracking mode. This timeout is only supported by the Linux kernel datapath. The timeout of the connection after the first UDP packet has been seen by conntrack. This timeout is only supported by the userspace datapath. The timeout of the connection when conntrack only seen UDP packet from the source host, but the destination host has never sent one back. The timeout of the connection when UDP packets have been seen in both directions. The timeout of the connection after the first ICMP packet has been seen by conntrack. The timeout of the connection when ICMP packets have been seen in both direction. This timeout is only supported by the userspace datapath. The overall purpose of these columns is described under Common Columns at the beginning of this document.
    SSL/TLS configuration for an Open_vSwitch. Name of a PEM file containing the private key used as the switch's identity for SSL/TLS connections to the controller. Name of a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the switch's private key, identifying a trustworthy switch. Name of a PEM file containing the CA certificate used to verify that the switch is connected to a trustworthy controller. If set to true, then Open vSwitch will attempt to obtain the CA certificate from the controller on its first SSL/TLS connection and save it to the named PEM file. If it is successful, it will immediately drop the connection and reconnect, and from then on all SSL/TLS connections must be authenticated by a certificate signed by the CA certificate thus obtained. This option exposes the SSL/TLS connection to a man-in-the-middle attack obtaining the initial CA certificate. It may still be useful for bootstrapping. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A set of sFlow(R) targets. sFlow is a protocol for remote monitoring of switches.

    Determines the agent address, that is, the IP address reported to collectors as the source of the sFlow data. It may be an IP address or the name of a network device. In the latter case, the network device's IP address is used,

    If not specified, the agent device is figured from the first target address and the routing table. If the routing table does not contain a route to the target, the IP address defaults to the in the collector's .

    If an agent IP address cannot be determined, sFlow is disabled.

    Number of bytes of a sampled packet to send to the collector. If not specified, the default is 128 bytes. Polling rate in seconds to send port statistics to the collector. If not specified, defaults to 30 seconds. Rate at which packets should be sampled and sent to the collector. If not specified, defaults to 400, which means one out of 400 packets, on average, will be sent to the collector. sFlow targets in the form ip:port. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for sending packets to IPFIX collectors.

    IPFIX is a protocol that exports a number of details about flows. The IPFIX implementation in Open vSwitch samples packets at a configurable rate, extracts flow information from those packets, optionally caches and aggregates the flow information, and sends the result to one or more collectors.

    IPFIX in Open vSwitch can be configured two different ways:

    • With per-bridge sampling, Open vSwitch performs IPFIX sampling automatically on all packets that pass through a bridge. To configure per-bridge sampling, create an record and point a table's column to it. The table is not used for per-bridge sampling.
    • With flow-based sampling, sample actions in the OpenFlow flow table drive IPFIX sampling. See ovs-actions(7) for a description of the sample action.

      Flow-based sampling also requires database configuration: create a record that describes the IPFIX configuration and a record that points to the whose flow table holds the sample actions and to record. The in the table is not used for flow-based sampling.

    IPFIX target collectors in the form ip:port. The maximum period in seconds for which an IPFIX flow record is cached and aggregated before being sent. If not specified, defaults to 0. If 0, caching is disabled. The maximum number of IPFIX flow records that can be cached at a time. If not specified, defaults to 0. If 0, caching is disabled.

    Interval (in seconds) for sending IPFIX exporting process statistics according to IETF RFC 5101 Section 4.3.

    Default value is 600

    Interval (in seconds) for sending IPFIX Template information for each Observation Domain ID.

    Default value is 600

    Set to true to enable sampling and reporting tunnel header 7-tuples in IPFIX flow records. Tunnel sampling is enabled by default.

    The following enterprise entities report the sampled tunnel info:

    tunnelType:

    ID: 891, and enterprise ID 6876 (VMware).

    type: unsigned 8-bit integer.

    data type semantics: identifier.

    description: Identifier of the layer 2 network overlay network encapsulation type: 0x01 VxLAN, 0x02 GRE, 0x07 GENEVE.

    tunnelKey:

    ID: 892, and enterprise ID 6876 (VMware).

    type: variable-length octetarray.

    data type semantics: identifier.

    description: Key which is used for identifying an individual traffic flow within a VxLAN (24-bit VNI), GENEVE (24-bit VNI), or GRE (32-bit key) tunnel. The key is encoded in this octetarray as a 3- or 4-byte integer ID in network byte order.

    tunnelSourceIPv4Address:

    ID: 893, and enterprise ID 6876 (VMware).

    type: unsigned 32-bit integer.

    data type semantics: identifier.

    description: The IPv4 source address in the tunnel IP packet header.

    tunnelDestinationIPv4Address:

    ID: 894, and enterprise ID 6876 (VMware).

    type: unsigned 32-bit integer.

    data type semantics: identifier.

    description: The IPv4 destination address in the tunnel IP packet header.

    tunnelProtocolIdentifier:

    ID: 895, and enterprise ID 6876 (VMware).

    type: unsigned 8-bit integer.

    data type semantics: identifier.

    description: The value of the protocol number in the tunnel IP packet header. The protocol number identifies the tunnel IP packet payload type.

    tunnelSourceTransportPort:

    ID: 896, and enterprise ID 6876 (VMware).

    type: unsigned 16-bit integer.

    data type semantics: identifier.

    description: The source port identifier in the tunnel transport header. For the transport protocols UDP, TCP, and SCTP, this is the source port number given in the respective header.

    tunnelDestinationTransportPort:

    ID: 897, and enterprise ID 6876 (VMware).

    type: unsigned 16-bit integer.

    data type semantics: identifier.

    description: The destination port identifier in the tunnel transport header. For the transport protocols UDP, TCP, and SCTP, this is the destination port number given in the respective header.

    Before Open vSwitch 2.5.90, was only supported with per-bridge sampling, and ignored otherwise. Open vSwitch 2.5.90 and later support for per-bridge and per-flow sampling.

    A string that accompanies each IPFIX flow record. Its intended use is for the ``virtual observation ID,'' an identifier of a virtual observation point that is locally unique in a virtual network. It describes a location in the virtual network where IP packets can be observed. The maximum length is 254 bytes. If not specified, the field is omitted from the IPFIX flow record.

    The following enterprise entity reports the specified virtual observation ID:

    virtualObsID:

    ID: 898, and enterprise ID 6876 (VMware).

    type: variable-length string.

    data type semantics: identifier.

    description: A virtual observation domain ID that is locally unique in a virtual network.

    This feature was introduced in Open vSwitch 2.5.90.

    These values affect only per-bridge sampling. See above for a description of the differences between per-bridge and flow-based sampling.

    The rate at which packets should be sampled and sent to each target collector. If not specified, defaults to 400, which means one out of 400 packets, on average, will be sent to each target collector. The IPFIX Observation Domain ID sent in each IPFIX packet. If not specified, defaults to 0. The IPFIX Observation Point ID sent in each IPFIX flow record. If not specified, defaults to 0. By default, Open vSwitch samples and reports flows at bridge port input in IPFIX flow records. Set this column to false to disable input sampling. By default, Open vSwitch samples and reports flows at bridge port output in IPFIX flow records. Set this column to false to disable output sampling.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A set of IPFIX or local sampling collectors of packet samples generated by OpenFlow sample actions.

    If the column ipfix contains a reference to a valid IPFIX entry, samples will be emitted via IPFIX. This mechanism is known as flow-based IPFIX sampling, as opposed to bridge-based sampling (see the table for a description of the two forms).

    If the column local_group_id contains an integer and the running datapath supports local sample emission, packets will be sent to some local sample collector. Samples will contain the group number specified by local_group_id which helps identify their source as well as a 64-bit cookie result from the concatenation of the observation_domain_id an the observation_point_id in network byte order. The way the sample is emitted and made available for local collectors is datapath-specific. Currently only Linux kernel datapath supports local sampling which is implemented by sending the packet to the psample netlink multicast group.

    Note: both local_group_id and ipfix can be configured simultaneously.

    The ID of this collector set, unique among the bridge's collector sets, to be used as the collector_set_id in OpenFlow sample actions. The bridge into which OpenFlow sample actions can be added to send packet samples to this set of IPFIX collectors. Configuration of the set of IPFIX collectors to send one flow record per sampled packet to. Configuration of the sample group id to be used in local sampling. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Auto Attach configuration within a bridge. The IETF Auto-Attach SPBM draft standard describes a compact method of using IEEE 802.1AB Link Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq Shortest Path Bridging (SPB) network to automatically attach network devices to individual services in a SPB network. The intent here is to allow network applications and devices using OVS to be able to easily take advantage of features offered by industry standard SPB networks.

    Auto Attach (AA) uses LLDP to communicate between a directly connected Auto Attach Client (AAC) and Auto Attach Server (AAS). The LLDP protocol is extended to add two new Type-Length-Value tuples (TLVs). The first new TLV supports the ongoing discovery of directly connected AA correspondents. Auto Attach operates by regularly transmitting AA discovery TLVs between the AA client and AA server. By exchanging these discovery messages, both the AAC and AAS learn the system name and system description of their peer. In the OVS context, OVS operates as the AA client and the AA server resides on a switch at the edge of the SPB network.

    Once AA discovery has been completed the AAC then uses the second new TLV to deliver identifier mappings from the AAC to the AAS. A primary feature of Auto Attach is to facilitate the mapping of VLANs defined outside the SPB network onto service ids (ISIDs) defined within the SPM network. By doing so individual external VLANs can be mapped onto specific SPB network services. These VLAN id to ISID mappings can be configured and managed locally using new options added to the ovs-vsctl command.

    The Auto Attach OVS feature does not provide a full implementation of the LLDP protocol. Support for the mandatory TLVs as defined by the LLDP standard and support for the AA TLV extensions is provided. LLDP protocol support in OVS can be enabled or disabled on a port by port basis. LLDP support is disabled by default.

    The system_name string is exported in LLDP messages. It should uniquely identify the bridge in the network. The system_description string is exported in LLDP messages. It should describe the type of software and hardware. A mapping from SPB network Individual Service Identifier (ISID) to VLAN id.
    openvswitch-3.7.0~git20260211.8c6ebf8/vtep/000077500000000000000000000000001514270232600177365ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/vtep/.gitignore000066400000000000000000000002211514270232600217210ustar00rootroot00000000000000/Makefile /Makefile.in /ovs-vtep /vtep-ctl /vtep-ctl.8 /vtep-idl.c /vtep-idl.h /vtep-idl.ovsidl /vtep.5 /vtep.gv /vtep.ovsschema.stamp /vtep.pic openvswitch-3.7.0~git20260211.8c6ebf8/vtep/automake.mk000066400000000000000000000041451514270232600221010ustar00rootroot00000000000000# vtep IDL OVSIDL_BUILT += \ vtep/vtep-idl.c \ vtep/vtep-idl.h \ vtep/vtep-idl.ovsidl EXTRA_DIST += vtep/vtep-idl.ann VTEP_IDL_FILES = \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep-idl.ann vtep/vtep-idl.ovsidl: $(VTEP_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(VTEP_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ # libvtep lib_LTLIBRARIES += vtep/libvtep.la vtep_libvtep_la_LDFLAGS = \ $(OVS_LTINFO) \ -Wl,--version-script=$(top_builddir)/vtep/libvtep.sym \ $(AM_LDFLAGS) nodist_vtep_libvtep_la_SOURCES = \ vtep/vtep-idl.c \ vtep/vtep-idl.h bin_PROGRAMS += \ vtep/vtep-ctl MAN_ROOTS += \ vtep/vtep-ctl.8.in CLEANFILES += \ vtep/vtep-ctl.8 man_MANS += \ vtep/vtep-ctl.8 vtep_vtep_ctl_SOURCES = vtep/vtep-ctl.c vtep_vtep_ctl_LDADD = vtep/libvtep.la lib/libopenvswitch.la # ovs-vtep scripts_SCRIPTS += \ vtep/ovs-vtep EXTRA_DIST += vtep/ovs-vtep.in CLEANFILES += vtep/ovs-vtep FLAKE8_PYFILES += vtep/ovs-vtep # VTEP schema and IDL EXTRA_DIST += vtep/vtep.ovsschema pkgdata_DATA += vtep/vtep.ovsschema # VTEP E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_DOT vtep/vtep.gv: ovsdb/ovsdb-dot.in vtep/vtep.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vtep/vtep.ovsschema > $@ vtep/vtep.pic: vtep/vtep.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < vtep/vtep.gv | $(PYTHON3) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ VTEP_PIC = vtep/vtep.pic VTEP_DOT_DIAGRAM_ARG = --er-diagram=$(VTEP_PIC) CLEANFILES += vtep/vtep.gv vtep/vtep.pic endif # VTEP schema documentation EXTRA_DIST += vtep/vtep.xml CLEANFILES += vtep/vtep.5 man_MANS += vtep/vtep.5 vtep/vtep.5: \ ovsdb/ovsdb-doc vtep/vtep.xml $(srcdir)/vtep/vtep.ovsschema $(VTEP_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VTEP_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep.xml > $@.tmp && \ mv $@.tmp $@ # Version checking for vtep.ovsschema. ALL_LOCAL += vtep/vtep.ovsschema.stamp vtep/vtep.ovsschema.stamp: vtep/vtep.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += vtep/vtep.ovsschema.stamp openvswitch-3.7.0~git20260211.8c6ebf8/vtep/libvtep.sym.in000066400000000000000000000000551514270232600225420ustar00rootroot00000000000000libvtep_@LT_CURRENT@ { global: *; }; openvswitch-3.7.0~git20260211.8c6ebf8/vtep/ovs-vtep.in000077500000000000000000000663411514270232600220660ustar00rootroot00000000000000#! @PYTHON3@ # Copyright (C) 2013 Nicira, Inc. All Rights Reserved. # # 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. # Limitations: # - Doesn't support multicast other than "unknown-dst" import argparse import re import shlex import subprocess import sys import time import ovs.daemon import ovs.dirs import ovs.unixctl.server import ovs.util import ovs.vlog VERSION = "0.99" root_prefix = "" __pychecker__ = 'no-reuseattr' # Remove in pychecker >= 0.8.19. vlog = ovs.vlog.Vlog("ovs-vtep") verbose_args = [] exiting = False ps_name = "" ps_type = "" Tunnel_Ip = "" Lswitches = {} Bindings = {} ls_count = 0 tun_id = 0 bfd_bridge = "vtep_bfd" bfd_ref = {} def call_prog(prog, args_list): cmd = [prog] + verbose_args + ["-vconsole:off"] + args_list creationFlags = 0 if sys.platform == 'win32': creationFlags = 0x08000000 # CREATE_NO_WINDOW output = subprocess.Popen(cmd, stdout=subprocess.PIPE, creationflags=creationFlags).communicate() if len(output) == 0 or output[0] is None: output = "" else: output = output[0].decode().strip() return output def ovs_vsctl(args): return call_prog("ovs-vsctl", shlex.split(args)) def ovs_ofctl(args): return call_prog("ovs-ofctl", shlex.split(args)) def vtep_ctl(args): return call_prog("vtep-ctl", shlex.split(args)) def unixctl_exit(conn, unused_argv, unused_aux): global exiting exiting = True conn.reply(None) class Logical_Switch(object): def __init__(self, ls_name, ps_name): global ls_count self.name = ls_name ls_count += 1 self.short_name = ps_name + "_vtep_ls" + str(ls_count) vlog.info("creating lswitch %s (%s)" % (self.name, self.short_name)) self.ports = {} self.tunnels = {} self.local_macs = set() self.remote_macs = {} self.unknown_dsts = set() self.setup_ls() self.replication_mode = "service_node" def __del__(self): vlog.info("destroying lswitch %s" % self.name) def setup_ls(self): if ps_type: ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s" % (self.short_name, self.short_name, ps_type)) else: ovs_vsctl("--may-exist add-br %s" % self.short_name) ovs_vsctl("br-set-external-id %s vtep_logical_switch true" % self.short_name) ovs_vsctl("br-set-external-id %s logical_switch_name %s" % (self.short_name, self.name)) vtep_ctl("clear-local-macs %s" % self.name) vtep_ctl("add-mcast-local %s unknown-dst %s" % (self.name, Tunnel_Ip)) ovs_ofctl("del-flows %s" % self.short_name) ovs_ofctl("add-flow %s priority=0,action=drop" % self.short_name) def cleanup_ls(self): for port_no, tun_name, remote_ip in self.tunnels.values(): del_bfd(remote_ip) def update_flood(self): flood_ports = list(self.ports.values()) # Traffic flowing from one 'unknown-dst' should not be flooded to # port belonging to another 'unknown-dst'. for tunnel in self.unknown_dsts: port_no = self.tunnels[tunnel][0] ovs_ofctl("add-flow %s table=1,priority=1,in_port=%s,action=%s" % (self.short_name, port_no, ",".join(flood_ports))) # Traffic coming from a VTEP physical port should always be flooded to # all the other physical ports that belong to that VTEP device and # this logical switch. If the replication mode is service node then # send to one unknown_dst node (the first one here); else we assume the # replication mode is source node and we send the packet to all # unknown_dst nodes. for tunnel in self.unknown_dsts: port_no = self.tunnels[tunnel][0] flood_ports.append(port_no) if self.replication_mode == "service_node": break ovs_ofctl("add-flow %s table=1,priority=0,action=%s" % (self.short_name, ",".join(flood_ports))) def add_lbinding(self, lbinding): vlog.info("adding %s binding to %s" % (lbinding, self.name)) port_no = ovs_vsctl("get Interface %s ofport" % lbinding) self.ports[lbinding] = port_no ovs_ofctl("add-flow %s in_port=%s,action=learn(table=1," "priority=1000,idle_timeout=15,cookie=0x5000," "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," "output:NXM_OF_IN_PORT[]),resubmit(,1)" % (self.short_name, port_no)) self.update_flood() def del_lbinding(self, lbinding): vlog.info("removing %s binding from %s" % (lbinding, self.name)) port_no = self.ports[lbinding] ovs_ofctl("del-flows %s in_port=%s" % (self.short_name, port_no)) del self.ports[lbinding] self.update_flood() def add_tunnel(self, tunnel, tunnel_key): global tun_id vlog.info("adding tunnel %s" % tunnel) encap, ip = tunnel.split("/") if encap != "vxlan_over_ipv4": vlog.warn("unsupported tunnel format %s" % encap) return tun_id += 1 tun_name = "vx" + str(tun_id) ovs_vsctl("add-port %s %s -- set Interface %s type=vxlan " "options:key=%s options:remote_ip=%s" % (self.short_name, tun_name, tun_name, tunnel_key, ip)) for i in range(10): port_no = ovs_vsctl("get Interface %s ofport" % tun_name) if port_no != "-1": break elif i == 9: vlog.warn("couldn't create tunnel %s" % tunnel) ovs_vsctl("del-port %s %s" % (self.short_name, tun_name)) return # Give the system a moment to allocate the port number time.sleep(0.5) self.tunnels[tunnel] = (port_no, tun_name, ip) add_bfd(ip) ovs_ofctl("add-flow %s table=0,priority=1000,in_port=%s," "actions=resubmit(,1)" % (self.short_name, port_no)) def del_tunnel(self, tunnel): vlog.info("removing tunnel %s" % tunnel) port_no, tun_name, remote_ip = self.tunnels[tunnel] ovs_ofctl("del-flows %s table=0,in_port=%s" % (self.short_name, port_no)) ovs_vsctl("del-port %s %s" % (self.short_name, tun_name)) del_bfd(remote_ip) del self.tunnels[tunnel] def update_local_macs(self): flows = ovs_ofctl("dump-flows %s cookie=0x5000/-1,table=1" % self.short_name).splitlines() macs = set() for f in flows: mac = re.split(r'.*dl_dst=(.*) .*', f) if len(mac) == 3: macs.add(mac[1]) for mac in macs.difference(self.local_macs): vlog.info("adding local ucast %s to %s" % (mac, self.name)) vtep_ctl("add-ucast-local %s %s %s" % (self.name, mac, Tunnel_Ip)) for mac in self.local_macs.difference(macs): vlog.info("removing local ucast %s from %s" % (mac, self.name)) vtep_ctl("del-ucast-local %s %s" % (self.name, mac)) self.local_macs = macs def add_remote_mac(self, mac, tunnel): port_no = self.tunnels.get(tunnel, (0, ""))[0] if not port_no: return ovs_ofctl("add-flow %s table=1,priority=1000,dl_dst=%s,action=%s" % (self.short_name, mac, port_no)) def del_remote_mac(self, mac): ovs_ofctl("del-flows %s table=1,dl_dst=%s" % (self.short_name, mac)) def update_remote_macs(self): remote_macs = {} unknown_dsts = set() tunnels = set() parse_ucast = True column = vtep_ctl("--columns=tunnel_key find logical_switch " "name=%s" % self.name) tunnel_key = column.partition(":")[2].strip() if tunnel_key and isinstance(eval(tunnel_key), int): vlog.info("update_remote_macs: using tunnel key %s in %s" % (tunnel_key, self.name)) else: vlog.info("Invalid tunnel key %s in %s post VTEP DB requery" % (tunnel_key, self.name)) return mac_list = vtep_ctl("list-remote-macs %s" % self.name).splitlines() for line in mac_list: if (line.find("mcast-mac-remote") != -1): parse_ucast = False continue entry = re.split(r' (.*) -> (.*)', line) if len(entry) != 4: continue if parse_ucast: remote_macs[entry[1]] = entry[2] else: if entry[1] != "unknown-dst": continue unknown_dsts.add(entry[2]) tunnels.add(entry[2]) old_tunnels = set(self.tunnels.keys()) for tunnel in tunnels.difference(old_tunnels): self.add_tunnel(tunnel, tunnel_key) for tunnel in old_tunnels.difference(tunnels): self.del_tunnel(tunnel) for mac in remote_macs.keys(): if (self.remote_macs.get(mac) != remote_macs[mac]): self.add_remote_mac(mac, remote_macs[mac]) for mac in self.remote_macs.keys(): if mac not in remote_macs: self.del_remote_mac(mac) self.remote_macs = remote_macs replication_mode = vtep_ctl("get logical_switch %s replication_mode" % self.name) # Replication mode is an optional column and if it is not set, # replication mode defaults to service_node. if replication_mode == "[]": replication_mode = "service_node" # If the logical switch level replication mode has changed then # update to that value. update_flood_set = False if replication_mode != self.replication_mode: self.replication_mode = replication_mode vlog.info("%s replication mode changed to %s" % (self.name, self.replication_mode)) update_flood_set = True if (self.unknown_dsts != unknown_dsts): self.unknown_dsts = unknown_dsts update_flood_set = True # If either the replication mode has changed or the unknown # destinations set has changed, update the flooding decision. if update_flood_set is True: self.update_flood() def update_stats(self): # Map Open_vSwitch's "interface:statistics" to columns of # vtep's logical_binding_stats. Since we are using the 'interface' from # the logical switch to collect stats, packets transmitted from it # is received in the physical switch and vice versa. stats_map = {'tx_packets': 'packets_to_local', 'tx_bytes': 'bytes_to_local', 'rx_packets': 'packets_from_local', 'rx_bytes': 'bytes_from_local'} # Go through all the logical switch's interfaces that end with "-l" # and copy the statistics to logical_binding_stats. for interface in self.ports.keys(): if not interface.endswith("-l"): continue # Physical ports can have a '-' as part of its name. vlan, remainder = interface.split("-", 1) pp_name, logical = remainder.rsplit("-", 1) uuid = vtep_ctl("get physical_port %s vlan_stats:%s" % (pp_name, vlan)) if not uuid: continue for mapfrom, mapto in stats_map.items(): value = ovs_vsctl("get interface %s statistics:%s" % (interface, mapfrom)).strip('"') vtep_ctl("set logical_binding_stats %s %s=%s" % (uuid, mapto, value)) def run(self): self.update_local_macs() self.update_remote_macs() self.update_stats() def get_vtep_tunnel(remote_ip): # Get the physical_locator record for the local tunnel end point. column = vtep_ctl("--columns=_uuid find physical_locator " "dst_ip=%s" % Tunnel_Ip) local = column.partition(":")[2].strip() if not local: return (None, None, None) # Get the physical_locator record for the remote tunnel end point. column = vtep_ctl("--columns=_uuid find physical_locator " "dst_ip=%s" % remote_ip) remote = column.partition(":")[2].strip() if not remote: return (None, None, None) column = vtep_ctl("--columns=_uuid find tunnel " "local=%s remote=%s" % (local, remote)) tunnel = column.partition(":")[2].strip() return (local, remote, tunnel) def create_vtep_tunnel(remote_ip): local, remote, tunnel = get_vtep_tunnel(remote_ip) if not local or not remote: return None if not tunnel: vlog.info("creating tunnel record in vtep for remote_ip:%s" % remote_ip) tunnel = vtep_ctl("add physical_switch %s tunnels @tun -- " "--id=@tun create Tunnel local=%s remote=%s" % (ps_name, local, remote)) return tunnel def destroy_vtep_tunnel(remote_ip): local, remote, tunnel = get_vtep_tunnel(remote_ip) if tunnel: vlog.info("destroying tunnel record in vtep for remote_ip:%s" % remote_ip) vtep_ctl("remove physical_switch %s tunnels %s " "-- --if-exists destroy tunnel %s" % (ps_name, tunnel, tunnel)) def add_bfd(remote_ip): # The VTEP emulator creates one OVS bridge for every logical switch. # Multiple logical switches can have multiple OVS tunnels to the # same machine (with different tunnel ids). But VTEP schema expects # a single BFD session between two physical locators. Therefore # create a separate bridge ('bfd_bridge') and create a single OVS tunnel # between two phsyical locators (using reference counter). if remote_ip in bfd_ref: bfd_ref[remote_ip] += 1 return vlog.info("adding bfd tunnel for remote_ip:%s" % remote_ip) port_name = "bfd" + remote_ip # Don't enable BFD yet. Enabling or disabling BFD is based on # the controller setting a value in VTEP DB's tunnel record. ovs_vsctl("--may-exist add-port %s %s " " -- set Interface %s type=vxlan options:remote_ip=%s" % (bfd_bridge, port_name, port_name, remote_ip)) bfd_ref[remote_ip] = 1 # Ideally, we should create a 'tunnel' record in the VTEP DB here. # To create a 'tunnel' record, we need 2 entries in 'physical_locator' # table (one for local and one for remote). But, 'physical_locator' # can be created/destroyed asynchronously when the remote controller # adds/removes entries in Ucast_Macs_Remote table. To prevent race # conditions, pass the responsibility of creating a 'tunnel' record # to run_bfd() which runs more often. def del_bfd(remote_ip): if remote_ip in bfd_ref: if bfd_ref[remote_ip] == 1: port_name = "bfd" + remote_ip vlog.info("deleting bfd tunnel for remote_ip:%s" % remote_ip) ovs_vsctl("--if-exists del-port %s" % port_name) destroy_vtep_tunnel(remote_ip) del bfd_ref[remote_ip] else: bfd_ref[remote_ip] -= 1 def run_bfd(): bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split() for port in bfd_ports: remote_ip = ovs_vsctl("get interface %s options:remote_ip" % port) tunnel = create_vtep_tunnel(remote_ip) if not tunnel: continue bfd_params_default = {'bfd_params:enable': 'false', 'bfd_params:min_rx': 1000, 'bfd_params:min_tx': 100, 'bfd_params:decay_min_rx': 0, 'bfd_params:cpath_down': 'false', 'bfd_params:check_tnl_key': 'false'} bfd_params_values = {} for key, default in bfd_params_default.items(): column = vtep_ctl("--if-exists get tunnel %s %s" % (tunnel, key)) if not column: bfd_params_values[key] = default else: bfd_params_values[key] = column for key, value in bfd_params_values.items(): new_key = key.replace('_params', '') ovs_vsctl("set interface %s %s=%s" % (port, new_key, value)) bfd_status = ['bfd_status:state', 'bfd_status:forwarding', 'bfd_status:diagnostic', 'bfd_status:remote_state', 'bfd_status:remote_diagnostic'] for key in bfd_status: value = ovs_vsctl("--if-exists get interface %s %s" % (port, key)) if value: vtep_ctl("set tunnel %s %s=%s" % (tunnel, key, value)) else: new_key = key.replace('bfd_status:', '') vtep_ctl("remove tunnel %s bfd_status %s" % (tunnel, new_key)) vtep_ctl("set tunnel %s bfd_status:enabled=%s" % (tunnel, bfd_params_values['bfd_params:enable'])) # Add the defaults as described in VTEP schema to make it explicit. bfd_lconf_default = {'bfd_config_local:bfd_dst_ip': '169.254.1.0', 'bfd_config_local:bfd_dst_mac': '00:23:20:00:00:01'} for key, value in bfd_lconf_default.items(): vtep_ctl("set tunnel %s %s=%s" % (tunnel, key, value)) # bfd_config_remote options from VTEP DB should be populated to # corresponding OVS DB values. bfd_dst_ip = vtep_ctl("--if-exists get tunnel %s " "bfd_config_remote:bfd_dst_ip" % (tunnel)) if not bfd_dst_ip: bfd_dst_ip = "169.254.1.1" bfd_dst_mac = vtep_ctl("--if-exists get tunnel %s " "bfd_config_remote:bfd_dst_mac" % (tunnel)) if not bfd_dst_mac: bfd_dst_mac = "00:23:20:00:00:01" ovs_vsctl("set interface %s bfd:bfd_dst_ip=%s " "bfd:bfd_remote_dst_mac=%s bfd:bfd_local_dst_mac=%s" % (port, bfd_dst_ip, bfd_lconf_default['bfd_config_local:bfd_dst_mac'], bfd_dst_mac)) def add_binding(binding, ls): vlog.info("adding binding %s" % binding) vlan, pp_name = binding.split("-", 1) pbinding = binding + "-p" lbinding = binding + "-l" # Create a patch port that connects the VLAN+port to the lswitch. # Do them as two separate calls so if one side already exists, the # other side is created. ovs_vsctl("add-port %s %s " " -- set Interface %s type=patch options:peer=%s" % (ps_name, pbinding, pbinding, lbinding)) ovs_vsctl("add-port %s %s " " -- set Interface %s type=patch options:peer=%s" % (ls.short_name, lbinding, lbinding, pbinding)) port_no = ovs_vsctl("get Interface %s ofport" % pp_name) patch_no = ovs_vsctl("get Interface %s ofport" % pbinding) vlan_ = vlan.lstrip('0') if vlan_: ovs_ofctl("add-flow %s priority=200,in_port=%s,dl_vlan=%s," "action=strip_vlan,%s" % (ps_name, port_no, vlan_, patch_no)) ovs_ofctl("add-flow %s priority=200,in_port=%s," "action=mod_vlan_vid:%s,%s" % (ps_name, patch_no, vlan_, port_no)) else: ovs_ofctl("add-flow %s priority=100,in_port=%s,action=%s" % (ps_name, port_no, patch_no)) ovs_ofctl("add-flow %s priority=100,in_port=%s,action=%s" % (ps_name, patch_no, port_no)) # Create a logical_bindings_stats record. if not vlan_: vlan_ = "0" vtep_ctl("set physical_port %s vlan_stats:%s=@stats -- " "--id=@stats create logical_binding_stats packets_from_local=0" % (pp_name, vlan_)) ls.add_lbinding(lbinding) Bindings[binding] = ls.name def del_binding(binding, ls): vlog.info("removing binding %s" % binding) vlan, pp_name = binding.split("-", 1) pbinding = binding + "-p" lbinding = binding + "-l" port_no = ovs_vsctl("get Interface %s ofport" % pp_name) patch_no = ovs_vsctl("get Interface %s ofport" % pbinding) vlan_ = vlan.lstrip('0') if vlan_: ovs_ofctl("del-flows %s priority=200,in_port=%s,dl_vlan=%s" % (ps_name, port_no, vlan_)) ovs_ofctl("del-flows %s priority=200,in_port=%s" % (ps_name, patch_no)) else: ovs_ofctl("--strict del-flows %s priority=100,in_port=%s" % (ps_name, port_no)) ovs_ofctl("--strict del-flows %s priority=100,in_port=%s" % (ps_name, patch_no)) ls.del_lbinding(lbinding) # Destroy the patch port that connects the VLAN+port to the lswitch ovs_vsctl("del-port %s %s -- del-port %s %s" % (ps_name, pbinding, ls.short_name, lbinding)) # Remove the record that links vlan with stats in logical_binding_stats. vtep_ctl("remove physical_port %s vlan_stats %s" % (pp_name, vlan)) del Bindings[binding] def handle_physical(): # Gather physical ports except the patch ports we created ovs_ports = ovs_vsctl("list-ports %s" % ps_name).split() ovs_port_set = set([port for port in ovs_ports if port[-2:] != "-p"]) vtep_pp_set = set(vtep_ctl("list-ports %s" % ps_name).split()) for pp_name in ovs_port_set.difference(vtep_pp_set): vlog.info("adding %s to %s" % (pp_name, ps_name)) vtep_ctl("add-port %s %s" % (ps_name, pp_name)) for pp_name in vtep_pp_set.difference(ovs_port_set): vlog.info("deleting %s from %s" % (pp_name, ps_name)) vtep_ctl("del-port %s %s" % (ps_name, pp_name)) new_bindings = set() for pp_name in vtep_pp_set: binding_set = set(vtep_ctl("list-bindings %s %s" % (ps_name, pp_name)).splitlines()) for b in binding_set: vlan, ls_name = b.split() if ls_name not in Lswitches: Lswitches[ls_name] = Logical_Switch(ls_name, ps_name) binding = "%s-%s" % (vlan, pp_name) ls = Lswitches[ls_name] new_bindings.add(binding) if binding in Bindings: if Bindings[binding] == ls_name: continue else: del_binding(binding, Lswitches[Bindings[binding]]) add_binding(binding, ls) dead_bindings = set(Bindings.keys()).difference(new_bindings) for binding in dead_bindings: ls_name = Bindings[binding] ls = Lswitches[ls_name] del_binding(binding, ls) if not len(ls.ports): ls.cleanup_ls() ovs_vsctl("del-br %s" % Lswitches[ls_name].short_name) vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name) del Lswitches[ls_name] def setup(): br_list = ovs_vsctl("list-br").split() if (ps_name not in br_list): ovs.util.ovs_fatal(0, "couldn't find OVS bridge %s" % ps_name, vlog) global ps_type ps_type = ovs_vsctl("get Bridge %s datapath_type" % ps_name).strip('"') call_prog("vtep-ctl", ["set", "physical_switch", ps_name, 'description="OVS VTEP Emulator"']) tunnel_ips = vtep_ctl("get physical_switch %s tunnel_ips" % ps_name).strip('[]"').split(", ") if len(tunnel_ips) != 1 or not tunnel_ips[0]: ovs.util.ovs_fatal(0, "exactly one 'tunnel_ips' should be set", vlog) global Tunnel_Ip Tunnel_Ip = tunnel_ips[0] ovs_ofctl("del-flows %s" % ps_name) # Remove any logical bridges from the previous run for br in br_list: if ovs_vsctl("br-get-external-id %s vtep_logical_switch" % br) == "true": # Remove the remote side of any logical switch ovs_ports = ovs_vsctl("list-ports %s" % br).split() for port in ovs_ports: port_type = ovs_vsctl("get Interface %s type" % port).strip('"') if port_type != "patch": continue peer = ovs_vsctl("get Interface %s options:peer" % port).strip('"') if (peer): ovs_vsctl("del-port %s" % peer) ovs_vsctl("del-br %s" % br) if br == bfd_bridge: bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split() for port in bfd_ports: remote_ip = ovs_vsctl("get interface %s options:remote_ip" % port) destroy_vtep_tunnel(remote_ip) ovs_vsctl("del-br %s" % br) if ps_type: ovs_vsctl("add-br %s -- set Bridge %s datapath_type=%s" % (bfd_bridge, bfd_bridge, ps_type)) else: ovs_vsctl("add-br %s" % bfd_bridge) # Remove local-mac entries from the previous run. Otherwise, if a vlan # binding is removed while the emulator is *not* running, the corresponding # local-mac entries are never cleaned up. vtep_ls = set(vtep_ctl("list-ls").split()) for ls_name in vtep_ls: vtep_ctl("clear-local-macs %s" % ls_name) def main(): parser = argparse.ArgumentParser() parser.add_argument("ps_name", metavar="PS-NAME", help="Name of physical switch.") parser.add_argument("--root-prefix", metavar="DIR", help="Use DIR as alternate root directory" " (for testing).") parser.add_argument("--version", action="version", version="%s %s" % (ovs.util.PROGRAM_NAME, VERSION)) ovs.vlog.add_args(parser) ovs.daemon.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) ovs.daemon.handle_args(args) global root_prefix if args.root_prefix: root_prefix = args.root_prefix global ps_name ps_name = args.ps_name global verbose_args if args.verbose: verbose_args = ['-v' + arg for arg in args.verbose] ovs.daemon.daemonize() ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None) error, unixctl = ovs.unixctl.server.UnixctlServer.create(None, version=VERSION) if error: ovs.util.ovs_fatal(error, "could not create unixctl server", vlog) setup() while True: unixctl.run() if exiting: break handle_physical() for ls_name, ls in Lswitches.items(): ls.run() run_bfd() poller = ovs.poller.Poller() unixctl.wait(poller) poller.timer_wait(1000) poller.block() unixctl.close() if __name__ == '__main__': try: main() except SystemExit: # Let system.exit() calls complete normally raise except: vlog.exception("traceback") sys.exit(ovs.daemon.RESTART_EXIT_CODE) openvswitch-3.7.0~git20260211.8c6ebf8/vtep/vtep-ctl.8.in000066400000000000000000000402421514270232600221740ustar00rootroot00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH vtep\-ctl 8 "March 2013" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN vtep\-ctl . .SH NAME vtep\-ctl \- utility for querying and configuring a VTEP database . .SH SYNOPSIS \fBvtep\-ctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]... . .SH DESCRIPTION The \fBvtep\-ctl\fR program configures a VTEP database. See \fBvtep\fR(5) for comprehensive documentation of the database schema. .PP \fBvtep\-ctl\fR connects to an \fBovsdb\-server\fR process that maintains a VTEP configuration database. Using this connection, it queries and possibly applies changes to the database, depending on the supplied commands. .PP \fBvtep\-ctl\fR can perform any number of commands in a single run, implemented as a single atomic transaction against the database. .PP The \fBvtep\-ctl\fR command line begins with global options (see \fBOPTIONS\fR below for details). The global options are followed by one or more commands. Each command should begin with \fB\-\-\fR by itself as a command-line argument, to separate it from the following commands. (The \fB\-\-\fR before the first command is optional.) The command itself starts with command-specific options, if any, followed by the command name and any arguments. See \fBEXAMPLES\fR below for syntax examples. . .SH OPTIONS . The following options affect the behavior \fBvtep\-ctl\fR as a whole. Some individual commands also accept their own options, which are given just before the command name. If the first command on the command line has options, then those options must be separated from the global options by \fB\-\-\fR. . .IP "\fB\-\-db=\fIserver\fR" Sets \fIserver\fR as the database server that \fBvtep\-ctl\fR contacts to query or modify configuration. \fIserver\fR may be an OVSDB active or passive connection method, as described in \fBovsdb\fR(7). The default is \fBunix:@RUNDIR@/db.sock\fR. .IP "\fB\-\-no\-syslog\fR" By default, \fBvtep\-ctl\fR logs its arguments and the details of any changes that it makes to the system log. This option disables this logging. .IP This option is equivalent to \fB\-\-verbose=vtep_ctl:syslog:warn\fR. . .IP "\fB\-\-oneline\fR" Modifies the output format so that the output for each command is printed on a single line. New-line characters that would otherwise separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that would otherwise appear in the output are doubled. Prints a blank line for each command that has no output. This option does not affect the formatting of output from the \fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR below. . .IP "\fB\-\-dry\-run\fR" Prevents \fBvtep\-ctl\fR from actually modifying the database. . .IP "\fB\-t \fIsecs\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" By default, or with a \fIsecs\fR of \fB0\fR, \fBvtep\-ctl\fR waits forever for a response from the database. This option limits runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBvtep\-ctl\fR will exit with a \fBSIGALRM\fR signal. (A timeout would normally happen only if the database cannot be contacted, or if the system is overloaded.) . .SS "Table Formatting Options" These options control the format of output from the \fBlist\fR and \fBfind\fR commands. .so lib/table.man . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .so lib/vlog.man .so lib/common.man . .SH COMMANDS The commands implemented by \fBvtep\-ctl\fR are described in the sections below. . .SS "Physical Switch Commands" These commands examine and manipulate physical switches. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-ps \fIpswitch\fR" Creates a new physical switch named \fIpswitch\fR. Initially the switch will have no ports. .IP Without \fB\-\-may\-exist\fR, attempting to create a switch that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIpswitch\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-ps \fIpswitch\fR" Deletes \fIpswitch\fR and all of its ports. .IP Without \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist has no effect. . .IP "\fBlist\-ps\fR" Lists all existing physical switches on standard output, one per line. . .IP "\fBps\-exists \fIpswitch\fR" Tests whether \fIpswitch\fR exists. If so, \fBvtep\-ctl\fR exits successfully with exit code 0. If not, \fBvtep\-ctl\fR exits unsuccessfully with exit code 2. . .SS "Port Commands" . These commands examine and manipulate VTEP physical ports. . .IP "\fBlist\-ports \fIpswitch\fR" Lists all of the ports within \fIpswitch\fR on standard output, one per line. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-port \fIpswitch port\fR" Creates on \fIpswitch\fR a new port named \fIport\fR from the network device of the same name. .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIpswitch\fR. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-port \fR[\fIpswitch\fR] \fIport\fR" Deletes \fIport\fR. If \fIpswitch\fR is omitted, \fIport\fR is removed from whatever switch contains it; if \fIpswitch\fR is specified, it must be the switch that contains \fIport\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a port that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a port that does not exist has no effect. . .SS "Logical Switch Commands" These commands examine and manipulate logical switches. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-ls \fIlswitch\fR" Creates a new logical switch named \fIlswitch\fR. Initially the switch will have no locator bindings. .IP Without \fB\-\-may\-exist\fR, attempting to create a switch that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIlswitch\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-ls \fIlswitch\fR" Deletes \fIlswitch\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist has no effect. . .IP "\fBlist\-ls\fR" Lists all existing logical switches on standard output, one per line. . .IP "\fBls\-exists \fIlswitch\fR" Tests whether \fIlswitch\fR exists. If so, \fBvtep\-ctl\fR exits successfully with exit code 0. If not, \fBvtep\-ctl\fR exits unsuccessfully with exit code 2. . .IP "\fBbind\-ls \fIpswitch port vlan lswitch\fR" Bind logical switch \fIlswitch\fR to the \fIport\fR/\fIvlan\fR combination on the physical switch \fIpswitch\fR. . .IP "\fBunbind\-ls \fIpswitch port vlan\fR" Remove the logical switch binding from the \fIport\fR/\fIvlan\fR combination on the physical switch \fIpswitch\fR. . .IP "\fBlist\-bindings \fIpswitch port\fR" List the logical switch bindings for \fIport\fR on the physical switch \fIpswitch\fR. . .IP "\fBset\-replication\-mode \fIlswitch replication\-mode\fR" Set logical switch \fIlswitch\fR replication mode to \fIreplication\-mode\fR; the only valid values for replication mode are "service_node" and "source_node". . For handling L2 broadcast, multicast and unknown unicast traffic, packets can be sent to all members of a logical switch referenced by a physical switch. There are different modes to replicate the packets. The default mode of replication is to send the traffic to a service node, which can be a hypervisor, server or appliance, and let the service node handle replication to other transport nodes (hypervisors or other VTEP physical switches). This mode is called service node replication. An alternate mode of replication, called source node replication involves the source node sending to all other transport nodes. Hypervisors are always responsible for doing their own replication for locally attached VMs in both modes. Service node mode is the default, if the replication mode is not explicitly set. Service node replication mode is considered a basic requirement because it only requires sending the packet to a single transport node. . .IP "\fBget\-replication\-mode \fIlswitch\fR" Get logical switch \fIlswitch\fR replication mode. The only valid values for replication mode are "service_node" and "source_node". An empty reply for replication mode implies a default of "service_node". . .SS "Logical Router Commands" These commands examine and manipulate logical routers. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-lr \fIlrouter\fR" Creates a new logical router named \fIlrouter\fR. .IP Without \fB\-\-may\-exist\fR, attempting to create a router that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIlrouter\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-lr \fIlrouter\fR" Deletes \fIlrouter\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a router that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a router that does not exist has no effect. . .IP "\fBlist\-lr\fR" Lists all existing logical routers on standard output, one per line. . .IP "\fBlr\-exists \fIlrouter\fR" Tests whether \fIlrouter\fR exists. If so, \fBvtep\-ctl\fR exits successfully with exit code 0. If not, \fBvtep\-ctl\fR exits unsuccessfully with exit code 2. .SS "Local MAC Binding Commands" These commands examine and manipulate local MAC bindings for the logical switch. The local maps are written by the VTEP to refer to MACs it has learned on its physical ports. . .IP "\fBadd\-ucast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Map the unicast Ethernet address \fImac\fR to the physical location \fIip\fR using encapsulation \fIencap\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBdel\-ucast\-local \fIlswitch mac\fR" Remove the local unicast Ethernet address \fImac\fR map from \fIlswitch\fR. The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBadd\-mcast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Add physical location \fIip\fR using encapsulation \fIencap\fR to the local mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBdel\-mcast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Remove physical location \fIip\fR using encapsulation \fIencap\fR from the local mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBclear\-local\-macs \fIlswitch\fR" Clear the local MAC bindings for \fIlswitch\fR. . .IP "\fBlist\-local\-macs \fIlswitch\fR" List the local MAC bindings for \fIlswitch\fR, one per line. . .SS "Remote MAC Binding Commands" These commands examine and manipulate local and remote MAC bindings for the logical switch. The remote maps are written by the network virtualization controller to refer to MACs that it has learned. . .IP "\fBadd\-ucast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Map the unicast Ethernet address \fImac\fR to the physical location \fIip\fR using encapsulation \fIencap\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBdel\-ucast\-remote \fIlswitch mac\fR" Remove the remote unicast Ethernet address \fImac\fR map from \fIlswitch\fR. The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBadd\-mcast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Add physical location \fIip\fR using encapsulation \fIencap\fR to the remote mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBdel\-mcast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Remove physical location \fIip\fR using encapsulation \fIencap\fR from the remote mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBclear\-remote\-macs \fIlswitch\fR" Clear the remote MAC bindings for \fIlswitch\fR. . .IP "\fBlist\-remote\-macs \fIlswitch\fR" List the remote MAC bindings for \fIlswitch\fR, one per line. . .SS "Manager Connectivity" . These commands manipulate the \fBmanagers\fR column in the \fBGlobal\fR table and rows in the \fBManagers\fR table. When \fBovsdb\-server\fR is configured to use the \fBmanagers\fR column for OVSDB connections (as described in the startup scripts provided with Open vSwitch), this allows the administrator to use \fBvtep\-ctl\fR to configure database connections. . .IP "\fBget\-manager\fR" Prints the configured manager(s). . .IP "\fBdel\-manager\fR" Deletes the configured manager(s). . .IP "\fBset\-manager\fR \fItarget\fR\&..." Sets the configured manager target or targets. Each \fItarget\fR may be an OVSDB active or passive connection method, e.g. \fBpssl:6640\fR, as described in \fBovsdb\fR(7). . .SS "Database Commands" . These commands query and modify the contents of \fBovsdb\fR tables. They are a slight abstraction of the \fBovsdb\fR interface and as such they operate at a lower level than other \fBvtep\-ctl\fR commands. .PP .ST "Identifying Tables, Records, and Columns" .PP Each of these commands has a \fItable\fR parameter to identify a table within the database. Many of them also take a \fIrecord\fR parameter that identifies a particular record within a table. The \fIrecord\fR parameter may be the UUID for a record, and many tables offer additional ways to identify records. Some commands also take \fIcolumn\fR parameters that identify a particular field within the records in a table. .PP The following tables are currently defined: .IP "\fBGlobal\fR" Top-level configuration for a hardware VTEP. This table contains exactly one record, identified by specifying \fB.\fR as the record name. .IP "\fBManager\fR" Configuration for an OVSDB connection. Records may be identified by target (e.g. \fBtcp:1.2.3.4\fR). .IP "\fBPhysical_Switch\fR" A physical switch that implements a VTEP. Records may be identified by physical switch name. .IP "\fBPhysical_Port\fR" A port within a physical switch. .IP "\fBLogical_Binding_Stats\fR" Reports statistics for the logical switch with which a VLAN on a physical port is associated. .IP "\fBLogical_Switch\fR" A logical Ethernet switch. Records may be identified by logical switch name. .IP "\fBUcast_Macs_Local\fR" Mapping of locally discovered unicast MAC addresses to tunnels. .IP "\fBUcast_Macs_Remote\fR" Mapping of remotely programmed unicast MAC addresses to tunnels. .IP "\fBMcast_Macs_Local\fR" Mapping of locally discovered multicast MAC addresses to tunnels. .IP "\fBMcast_Macs_Remote\fR" Mapping of remotely programmed multicast MAC addresses to tunnels. .IP "\fBPhysical_Locator_Set\fR" A set of one or more physical locators. .IP "\fBPhysical_Locator\fR" Identifies an endpoint to which logical switch traffic may be encapsulated and forwarded. Records may be identified by physical locator name. .PP Record names must be specified in full and with correct capitalization, except that UUIDs may be abbreviated to their first 4 (or more) hex digits, as long as that is unique within the table. Names of tables and columns are not case-sensitive, and \fB\-\fR and \fB_\fR are treated interchangeably. Unique abbreviations of table and column names are acceptable, e.g. \fBman\fR or \fBm\fR is sufficient to identify the \fBManager\fR table. . .so lib/db-ctl-base.man .PP .SH "EXIT STATUS" .IP "0" Successful program execution. .IP "1" Usage, syntax, or configuration file error. .IP "2" The \fIswitch\fR argument to \fBps\-exists\fR specified the name of a physical switch that does not exist. .SH "SEE ALSO" . .BR ovsdb\-server (1), .BR vtep (5). openvswitch-3.7.0~git20260211.8c6ebf8/vtep/vtep-ctl.c000066400000000000000000002316101514270232600216430ustar00rootroot00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2015, 2016, 2017 Nicira, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "openvswitch/dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "openvswitch/json.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "openvswitch/poll-loop.h" #include "process.h" #include "stream.h" #include "stream-ssl.h" #include "smap.h" #include "sset.h" #include "svec.h" #include "vtep/vtep-idl.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(vtep_ctl); struct vtep_ctl_context; /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --timeout: Time to wait for a connection to 'db'. */ static unsigned int timeout; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; /* The IDL we're using and the current transaction, if any. * This is for use by vtep_ctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void vtep_ctl_exit(int status); static void vtep_ctl_cmd_init(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static bool do_vtep_ctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); static struct vtep_ctl_lswitch *find_lswitch(struct vtep_ctl_context *, const char *name, bool must_exist); static struct vtep_ctl_lrouter *find_lrouter(struct vtep_ctl_context *, const char *name, bool must_exist); int main(int argc, char *argv[]) { struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels_from_string_assert("reconnect:warn"); vtep_ctl_cmd_init(); /* Parse command line. */ char *args = process_escape_args(argv); shash_init(&local_options); parse_options(argc, argv, &local_options); char *error = ctl_parse_commands(argc - optind, argv + optind, &local_options, &commands, &n_commands); if (error) { ctl_fatal("%s", error); } VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, "Called as %s", args); ctl_timeout_setup(timeout); /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &vteprec_idl_class, false, false); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { seqno = ovsdb_idl_get_seqno(idl); if (do_vtep_ctl(args, commands, n_commands, idl)) { free(args); exit(EXIT_SUCCESS); } } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_ONELINE, OPT_NO_SYSLOG, OPT_DRY_RUN, OPT_PEER_CA_CERT, OPT_LOCAL, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS, SSL_OPTION_ENUMS, }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, TABLE_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); for (;;) { int idx = INT_MAX; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), nullable_xstrdup(optarg)); break; case 'h': usage(); case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", vteprec_get_db_version()); exit(EXIT_SUCCESS); case 't': if (!str_to_uint(optarg, 10, &timeout) || !timeout) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!db) { db = ctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void vtep_ctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } static void usage(void) { printf("\ %s: VTEP configuration utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ VTEP commands:\n\ show print overview of database contents\n\ \n\ Manager commands:\n\ get-manager print the managers\n\ del-manager delete the managers\n\ [--inactivity-probe=MSECS]\n\ set-manager TARGET... set the list of managers to TARGET...\n\ \n\ Physical Switch commands:\n\ add-ps PS create a new physical switch named PS\n\ del-ps PS delete PS and all of its ports\n\ list-ps print the names of all the physical switches\n\ ps-exists PS exit 2 if PS does not exist\n\ \n\ Port commands:\n\ list-ports PS print the names of all the ports on PS\n\ add-port PS PORT add network device PORT to PS\n\ del-port PS PORT delete PORT from PS\n\ \n\ Logical Switch commands:\n\ add-ls LS create a new logical switch named LS\n\ del-ls LS delete LS and all of its ports\n\ list-ls print the names of all the logical switches\n\ ls-exists LS exit 2 if LS does not exist\n\ bind-ls PS PORT VLAN LS bind LS to VLAN on PORT\n\ unbind-ls PS PORT VLAN unbind logical switch on VLAN from PORT\n\ list-bindings PS PORT list bindings for PORT on PS\n\ set-replication-mode LS MODE set replication mode on LS\n\ get-replication-mode LS get replication mode on LS\n\ \n\ Logical Router commands:\n\ add-lr LR create a new logical router named LR\n\ del-lr LR delete LR\n\ list-lr print the names of all the logical routers\n\ lr-exists LR exit 2 if LR does not exist\n\ \n\ MAC binding commands:\n\ add-ucast-local LS MAC [ENCAP] IP add ucast local entry in LS\n\ del-ucast-local LS MAC del ucast local entry from LS\n\ add-mcast-local LS MAC [ENCAP] IP add mcast local entry in LS\n\ del-mcast-local LS MAC [ENCAP] IP del mcast local entry from LS\n\ clear-local-macs LS clear local mac entries\n\ list-local-macs LS list local mac entries\n\ add-ucast-remote LS MAC [ENCAP] IP add ucast remote entry in LS\n\ del-ucast-remote LS MAC del ucast remote entry from LS\n\ add-mcast-remote LS MAC [ENCAP] IP add mcast remote entry in LS\n\ del-mcast-remote LS MAC [ENCAP] IP del mcast remote entry from LS\n\ clear-remote-macs LS clear remote mac entries\n\ list-remote-macs LS list remote mac entries\n\ \n\ %s\ %s\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ -t, --timeout=SECS wait at most SECS seconds\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, ctl_get_db_cmd_usage(), ctl_list_db_tables_usage(), ctl_default_db()); table_usage(); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=vtep_ctl:syslog:warn\n"); stream_usage("database", true, true, false); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); exit(EXIT_SUCCESS); } static struct cmd_show_table cmd_show_tables[] = { {&vteprec_table_global, NULL, {&vteprec_global_col_managers, &vteprec_global_col_switches, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_manager, &vteprec_manager_col_target, {&vteprec_manager_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_physical_switch, &vteprec_physical_switch_col_name, {&vteprec_physical_switch_col_management_ips, &vteprec_physical_switch_col_tunnel_ips, &vteprec_physical_switch_col_ports}, {NULL, NULL, NULL} }, {&vteprec_table_physical_port, &vteprec_physical_port_col_name, {&vteprec_physical_port_col_vlan_bindings, NULL, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_logical_switch, &vteprec_logical_switch_col_name, {NULL, NULL, NULL}, {NULL, NULL, NULL} }, {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}} }; /* vtep-ctl specific context. Inherits the 'struct ctl_context' as base. */ struct vtep_ctl_context { struct ctl_context base; /* Modifiable state. */ const struct vteprec_global *vtep_global; bool verified_ports; /* A cache of the contents of the database. * * A command that needs to use any of this information must first * call vtep_ctl_context_populate_cache(). A command that changes * anything that could invalidate the cache must either call * vtep_ctl_context_invalidate_cache() or manually update the cache * to maintain its correctness. */ bool cache_valid; struct shash pswitches; /* Maps from physical switch name to * struct vtep_ctl_pswitch. */ struct shash ports; /* Maps from port name to struct vtep_ctl_port. */ struct shash lswitches; /* Maps from logical switch name to * struct vtep_ctl_lswitch. */ struct shash plocs; /* Maps from "+" to * struct vteprec_physical_locator. */ struct shash lrouters; /* Maps from logical router name to * struct vtep_ctl_lrouter. */ }; /* Casts 'base' into 'struct vtep_ctl_context'. */ static struct vtep_ctl_context * vtep_ctl_context_cast(struct ctl_context *base) { return CONTAINER_OF(base, struct vtep_ctl_context, base); } struct vtep_ctl_pswitch { const struct vteprec_physical_switch *ps_cfg; char *name; struct ovs_list ports; /* Contains "struct vteprec_physical_port"s. */ }; struct vtep_ctl_port { struct ovs_list ports_node; /* In struct vtep_ctl_pswitch's 'ports' list. */ const struct vteprec_physical_port *port_cfg; struct vtep_ctl_pswitch *ps; struct shash bindings; /* Maps from vlan to vtep_ctl_lswitch. */ }; struct vtep_ctl_lswitch { const struct vteprec_logical_switch *ls_cfg; char *name; struct shash ucast_local; /* Maps from mac to vteprec_ucast_macs_local. */ struct shash ucast_remote; /* Maps from mac to vteprec_ucast_macs_remote.*/ struct shash mcast_local; /* Maps from mac to vtep_ctl_mcast_mac. */ struct shash mcast_remote; /* Maps from mac to vtep_ctl_mcast_mac. */ }; struct vtep_ctl_lrouter { const struct vteprec_logical_router *lr_cfg; char *name; }; struct vtep_ctl_mcast_mac { const struct vteprec_mcast_macs_local *local_cfg; const struct vteprec_mcast_macs_remote *remote_cfg; const struct vteprec_physical_locator_set *ploc_set_cfg; struct ovs_list locators; /* Contains 'vtep_ctl_ploc's. */ }; struct vtep_ctl_ploc { struct ovs_list locators_node; /* In struct vtep_ctl_ploc_set's 'locators' list. */ const struct vteprec_physical_locator *ploc_cfg; }; static void verify_ports(struct vtep_ctl_context *vtepctl_ctx) { if (!vtepctl_ctx->verified_ports) { const struct vteprec_physical_switch *ps; vteprec_global_verify_switches(vtepctl_ctx->vtep_global); VTEPREC_PHYSICAL_SWITCH_FOR_EACH (ps, vtepctl_ctx->base.idl) { vteprec_physical_switch_verify_ports(ps); } vtepctl_ctx->verified_ports = true; } } static struct vtep_ctl_port * add_port_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps, struct vteprec_physical_port *port_cfg) { char *cache_name = xasprintf("%s+%s", ps->name, port_cfg->name); struct vtep_ctl_port *port; port = xmalloc(sizeof *port); ovs_list_push_back(&ps->ports, &port->ports_node); port->port_cfg = port_cfg; port->ps = ps; shash_add(&vtepctl_ctx->ports, cache_name, port); free(cache_name); shash_init(&port->bindings); return port; } static void del_cached_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port) { char *cache_name = xasprintf("%s+%s", port->ps->name, port->port_cfg->name); ovs_list_remove(&port->ports_node); shash_find_and_delete(&vtepctl_ctx->ports, cache_name); vteprec_physical_port_delete(port->port_cfg); shash_destroy(&port->bindings); free(cache_name); free(port); } static void add_pswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vteprec_physical_switch *ps_cfg) { struct vtep_ctl_pswitch *ps = xmalloc(sizeof *ps); ps->ps_cfg = ps_cfg; ps->name = xstrdup(ps_cfg->name); ovs_list_init(&ps->ports); shash_add(&vtepctl_ctx->pswitches, ps->name, ps); } static void vtep_delete_pswitch(const struct vteprec_global *vtep_global, const struct vteprec_physical_switch *ps) { struct vteprec_physical_switch **pswitches; size_t i, n; pswitches = xmalloc(sizeof *vtep_global->switches * vtep_global->n_switches); for (i = n = 0; i < vtep_global->n_switches; i++) { if (vtep_global->switches[i] != ps) { pswitches[n++] = vtep_global->switches[i]; } } vteprec_global_set_switches(vtep_global, pswitches, n); free(pswitches); } static void del_cached_pswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_pswitch *ps) { ovs_assert(ovs_list_is_empty(&ps->ports)); if (ps->ps_cfg) { vteprec_physical_switch_delete(ps->ps_cfg); vtep_delete_pswitch(ctx->vtep_global, ps->ps_cfg); } shash_find_and_delete(&ctx->pswitches, ps->name); free(ps->name); free(ps); } static struct vtep_ctl_lswitch * add_lswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx, const struct vteprec_logical_switch *ls_cfg) { struct vtep_ctl_lswitch *ls = xmalloc(sizeof *ls); ls->ls_cfg = ls_cfg; ls->name = xstrdup(ls_cfg->name); shash_add(&vtepctl_ctx->lswitches, ls->name, ls); shash_init(&ls->ucast_local); shash_init(&ls->ucast_remote); shash_init(&ls->mcast_local); shash_init(&ls->mcast_remote); return ls; } static void del_cached_lswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_lswitch *ls) { if (ls->ls_cfg) { vteprec_logical_switch_delete(ls->ls_cfg); } shash_find_and_delete(&ctx->lswitches, ls->name); free(ls->name); free(ls); } static void commit_ls_bindings(struct vtep_ctl_port *port) { struct vteprec_logical_switch **binding_values; int64_t *binding_keys; size_t n_bindings; struct shash_node *node; int i; n_bindings = shash_count(&port->bindings); binding_keys = xmalloc(n_bindings * sizeof *binding_keys); binding_values = xmalloc(n_bindings * sizeof *binding_values); i = 0; SHASH_FOR_EACH(node, &port->bindings) { struct vtep_ctl_lswitch *ls_entry = node->data; binding_keys[i] = strtoll(node->name, NULL, 0); binding_values[i] = (struct vteprec_logical_switch *)ls_entry->ls_cfg; i++; } vteprec_physical_port_set_vlan_bindings(port->port_cfg, binding_keys, binding_values, n_bindings); free(binding_values); free(binding_keys); } static void add_ls_binding_to_cache(struct vtep_ctl_port *port, const char *vlan, struct vtep_ctl_lswitch *ls) { if (shash_find(&port->bindings, vlan)) { ctl_fatal("multiple bindings for vlan %s", vlan); } shash_add(&port->bindings, vlan, ls); } static void del_cached_ls_binding(struct vtep_ctl_port *port, const char *vlan) { if (!shash_find(&port->bindings, vlan)) { ctl_fatal("no binding for vlan %s", vlan); } shash_find_and_delete(&port->bindings, vlan); } static struct vtep_ctl_lrouter * add_lrouter_to_cache(struct vtep_ctl_context *vtepctl_ctx, const struct vteprec_logical_router *lr_cfg) { struct vtep_ctl_lrouter *lr = xmalloc(sizeof *lr); lr->lr_cfg = lr_cfg; lr->name = xstrdup(lr_cfg->name); shash_add(&vtepctl_ctx->lrouters, lr->name, lr); return lr; } static void del_cached_lrouter(struct vtep_ctl_context *ctx, struct vtep_ctl_lrouter *lr) { if (lr->lr_cfg) { vteprec_logical_router_delete(lr->lr_cfg); } shash_find_and_delete(&ctx->lrouters, lr->name); free(lr->name); free(lr); } static struct vteprec_physical_locator * find_ploc(struct vtep_ctl_context *vtepctl_ctx, const char *encap, const char *dst_ip) { struct vteprec_physical_locator *ploc; char *name = xasprintf("%s+%s", encap, dst_ip); ovs_assert(vtepctl_ctx->cache_valid); ploc = shash_find_data(&vtepctl_ctx->plocs, name); free(name); return ploc; } static void add_ploc_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vteprec_physical_locator *ploc) { char *name = xasprintf("%s+%s", ploc->encapsulation_type, ploc->dst_ip); struct vteprec_physical_locator *orig_ploc; orig_ploc = find_ploc(vtepctl_ctx, ploc->encapsulation_type, ploc->dst_ip); if (!orig_ploc) { shash_add(&vtepctl_ctx->plocs, name, ploc); } free(name); } static void add_ploc_to_mcast_mac(struct vtep_ctl_mcast_mac *mcast_mac, struct vteprec_physical_locator *ploc_cfg) { struct vtep_ctl_ploc *ploc; ploc = xmalloc(sizeof *ploc); ploc->ploc_cfg = ploc_cfg; ovs_list_push_back(&mcast_mac->locators, &ploc->locators_node); } static void del_ploc_from_mcast_mac(struct vtep_ctl_mcast_mac *mcast_mac, struct vteprec_physical_locator *ploc_cfg) { struct vtep_ctl_ploc *ploc; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { if (ploc->ploc_cfg == ploc_cfg) { ovs_list_remove(&ploc->locators_node); free(ploc); return; } } } static struct vtep_ctl_mcast_mac * add_mcast_mac_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lswitch *ls, const char *mac, struct vteprec_physical_locator_set *ploc_set_cfg, bool local) { struct vtep_ctl_mcast_mac *mcast_mac; struct shash *mcast_shash; size_t i; mcast_mac = xmalloc(sizeof *mcast_mac); mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; mcast_mac->ploc_set_cfg = ploc_set_cfg; ovs_list_init(&mcast_mac->locators); shash_add(mcast_shash, mac, mcast_mac); for (i = 0; i < ploc_set_cfg->n_locators; i++) { struct vteprec_physical_locator *ploc_cfg; ploc_cfg = ploc_set_cfg->locators[i]; add_ploc_to_mcast_mac(mcast_mac, ploc_cfg); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } return mcast_mac; } static void vtep_ctl_context_invalidate_cache(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; if (!vtepctl_ctx->cache_valid) { return; } vtepctl_ctx->cache_valid = false; SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) { struct vtep_ctl_pswitch *ps = node->data; free(ps->name); free(ps); } shash_destroy(&vtepctl_ctx->pswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->ports) { struct vtep_ctl_port *port = node->data; shash_destroy(&port->bindings); } shash_destroy_free_data(&vtepctl_ctx->ports); SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) { struct vtep_ctl_lswitch *ls = node->data; struct shash_node *node2; shash_destroy(&ls->ucast_local); shash_destroy(&ls->ucast_remote); SHASH_FOR_EACH_SAFE (node2, &ls->mcast_local) { struct vtep_ctl_mcast_mac *mcast_mac = node2->data; struct vtep_ctl_ploc *ploc; LIST_FOR_EACH_SAFE (ploc, locators_node, &mcast_mac->locators) { free(ploc); } free(mcast_mac); } shash_destroy(&ls->mcast_local); SHASH_FOR_EACH_SAFE (node2, &ls->mcast_remote) { struct vtep_ctl_mcast_mac *mcast_mac = node2->data; struct vtep_ctl_ploc *ploc; LIST_FOR_EACH_SAFE (ploc, locators_node, &mcast_mac->locators) { free(ploc); } free(mcast_mac); } shash_destroy(&ls->mcast_remote); free(ls->name); free(ls); } shash_destroy(&vtepctl_ctx->lswitches); shash_destroy(&vtepctl_ctx->plocs); SHASH_FOR_EACH (node, &vtepctl_ctx->lrouters) { struct vtep_ctl_lrouter *lr = node->data; free(lr->name); free(lr); } shash_destroy(&vtepctl_ctx->lrouters); } static void pre_get_info(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_switches); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_ports); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_tunnels); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_port_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_port_col_vlan_bindings); ovsdb_idl_add_column(ctx->idl, &vteprec_logical_switch_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_logical_switch_col_replication_mode); ovsdb_idl_add_column(ctx->idl, &vteprec_logical_router_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_locator); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_locator); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_locator_set); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_locator_set); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_set_col_locators); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_col_dst_ip); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_col_encapsulation_type); ovsdb_idl_add_column(ctx->idl, &vteprec_tunnel_col_local); ovsdb_idl_add_column(ctx->idl, &vteprec_tunnel_col_remote); } static void vtep_ctl_context_populate_cache(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; const struct vteprec_logical_switch *ls_cfg; const struct vteprec_logical_router *lr_cfg; const struct vteprec_ucast_macs_local *ucast_local_cfg; const struct vteprec_ucast_macs_remote *ucast_remote_cfg; const struct vteprec_mcast_macs_local *mcast_local_cfg; const struct vteprec_mcast_macs_remote *mcast_remote_cfg; const struct vteprec_tunnel *tunnel_cfg; struct sset pswitches, ports, lswitches; struct sset lrouters; size_t i; if (vtepctl_ctx->cache_valid) { /* Cache is already populated. */ return; } vtepctl_ctx->cache_valid = true; shash_init(&vtepctl_ctx->pswitches); shash_init(&vtepctl_ctx->ports); shash_init(&vtepctl_ctx->lswitches); shash_init(&vtepctl_ctx->plocs); shash_init(&vtepctl_ctx->lrouters); sset_init(&pswitches); sset_init(&ports); for (i = 0; i < vtep_global->n_switches; i++) { struct vteprec_physical_switch *ps_cfg = vtep_global->switches[i]; size_t j; if (!sset_add(&pswitches, ps_cfg->name)) { VLOG_WARN("%s: database contains duplicate physical switch name", ps_cfg->name); continue; } add_pswitch_to_cache(vtepctl_ctx, ps_cfg); for (j = 0; j < ps_cfg->n_ports; j++) { struct vteprec_physical_port *port_cfg = ps_cfg->ports[j]; if (!sset_add(&ports, port_cfg->name)) { /* Duplicate port name. (We will warn about that later.) */ continue; } } } sset_destroy(&pswitches); sset_destroy(&ports); sset_init(&lswitches); VTEPREC_LOGICAL_SWITCH_FOR_EACH (ls_cfg, ctx->idl) { if (!sset_add(&lswitches, ls_cfg->name)) { VLOG_WARN("%s: database contains duplicate logical switch name", ls_cfg->name); continue; } add_lswitch_to_cache(vtepctl_ctx, ls_cfg); } sset_destroy(&lswitches); sset_init(&lrouters); VTEPREC_LOGICAL_ROUTER_FOR_EACH (lr_cfg, ctx->idl) { if (!sset_add(&lrouters, lr_cfg->name)) { VLOG_WARN("%s: database contains duplicate logical router name", lr_cfg->name); continue; } add_lrouter_to_cache(vtepctl_ctx, lr_cfg); } sset_destroy(&lrouters); VTEPREC_UCAST_MACS_LOCAL_FOR_EACH (ucast_local_cfg, ctx->idl) { struct vtep_ctl_lswitch *ls; if (!ucast_local_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, ucast_local_cfg->logical_switch->name, false); if (!ls) { continue; } if (ucast_local_cfg->locator) { add_ploc_to_cache(vtepctl_ctx, ucast_local_cfg->locator); } shash_add(&ls->ucast_local, ucast_local_cfg->MAC, ucast_local_cfg); } VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (ucast_remote_cfg, ctx->idl) { struct vtep_ctl_lswitch *ls; if (!ucast_remote_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, ucast_remote_cfg->logical_switch->name, false); if (!ls) { continue; } if (ucast_remote_cfg->locator) { add_ploc_to_cache(vtepctl_ctx, ucast_remote_cfg->locator); } shash_add(&ls->ucast_remote, ucast_remote_cfg->MAC, ucast_remote_cfg); } VTEPREC_MCAST_MACS_LOCAL_FOR_EACH (mcast_local_cfg, ctx->idl) { struct vtep_ctl_mcast_mac *mcast_mac; struct vtep_ctl_lswitch *ls; if (!mcast_local_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, mcast_local_cfg->logical_switch->name, false); if (!ls) { continue; } mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_local_cfg->MAC, mcast_local_cfg->locator_set, true); mcast_mac->local_cfg = mcast_local_cfg; } VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mcast_remote_cfg, ctx->idl) { struct vtep_ctl_mcast_mac *mcast_mac; struct vtep_ctl_lswitch *ls; if (!mcast_remote_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, mcast_remote_cfg->logical_switch->name, false); if (!ls) { continue; } mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_remote_cfg->MAC, mcast_remote_cfg->locator_set, false); mcast_mac->remote_cfg = mcast_remote_cfg; } VTEPREC_TUNNEL_FOR_EACH (tunnel_cfg, ctx->idl) { if (tunnel_cfg->local) { add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->local); } if (tunnel_cfg->remote) { add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->remote); } } sset_init(&pswitches); for (i = 0; i < vtep_global->n_switches; i++) { struct vteprec_physical_switch *ps_cfg = vtep_global->switches[i]; struct vtep_ctl_pswitch *ps; size_t j; if (!sset_add(&pswitches, ps_cfg->name)) { continue; } ps = shash_find_data(&vtepctl_ctx->pswitches, ps_cfg->name); ovs_assert(ps); for (j = 0; j < ps_cfg->n_ports; j++) { struct vteprec_physical_port *port_cfg = ps_cfg->ports[j]; struct vtep_ctl_port *port; size_t k; port = shash_find_data(&vtepctl_ctx->ports, port_cfg->name); if (port) { if (port_cfg == port->port_cfg) { VLOG_WARN("%s: port is in multiple physical switches " "(%s and %s)", port_cfg->name, ps->name, port->ps->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server shouldn't * have allowed it. */ VLOG_ERR("%s: database contains duplicate port name", port_cfg->name); } continue; } port = add_port_to_cache(vtepctl_ctx, ps, port_cfg); for (k = 0; k < port_cfg->n_vlan_bindings; k++) { struct vtep_ctl_lswitch *ls; char *vlan; vlan = xasprintf("%"PRId64, port_cfg->key_vlan_bindings[k]); if (shash_find(&port->bindings, vlan)) { ctl_fatal("multiple bindings for vlan %s", vlan); } ls_cfg = port_cfg->value_vlan_bindings[k]; ls = find_lswitch(vtepctl_ctx, ls_cfg->name, true); shash_add_nocopy(&port->bindings, vlan, ls); } } } sset_destroy(&pswitches); } static struct vtep_ctl_pswitch * find_pswitch(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist) { struct vtep_ctl_pswitch *ps; ovs_assert(vtepctl_ctx->cache_valid); ps = shash_find_data(&vtepctl_ctx->pswitches, name); if (must_exist && !ps) { ctl_fatal("no physical switch named %s", name); } vteprec_global_verify_switches(vtepctl_ctx->vtep_global); return ps; } static struct vtep_ctl_port * find_port(struct vtep_ctl_context *vtepctl_ctx, const char *ps_name, const char *port_name, bool must_exist) { char *cache_name = xasprintf("%s+%s", ps_name, port_name); struct vtep_ctl_port *port; ovs_assert(vtepctl_ctx->cache_valid); port = shash_find_data(&vtepctl_ctx->ports, cache_name); if (port && !strcmp(port_name, port->ps->name)) { port = NULL; } free(cache_name); if (must_exist && !port) { ctl_fatal("no port named %s", port_name); } verify_ports(vtepctl_ctx); return port; } static void pswitch_insert_port(const struct vteprec_physical_switch *ps, struct vteprec_physical_port *port) { struct vteprec_physical_port **ports; size_t i; ports = xmalloc(sizeof *ps->ports * (ps->n_ports + 1)); for (i = 0; i < ps->n_ports; i++) { ports[i] = ps->ports[i]; } ports[ps->n_ports] = port; vteprec_physical_switch_set_ports(ps, ports, ps->n_ports + 1); free(ports); } static void pswitch_delete_port(const struct vteprec_physical_switch *ps, const struct vteprec_physical_port *port) { struct vteprec_physical_port **ports; size_t i, n; ports = xmalloc(sizeof *ps->ports * ps->n_ports); for (i = n = 0; i < ps->n_ports; i++) { if (ps->ports[i] != port) { ports[n++] = ps->ports[i]; } } vteprec_physical_switch_set_ports(ps, ports, n); free(ports); } static void vtep_insert_pswitch(const struct vteprec_global *vtep_global, struct vteprec_physical_switch *ps) { struct vteprec_physical_switch **pswitches; size_t i; pswitches = xmalloc(sizeof *vtep_global->switches * (vtep_global->n_switches + 1)); for (i = 0; i < vtep_global->n_switches; i++) { pswitches[i] = vtep_global->switches[i]; } pswitches[vtep_global->n_switches] = ps; vteprec_global_set_switches(vtep_global, pswitches, vtep_global->n_switches + 1); free(pswitches); } static void cmd_add_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const char *ps_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vteprec_physical_switch *ps; vtep_ctl_context_populate_cache(ctx); if (find_pswitch(vtepctl_ctx, ps_name, false)) { if (!may_exist) { ctl_fatal("cannot create physical switch %s because it " "already exists", ps_name); } return; } ps = vteprec_physical_switch_insert(ctx->txn); vteprec_physical_switch_set_name(ps, ps_name); vtep_insert_pswitch(vtepctl_ctx->vtep_global, ps); vtep_ctl_context_invalidate_cache(ctx); } static void del_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port) { pswitch_delete_port(port->ps->ps_cfg, port->port_cfg); del_cached_port(vtepctl_ctx, port); } static void del_pswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps) { struct vtep_ctl_port *port; LIST_FOR_EACH_SAFE (port, ports_node, &ps->ports) { del_port(vtepctl_ctx, port); } del_cached_pswitch(vtepctl_ctx, ps); } static void cmd_del_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_pswitch *ps; vtep_ctl_context_populate_cache(ctx); ps = find_pswitch(vtepctl_ctx, ctx->argv[1], must_exist); if (ps) { del_pswitch(vtepctl_ctx, ps); } } static void output_sorted(struct svec *svec, struct ds *output) { const char *name; size_t i; svec_sort(svec); SVEC_FOR_EACH (i, name, svec) { ds_put_format(output, "%s\n", name); } } static void cmd_list_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; struct svec pswitches; vtep_ctl_context_populate_cache(ctx); svec_init(&pswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) { struct vtep_ctl_pswitch *ps = node->data; svec_add(&pswitches, ps->name); } output_sorted(&pswitches, &ctx->output); svec_destroy(&pswitches); } static void cmd_ps_exists(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); vtep_ctl_context_populate_cache(ctx); if (!find_pswitch(vtepctl_ctx, ctx->argv[1], false)) { vtep_ctl_exit(2); } } static void cmd_list_ports(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_pswitch *ps; struct vtep_ctl_port *port; struct svec ports; vtep_ctl_context_populate_cache(ctx); ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true); vteprec_physical_switch_verify_ports(ps->ps_cfg); svec_init(&ports); LIST_FOR_EACH (port, ports_node, &ps->ports) { if (strcmp(port->port_cfg->name, ps->name)) { svec_add(&ports, port->port_cfg->name); } } output_sorted(&ports, &ctx->output); svec_destroy(&ports); } static void add_port(struct ctl_context *ctx, const char *ps_name, const char *port_name, bool may_exist) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_port *vtep_ctl_port; struct vtep_ctl_pswitch *ps; struct vteprec_physical_port *port; vtep_ctl_context_populate_cache(ctx); vtep_ctl_port = find_port(vtepctl_ctx, ps_name, port_name, false); if (vtep_ctl_port) { if (!may_exist) { ctl_fatal("cannot create a port named %s on %s because a " "port with that name already exists", port_name, ps_name); } return; } ps = find_pswitch(vtepctl_ctx, ps_name, true); port = vteprec_physical_port_insert(ctx->txn); vteprec_physical_port_set_name(port, port_name); pswitch_insert_port(ps->ps_cfg, port); add_port_to_cache(vtepctl_ctx, ps, port); } static void cmd_add_port(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist); } static void cmd_del_port(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_port *port; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], must_exist); if (port) { if (ctx->argc == 3) { struct vtep_ctl_pswitch *ps; ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true); if (port->ps != ps) { ctl_fatal("physical switch %s does not have a port %s", ctx->argv[1], ctx->argv[2]); } } del_port(vtepctl_ctx, port); } } static struct vtep_ctl_lswitch * find_lswitch(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist) { struct vtep_ctl_lswitch *ls; ovs_assert(vtepctl_ctx->cache_valid); ls = shash_find_data(&vtepctl_ctx->lswitches, name); if (must_exist && !ls) { ctl_fatal("no logical switch named %s", name); } return ls; } static void cmd_add_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const char *ls_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vteprec_logical_switch *ls; vtep_ctl_context_populate_cache(ctx); if (find_lswitch(vtepctl_ctx, ls_name, false)) { if (!may_exist) { ctl_fatal("cannot create logical switch %s because it " "already exists", ls_name); } return; } ls = vteprec_logical_switch_insert(ctx->txn); vteprec_logical_switch_set_name(ls, ls_name); vtep_ctl_context_invalidate_cache(ctx); } static void del_lswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lswitch *ls) { del_cached_lswitch(vtepctl_ctx, ls); } static void cmd_del_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_lswitch *ls; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], must_exist); if (ls) { del_lswitch(vtepctl_ctx, ls); } } static void cmd_list_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; struct svec lswitches; vtep_ctl_context_populate_cache(ctx); svec_init(&lswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) { struct vtep_ctl_lswitch *ls = node->data; svec_add(&lswitches, ls->name); } output_sorted(&lswitches, &ctx->output); svec_destroy(&lswitches); } static void cmd_ls_exists(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); vtep_ctl_context_populate_cache(ctx); if (!find_lswitch(vtepctl_ctx, ctx->argv[1], false)) { vtep_ctl_exit(2); } } static void cmd_list_bindings(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct shash_node *node; struct vtep_ctl_port *port; struct svec bindings; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); svec_init(&bindings); SHASH_FOR_EACH (node, &port->bindings) { struct vtep_ctl_lswitch *lswitch = node->data; char *binding; binding = xasprintf("%04lld %s", strtoll(node->name, NULL, 0), lswitch->name); svec_add_nocopy(&bindings, binding); } output_sorted(&bindings, &ctx->output); svec_destroy(&bindings); } static void cmd_bind_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; struct vtep_ctl_port *port; const char *vlan; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); vlan = ctx->argv[3]; ls = find_lswitch(vtepctl_ctx, ctx->argv[4], true); add_ls_binding_to_cache(port, vlan, ls); commit_ls_bindings(port); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_unbind_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_port *port; const char *vlan; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); vlan = ctx->argv[3]; del_cached_ls_binding(port, vlan); commit_ls_bindings(port); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_set_replication_mode(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *ls_name = ctx->argv[1]; vtep_ctl_context_populate_cache(ctx); if (strcmp(ctx->argv[2], "service_node") && strcmp(ctx->argv[2], "source_node")) { ctl_fatal("Replication mode must be 'service_node' or 'source_node'"); } ls = find_lswitch(vtepctl_ctx, ls_name, true); vteprec_logical_switch_set_replication_mode(ls->ls_cfg, ctx->argv[2]); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_get_replication_mode(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *ls_name = ctx->argv[1]; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ls_name, true); ds_put_format(&ctx->output, "%s\n", ls->ls_cfg->replication_mode); } static struct vtep_ctl_lrouter * find_lrouter(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist) { struct vtep_ctl_lrouter *lr; ovs_assert(vtepctl_ctx->cache_valid); lr = shash_find_data(&vtepctl_ctx->lrouters, name); if (must_exist && !lr) { ctl_fatal("no logical router named %s", name); } return lr; } static void cmd_add_lr(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const char *lr_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vteprec_logical_router *lr; vtep_ctl_context_populate_cache(ctx); if (find_lrouter(vtepctl_ctx, lr_name, false)) { if (!may_exist) { ctl_fatal("cannot create logical router %s because it " "already exists", lr_name); } return; } lr = vteprec_logical_router_insert(ctx->txn); vteprec_logical_router_set_name(lr, lr_name); vtep_ctl_context_invalidate_cache(ctx); } static void del_lrouter(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lrouter *lr) { del_cached_lrouter(vtepctl_ctx, lr); } static void cmd_del_lr(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_lrouter *lr; vtep_ctl_context_populate_cache(ctx); lr = find_lrouter(vtepctl_ctx, ctx->argv[1], must_exist); if (lr) { del_lrouter(vtepctl_ctx, lr); } } static void cmd_list_lr(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; struct svec lrouters; vtep_ctl_context_populate_cache(ctx); svec_init(&lrouters); SHASH_FOR_EACH (node, &vtepctl_ctx->lrouters) { struct vtep_ctl_lrouter *lr = node->data; svec_add(&lrouters, lr->name); } output_sorted(&lrouters, &ctx->output); svec_destroy(&lrouters); } static void cmd_lr_exists(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); vtep_ctl_context_populate_cache(ctx); if (!find_lrouter(vtepctl_ctx, ctx->argv[1], false)) { vtep_ctl_exit(2); } } static void add_ucast_entry(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *mac; const char *encap; const char *dst_ip; struct vteprec_physical_locator *ploc_cfg; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); mac = ctx->argv[2]; if (ctx->argc == 4) { encap = "vxlan_over_ipv4"; dst_ip = ctx->argv[3]; } else { encap = ctx->argv[3]; dst_ip = ctx->argv[4]; } ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { ploc_cfg = vteprec_physical_locator_insert(ctx->txn); vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip); vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } if (local) { struct vteprec_ucast_macs_local *ucast_cfg; ucast_cfg = shash_find_data(&ls->ucast_local, mac); if (!ucast_cfg) { ucast_cfg = vteprec_ucast_macs_local_insert(ctx->txn); vteprec_ucast_macs_local_set_MAC(ucast_cfg, mac); vteprec_ucast_macs_local_set_logical_switch(ucast_cfg, ls->ls_cfg); shash_add(&ls->ucast_local, mac, ucast_cfg); } vteprec_ucast_macs_local_set_locator(ucast_cfg, ploc_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg; ucast_cfg = shash_find_data(&ls->ucast_remote, mac); if (!ucast_cfg) { ucast_cfg = vteprec_ucast_macs_remote_insert(ctx->txn); vteprec_ucast_macs_remote_set_MAC(ucast_cfg, mac); vteprec_ucast_macs_remote_set_logical_switch(ucast_cfg, ls->ls_cfg); shash_add(&ls->ucast_remote, mac, ucast_cfg); } vteprec_ucast_macs_remote_set_locator(ucast_cfg, ploc_cfg); } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_add_ucast_local(struct ctl_context *ctx) { add_ucast_entry(ctx, true); } static void cmd_add_ucast_remote(struct ctl_context *ctx) { add_ucast_entry(ctx, false); } static void del_ucast_entry(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; struct shash *ucast_shash; struct shash_node *node; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; node = shash_find(ucast_shash, ctx->argv[2]); if (!node) { return; } if (local) { struct vteprec_ucast_macs_local *ucast_cfg = node->data; vteprec_ucast_macs_local_delete(ucast_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg = node->data; vteprec_ucast_macs_remote_delete(ucast_cfg); } shash_delete(ucast_shash, node); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_del_ucast_local(struct ctl_context *ctx) { del_ucast_entry(ctx, true); } static void cmd_del_ucast_remote(struct ctl_context *ctx) { del_ucast_entry(ctx, false); } static void commit_mcast_entries(struct vtep_ctl_mcast_mac *mcast_mac) { struct vtep_ctl_ploc *ploc; struct vteprec_physical_locator **locators = NULL; size_t n_locators; int i; n_locators = ovs_list_size(&mcast_mac->locators); ovs_assert(n_locators); locators = xmalloc(n_locators * sizeof *locators); i = 0; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { locators[i] = (struct vteprec_physical_locator *)ploc->ploc_cfg; i++; } vteprec_physical_locator_set_set_locators(mcast_mac->ploc_set_cfg, locators, n_locators); free(locators); } static void add_mcast_entry(struct ctl_context *ctx, struct vtep_ctl_lswitch *ls, const char *mac, const char *encap, const char *dst_ip, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash *mcast_shash; struct vtep_ctl_mcast_mac *mcast_mac; struct vteprec_physical_locator *ploc_cfg; struct vteprec_physical_locator_set *ploc_set_cfg; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; /* Physical locator sets are immutable, so allocate a new one. */ ploc_set_cfg = vteprec_physical_locator_set_insert(ctx->txn); mcast_mac = shash_find_data(mcast_shash, mac); if (!mcast_mac) { mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mac, ploc_set_cfg, local); if (local) { mcast_mac->local_cfg = vteprec_mcast_macs_local_insert(ctx->txn); vteprec_mcast_macs_local_set_MAC(mcast_mac->local_cfg, mac); vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); vteprec_mcast_macs_local_set_logical_switch(mcast_mac->local_cfg, ls->ls_cfg); mcast_mac->remote_cfg = NULL; } else { mcast_mac->remote_cfg = vteprec_mcast_macs_remote_insert(ctx->txn); vteprec_mcast_macs_remote_set_MAC(mcast_mac->remote_cfg, mac); vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); vteprec_mcast_macs_remote_set_logical_switch(mcast_mac->remote_cfg, ls->ls_cfg); mcast_mac->local_cfg = NULL; } } else { mcast_mac->ploc_set_cfg = ploc_set_cfg; if (local) { vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); } else { vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); } } ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { ploc_cfg = vteprec_physical_locator_insert(ctx->txn); vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip); vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } add_ploc_to_mcast_mac(mcast_mac, ploc_cfg); commit_mcast_entries(mcast_mac); } static void del_mcast_entry(struct ctl_context *ctx, struct vtep_ctl_lswitch *ls, const char *mac, const char *encap, const char *dst_ip, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vteprec_physical_locator_set *ploc_set_cfg; struct vteprec_physical_locator *ploc_cfg; struct vtep_ctl_mcast_mac *mcast_mac; struct shash *mcast_shash; struct shash_node *mcast_node; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; mcast_node = shash_find(mcast_shash, mac); if (!mcast_node || !mcast_node->data) { return; } mcast_mac = mcast_node->data; ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { /* Couldn't find the physical locator, so just ignore. */ return; } /* Physical locator sets are immutable, so allocate a new one. */ ploc_set_cfg = vteprec_physical_locator_set_insert(ctx->txn); mcast_mac->ploc_set_cfg = ploc_set_cfg; del_ploc_from_mcast_mac(mcast_mac, ploc_cfg); if (ovs_list_is_empty(&mcast_mac->locators)) { vteprec_physical_locator_set_delete(ploc_set_cfg); if (local) { vteprec_mcast_macs_local_delete(mcast_mac->local_cfg); } else { vteprec_mcast_macs_remote_delete(mcast_mac->remote_cfg); } free(mcast_node->data); shash_delete(mcast_shash, mcast_node); } else { if (local) { vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); } else { vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); } commit_mcast_entries(mcast_mac); } } static void add_del_mcast_entry(struct ctl_context *ctx, bool add, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *mac; const char *encap; const char *dst_ip; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); mac = ctx->argv[2]; if (ctx->argc == 4) { encap = "vxlan_over_ipv4"; dst_ip = ctx->argv[3]; } else { encap = ctx->argv[3]; dst_ip = ctx->argv[4]; } if (add) { add_mcast_entry(ctx, ls, mac, encap, dst_ip, local); } else { del_mcast_entry(ctx, ls, mac, encap, dst_ip, local); } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_add_mcast_local(struct ctl_context *ctx) { add_del_mcast_entry(ctx, true, true); } static void cmd_add_mcast_remote(struct ctl_context *ctx) { add_del_mcast_entry(ctx, true, false); } static void cmd_del_mcast_local(struct ctl_context *ctx) { add_del_mcast_entry(ctx, false, true); } static void cmd_del_mcast_remote(struct ctl_context *ctx) { add_del_mcast_entry(ctx, false, false); } static void clear_macs(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const struct shash_node *node; struct shash *ucast_shash; struct shash *mcast_shash; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; SHASH_FOR_EACH (node, ucast_shash) { if (local) { struct vteprec_ucast_macs_local *ucast_cfg = node->data; vteprec_ucast_macs_local_delete(ucast_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg = node->data; vteprec_ucast_macs_remote_delete(ucast_cfg); } } SHASH_FOR_EACH (node, mcast_shash) { struct vtep_ctl_mcast_mac *mcast_mac = node->data; if (local) { vteprec_mcast_macs_local_delete(mcast_mac->local_cfg); } else { vteprec_mcast_macs_remote_delete(mcast_mac->remote_cfg); } } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_clear_local_macs(struct ctl_context *ctx) { clear_macs(ctx, true); } static void cmd_clear_remote_macs(struct ctl_context *ctx) { clear_macs(ctx, false); } static void list_macs(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const struct shash_node *node; struct shash *ucast_shash; struct svec ucast_macs; struct shash *mcast_shash; struct svec mcast_macs; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; svec_init(&ucast_macs); SHASH_FOR_EACH (node, ucast_shash) { struct vteprec_ucast_macs_local *ucast_local = node->data; struct vteprec_ucast_macs_remote *ucast_remote = node->data; struct vteprec_physical_locator *ploc_cfg; char *entry; ploc_cfg = local ? ucast_local->locator : ucast_remote->locator; entry = xasprintf(" %s -> %s/%s", node->name, ploc_cfg->encapsulation_type, ploc_cfg->dst_ip); svec_add_nocopy(&ucast_macs, entry); } ds_put_format(&ctx->output, "ucast-mac-%s\n", local ? "local" : "remote"); output_sorted(&ucast_macs, &ctx->output); ds_put_char(&ctx->output, '\n'); svec_destroy(&ucast_macs); svec_init(&mcast_macs); SHASH_FOR_EACH (node, mcast_shash) { struct vtep_ctl_mcast_mac *mcast_mac = node->data; struct vtep_ctl_ploc *ploc; char *entry; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { entry = xasprintf(" %s -> %s/%s", node->name, ploc->ploc_cfg->encapsulation_type, ploc->ploc_cfg->dst_ip); svec_add_nocopy(&mcast_macs, entry); } } ds_put_format(&ctx->output, "mcast-mac-%s\n", local ? "local" : "remote"); output_sorted(&mcast_macs, &ctx->output); ds_put_char(&ctx->output, '\n'); svec_destroy(&mcast_macs); } static void cmd_list_local_macs(struct ctl_context *ctx) { list_macs(ctx, true); } static void cmd_list_remote_macs(struct ctl_context *ctx) { list_macs(ctx, false); } static void verify_managers(const struct vteprec_global *vtep_global) { size_t i; vteprec_global_verify_managers(vtep_global); for (i = 0; i < vtep_global->n_managers; ++i) { const struct vteprec_manager *mgr = vtep_global->managers[i]; vteprec_manager_verify_target(mgr); } } static void pre_manager(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_managers); ovsdb_idl_add_column(ctx->idl, &vteprec_manager_col_target); ovsdb_idl_add_column(ctx->idl, &vteprec_manager_col_inactivity_probe); } static void cmd_get_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; struct svec targets; size_t i; verify_managers(vtep_global); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < vtep_global->n_managers; i++) { svec_add(&targets, vtep_global->managers[i]->target); } svec_sort_unique(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_managers(const struct vtep_ctl_context *vtepctl_ctx) { const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; size_t i; /* Delete Manager rows pointed to by 'managers' column. */ for (i = 0; i < vtep_global->n_managers; i++) { vteprec_manager_delete(vtep_global->managers[i]); } /* Delete 'Manager' row refs in 'managers' column. */ vteprec_global_set_managers(vtep_global, NULL, 0); } static void cmd_del_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; verify_managers(vtep_global); delete_managers(vtepctl_ctx); } static void insert_managers(struct vtep_ctl_context *vtepctl_ctx, char *targets[], size_t n, struct shash *options) { struct vteprec_manager **managers; size_t i; const char *inactivity_probe = shash_find_data(options, "--inactivity-probe"); /* Insert each manager in a new row in Manager table. */ managers = xmalloc(n * sizeof *managers); for (i = 0; i < n; i++) { if (stream_verify_name(targets[i]) && pstream_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } managers[i] = vteprec_manager_insert(vtepctl_ctx->base.txn); vteprec_manager_set_target(managers[i], targets[i]); if (inactivity_probe) { int64_t msecs = atoll(inactivity_probe); vteprec_manager_set_inactivity_probe(managers[i], &msecs, 1); } } /* Store uuids of new Manager rows in 'managers' column. */ vteprec_global_set_managers(vtepctl_ctx->vtep_global, managers, n); free(managers); } static void cmd_set_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const size_t n = ctx->argc - 1; verify_managers(vtepctl_ctx->vtep_global); delete_managers(vtepctl_ctx); insert_managers(vtepctl_ctx, &ctx->argv[1], n, &ctx->options); } /* Parameter commands. */ static const struct ctl_table_class tables[VTEPREC_N_TABLES] = { [VTEPREC_TABLE_LOGICAL_SWITCH].row_ids[0] = {&vteprec_logical_switch_col_name, NULL, NULL}, [VTEPREC_TABLE_MANAGER].row_ids[0] = {&vteprec_manager_col_target, NULL, NULL}, [VTEPREC_TABLE_PHYSICAL_PORT].row_ids[0] = {&vteprec_physical_port_col_name, NULL, NULL}, [VTEPREC_TABLE_PHYSICAL_SWITCH].row_ids[0] = {&vteprec_physical_switch_col_name, NULL, NULL}, [VTEPREC_TABLE_LOGICAL_ROUTER].row_ids[0] = {&vteprec_logical_router_col_name, NULL, NULL}, }; static void vtep_ctl_context_init_command(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command, bool last_command) { ctl_context_init_command(&vtepctl_ctx->base, command, last_command); vtepctl_ctx->verified_ports = false; } static void vtep_ctl_context_init(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, const struct vteprec_global *vtep_global, struct ovsdb_symbol_table *symtab) { ctl_context_init(&vtepctl_ctx->base, command, idl, txn, symtab, vtep_ctl_context_invalidate_cache); if (command) { vtepctl_ctx->verified_ports = false; } vtepctl_ctx->vtep_global = vtep_global; vtepctl_ctx->cache_valid = false; } static void vtep_ctl_context_done_command(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command) { ctl_context_done_command(&vtepctl_ctx->base, command); } static void vtep_ctl_context_done(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command) { ctl_context_done(&vtepctl_ctx->base, command); } static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; ovsdb_idl_add_table(idl, &vteprec_table_global); for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct vtep_ctl_context vtepctl_ctx; ds_init(&c->output); c->table = NULL; vtep_ctl_context_init(&vtepctl_ctx, c, idl, NULL, NULL, NULL); (c->syntax->prerequisites)(&vtepctl_ctx.base); if (vtepctl_ctx.base.error) { ctl_fatal("%s", vtepctl_ctx.base.error); } vtep_ctl_context_done(&vtepctl_ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static bool do_vtep_ctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; const struct vteprec_global *vtep_global; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct vtep_ctl_context vtepctl_ctx; struct ctl_command *c; struct shash_node *node; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ovsdb_idl_txn_add_comment(txn, "vtep-ctl: %s", args); vtep_global = vteprec_global_first(idl); if (!vtep_global) { /* XXX add verification that table is empty */ vtep_global = vteprec_global_insert(txn); } symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } vtep_ctl_context_init(&vtepctl_ctx, NULL, idl, txn, vtep_global, symtab); for (c = commands; c < &commands[n_commands]; c++) { vtep_ctl_context_init_command(&vtepctl_ctx, c, c == &commands[n_commands - 1]); if (c->syntax->run) { (c->syntax->run)(&vtepctl_ctx.base); } if (vtepctl_ctx.base.error) { ctl_fatal("%s", vtepctl_ctx.base.error); } vtep_ctl_context_done_command(&vtepctl_ctx, c); if (vtepctl_ctx.base.try_again) { vtep_ctl_context_done(&vtepctl_ctx, NULL); goto try_again; } } vtep_ctl_context_done(&vtepctl_ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created " "(e.g. with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { vtep_ctl_context_init(&vtepctl_ctx, c, idl, txn, vtep_global, symtab); (c->syntax->postprocess)(&vtepctl_ctx.base); if (vtepctl_ctx.base.error) { ctl_fatal("%s", vtepctl_ctx.base.error); } vtep_ctl_context_done(&vtepctl_ctx, c); } } } error = xstrdup(ovsdb_idl_txn_get_error(txn)); ovsdb_idl_txn_destroy(txn); txn = the_idl_txn = NULL; switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", error); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } free(error); ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); ovsdb_idl_destroy(idl); return true; try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ if (txn) { ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } free(error); return false; } static const struct ctl_command_syntax vtep_commands[] = { /* Physical Switch commands. */ {"add-ps", 1, 1, NULL, pre_get_info, cmd_add_ps, NULL, "--may-exist", RW}, {"del-ps", 1, 1, NULL, pre_get_info, cmd_del_ps, NULL, "--if-exists", RW}, {"list-ps", 0, 0, NULL, pre_get_info, cmd_list_ps, NULL, "", RO}, {"ps-exists", 1, 1, NULL, pre_get_info, cmd_ps_exists, NULL, "", RO}, /* Port commands. */ {"list-ports", 1, 1, NULL, pre_get_info, cmd_list_ports, NULL, "", RO}, {"add-port", 2, 2, NULL, pre_get_info, cmd_add_port, NULL, "--may-exist", RW}, {"del-port", 2, 2, NULL, pre_get_info, cmd_del_port, NULL, "--if-exists", RW}, /* Logical Switch commands. */ {"add-ls", 1, 1, NULL, pre_get_info, cmd_add_ls, NULL, "--may-exist", RW}, {"del-ls", 1, 1, NULL, pre_get_info, cmd_del_ls, NULL, "--if-exists", RW}, {"list-ls", 0, 0, NULL, pre_get_info, cmd_list_ls, NULL, "", RO}, {"ls-exists", 1, 1, NULL, pre_get_info, cmd_ls_exists, NULL, "", RO}, {"list-bindings", 2, 2, NULL, pre_get_info, cmd_list_bindings, NULL, "", RO}, {"bind-ls", 4, 4, NULL, pre_get_info, cmd_bind_ls, NULL, "", RO}, {"unbind-ls", 3, 3, NULL, pre_get_info, cmd_unbind_ls, NULL, "", RO}, {"set-replication-mode", 2, 2, "LS MODE", pre_get_info, cmd_set_replication_mode, NULL, "", RW}, {"get-replication-mode", 1, 1, "LS", pre_get_info, cmd_get_replication_mode, NULL, "", RO}, /* Logical Router commands. */ {"add-lr", 1, 1, NULL, pre_get_info, cmd_add_lr, NULL, "--may-exist", RW}, {"del-lr", 1, 1, NULL, pre_get_info, cmd_del_lr, NULL, "--if-exists", RW}, {"list-lr", 0, 0, NULL, pre_get_info, cmd_list_lr, NULL, "", RO}, {"lr-exists", 1, 1, NULL, pre_get_info, cmd_lr_exists, NULL, "", RO}, /* MAC binding commands. */ {"add-ucast-local", 3, 4, NULL, pre_get_info, cmd_add_ucast_local, NULL, "", RW}, {"del-ucast-local", 2, 2, NULL, pre_get_info, cmd_del_ucast_local, NULL, "", RW}, {"add-mcast-local", 3, 4, NULL, pre_get_info, cmd_add_mcast_local, NULL, "", RW}, {"del-mcast-local", 3, 4, NULL, pre_get_info, cmd_del_mcast_local, NULL, "", RW}, {"clear-local-macs", 1, 1, NULL, pre_get_info, cmd_clear_local_macs, NULL, "", RO}, {"list-local-macs", 1, 1, NULL, pre_get_info, cmd_list_local_macs, NULL, "", RO}, {"add-ucast-remote", 3, 4, NULL, pre_get_info, cmd_add_ucast_remote, NULL, "", RW}, {"del-ucast-remote", 2, 2, NULL, pre_get_info, cmd_del_ucast_remote, NULL, "", RW}, {"add-mcast-remote", 3, 4, NULL, pre_get_info, cmd_add_mcast_remote, NULL, "", RW}, {"del-mcast-remote", 3, 4, NULL, pre_get_info, cmd_del_mcast_remote, NULL, "", RW}, {"clear-remote-macs", 1, 1, NULL, pre_get_info, cmd_clear_remote_macs, NULL, "", RO}, {"list-remote-macs", 1, 1, NULL, pre_get_info, cmd_list_remote_macs, NULL, "", RO}, /* Manager commands. */ {"get-manager", 0, 0, NULL, pre_manager, cmd_get_manager, NULL, "", RO}, {"del-manager", 0, 0, NULL, pre_manager, cmd_del_manager, NULL, "", RW}, {"set-manager", 1, INT_MAX, NULL, pre_manager, cmd_set_manager, NULL, "--inactivity-probe=", RW}, {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; /* Registers vsctl and common db commands. */ static void vtep_ctl_cmd_init(void) { ctl_init(&vteprec_idl_class, vteprec_table_classes, tables, cmd_show_tables, vtep_ctl_exit); ctl_register_commands(vtep_commands); } openvswitch-3.7.0~git20260211.8c6ebf8/vtep/vtep-idl.ann000066400000000000000000000005371514270232600221650ustar00rootroot00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates vswitch.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "vteprec_" s["idlHeader"] = "\"vtep/vtep-idl.h\"" openvswitch-3.7.0~git20260211.8c6ebf8/vtep/vtep.ovsschema000066400000000000000000000263101514270232600226300ustar00rootroot00000000000000{ "name": "hardware_vtep", "cksum": "353943336 11434", "tables": { "Global": { "columns": { "managers": { "type": {"key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}, "switches": { "type": {"key": {"type": "uuid", "refTable": "Physical_Switch"}, "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}} }, "maxRows": 1, "isRoot": true}, "Physical_Switch": { "columns": { "ports": { "type": {"key": {"type": "uuid", "refTable": "Physical_Port"}, "min": 0, "max": "unlimited"}}, "name": {"type": "string"}, "description": {"type": "string"}, "management_ips": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "tunnel_ips": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "tunnels": { "type": {"key": {"type": "uuid", "refTable": "Tunnel"}, "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "switch_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["name"]]}, "Physical_Port": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "vlan_bindings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "Logical_Switch"}, "min": 0, "max": "unlimited"}}, "acl_bindings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "ACL"}, "min": 0, "max": "unlimited"}}, "vlan_stats": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "Logical_Binding_Stats"}, "min": 0, "max": "unlimited"}, "ephemeral": true}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "port_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Tunnel": { "columns": { "local": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "remote": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "bfd_config_local": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_config_remote": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_params": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Logical_Binding_Stats": { "columns": { "bytes_from_local": {"type": "integer", "ephemeral": true}, "packets_from_local": {"type": "integer", "ephemeral": true}, "bytes_to_local": {"type": "integer", "ephemeral": true}, "packets_to_local": {"type": "integer", "ephemeral": true}}}, "Logical_Switch": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "tunnel_key": {"type": {"key": "integer", "min": 0, "max": 1}}, "replication_mode": { "type": { "key": { "enum": ["set", ["service_node", "source_node"]], "type": "string"},"min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true, "indexes": [["name"]]}, "Ucast_Macs_Local": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Ucast_Macs_Remote": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Mcast_Macs_Local": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator_set": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator_Set"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Mcast_Macs_Remote": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator_set": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator_Set"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Logical_Router": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "switch_binding": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "Logical_Switch"}, "min": 0, "max": "unlimited"}}, "static_routes": { "type": {"key": {"type": "string"}, "value": {"type" : "string"}, "min": 0, "max": "unlimited"}}, "acl_binding": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "ACL"}, "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "LR_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "isRoot": true, "indexes": [["name"]]}, "Arp_Sources_Local": { "columns": { "src_mac": {"type": "string"}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}}, "isRoot": true}, "Arp_Sources_Remote": { "columns": { "src_mac": {"type": "string"}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}}, "isRoot": true}, "Physical_Locator_Set": { "columns": { "locators": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}, "min": 1, "max": "unlimited"}, "mutable": false}}}, "Physical_Locator": { "columns": { "encapsulation_type": { "type": { "key": { "enum": ["set", ["vxlan_over_ipv4"]], "type": "string"}}, "mutable": false}, "dst_ip": {"type": "string", "mutable": false}, "tunnel_key": {"type": {"key": "integer", "min": 0, "max": 1}}}, "indexes": [["encapsulation_type", "dst_ip", "tunnel_key"]]}, "ACL_entry": { "columns": { "sequence": {"type": "integer"}, "source_mac": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_mac": { "type": { "key": "string", "min": 0, "max": 1}}, "ethertype": { "type": { "key": "string", "min": 0, "max": 1}}, "source_ip": { "type": { "key": "string", "min": 0, "max": 1}}, "source_mask": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_ip": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_mask": { "type": { "key": "string", "min": 0, "max": 1}}, "protocol": { "type": { "key": "integer", "min": 0, "max": 1}}, "source_port_min": { "type": { "key": "integer", "min": 0, "max": 1}}, "source_port_max": { "type": { "key": "integer", "min": 0, "max": 1}}, "dest_port_min": { "type": { "key": "integer", "min": 0, "max": 1}}, "dest_port_max": { "type": { "key": "integer", "min": 0, "max": 1}}, "tcp_flags": { "type": { "key": "integer", "min": 0, "max": 1}}, "tcp_flags_mask": { "type": { "key": "integer", "min": 0, "max": 1}}, "icmp_code": { "type": { "key": "integer", "min": 0, "max": 1}}, "icmp_type": { "type": { "key": "integer", "min": 0, "max": 1}}, "direction": { "type": { "key": {"type": "string", "enum": ["set", ["ingress", "egress"]]}}}, "action": { "type": { "key": {"type": "string", "enum": ["set", ["permit", "deny"]]}}}, "acle_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "isRoot": true}, "ACL": { "columns": { "acl_entries": { "type": {"key": {"type": "uuid", "refTable": "ACL_entry"}, "min": 1, "max": "unlimited"}}, "acl_name": {"type": "string"}, "acl_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["acl_name"]], "isRoot": true}, "Manager": { "columns": { "target": {"type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["target"]], "isRoot": false}}, "version": "1.7.0"} openvswitch-3.7.0~git20260211.8c6ebf8/vtep/vtep.xml000066400000000000000000001475501514270232600214520ustar00rootroot00000000000000

    This schema specifies relations that a VTEP can use to integrate physical ports into logical switches maintained by a network virtualization controller such as NSX.

    Glossary:

    VTEP
    VXLAN Tunnel End Point, an entity which originates and/or terminates VXLAN tunnels.
    HSC
    Hardware Switch Controller.
    NVC
    Network Virtualization Controller, e.g. NSX.
    VRF
    Virtual Routing and Forwarding instance.

    Common Column

    Some tables contain a column, named other_config. This column has the same form and purpose each place that it appears, so we describe it here to save space later.

    other_config: map of string-string pairs

    Key-value pairs for configuring rarely used or proprietary features.

    Some tables do not have other_config column because no key-value pairs have yet been defined for them.

    Top-level configuration for a hardware VTEP. There must be exactly one record in the table.

    The physical switch or switches managed by the VTEP.

    When a physical switch integrates support for this VTEP schema, which is expected to be the most common case, this column should point to one record that represents the switch itself. In another possible implementation, a server or a VM presents a VTEP schema front-end interface to one or more physical switches, presumably communicating with those physical switches over a proprietary protocol. In that case, this column would point to one for each physical switch, and the set might change over time as the front-end server comes to represent a differing set of switches.

    These columns primarily configure the database server (ovsdb-server), not the hardware VTEP itself.

    Database clients to which the database server should connect or to which it should listen, along with options for how these connection should be configured. See the table for more information.
    The overall purpose of this column is described under Common Column at the beginning of this document.

    Configuration for a database connection to an Open vSwitch Database (OVSDB) client.

    The database server can initiate and maintain active connections to remote clients. It can also listen for database connections.

    Connection method for managers.

    The following connection methods are currently supported:

    ssl:host[:port]

    The specified SSL/TLS port (default: 6640) on the given host, which can either be a DNS name (if built with unbound library) or an IP address.

    SSL/TLS key and certificate configuration happens outside the database.

    tcp:host[:port]
    The specified TCP port (default: 6640) on the given host, which can either be a DNS name (if built with unbound library) or an IP address.
    pssl:[port][:host]

    Listens for SSL/TLS connections on the specified TCP port (default: 6640). If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address.

    ptcp:[port][:host]
    Listens for connections on the specified TCP port (default: 6640). If host, which can either be a DNS name (if built with unbound library) or an IP address, is specified, then connections are restricted to the resolved or specified local IP address.
    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to the client before sending an inactivity probe message. If the Open vSwitch database does not communicate with the client for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, the database server assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes. true if currently connected to this manager, false otherwise. A human-readable description of the last error on the connection to the manager; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the manager:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this manager last successfully connected to the database (in seconds). Value is empty if manager has never successfully connected. The amount of time since this manager last disconnected from the database (in seconds). Value is empty if manager has never disconnected. Space-separated list of the names of OVSDB locks that the connection holds. Omitted if the connection does not hold any locks. Space-separated list of the names of OVSDB locks that the connection is currently waiting to acquire. Omitted if the connection is not waiting for any locks. Space-separated list of the names of OVSDB locks that the connection has had stolen by another OVSDB client. Omitted if no locks have been stolen from this connection.

    When specifies a connection method that listens for inbound connections (e.g. ptcp: or pssl:) and more than one connection is actually active, the value is the number of active connections. Otherwise, this key-value pair is omitted.

    When multiple connections are active, status columns and key-value pairs (other than this one) report the status of one arbitrarily chosen connection.

    Additional configuration for a connection between the manager and the database server.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the manager and the database server. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    A physical switch that implements a VTEP. The physical ports within the switch. Tunnels created by this switch as instructed by the NVC. IPv4 or IPv6 addresses at which the switch may be contacted for management purposes.

    IPv4 or IPv6 addresses on which the switch may originate or terminate tunnels.

    This column is intended to allow a to determine the that terminates the tunnel represented by a .

    Symbolic name for the switch, such as its hostname. An extended description for the switch, such as its switch login banner.

    An entry in this column indicates to the NVC that this switch has encountered a fault. The switch must clear this column when the fault has been cleared.

    Indicates that the switch has been unable to process MAC entries requested by the NVC due to lack of table resources. Indicates that the switch has been unable to create tunnels requested by the NVC due to lack of resources. Indicates that the switch has been unable to create the logical router interfaces requested by the NVC due to conflicting configurations or a lack of hardware resources. Indicates that the switch has been unable to create the static routes requested by the NVC due to conflicting configurations or a lack of hardware resources. Indicates that the switch has been unable to create the logical router requested by the NVC due to conflicting configurations or a lack of hardware resources. Indicates that the switch does not support logical routing. Indicates that an error has occurred in the switch but that no more specific information is available. Indicates that the requested source node replication mode cannot be supported by the physical switch; this specifically means in this context that the physical switch lacks the capability to support source node replication mode. This error occurs when a controller attempts to set source node replication mode for one of the logical switches that the physical switch is keeping context for. An NVC that observes this error should take appropriate action (for example reverting the logical switch to service node replication mode). It is recommended that an NVC be proactive and test for support of source node replication by using a test logical switch on vtep physical switch nodes and then trying to change the replication mode to source node on this logical switch, checking for error. The NVC could remember this capability per vtep physical switch. Using mixed replication modes on a given logical switch is not recommended. Service node replication mode is considered a basic requirement since it only requires sending a packet to a single transport node, hence it is not expected that a switch should report that service node mode cannot be supported.
    The overall purpose of this column is described under Common Column at the beginning of this document.
    A tunnel created by a . Tunnel end-point local to the physical switch. Tunnel end-point remote to the physical switch.

    BFD, defined in RFC 5880, allows point to point detection of connectivity failures by occasional transmission of BFD control messages. VTEPs are expected to implement BFD.

    BFD operates by regularly transmitting BFD control messages at a rate negotiated independently in each direction. Each endpoint specifies the rate at which it expects to receive control messages, and the rate at which it's willing to transmit them. An endpoint which fails to receive BFD control messages for a period of three times the expected reception rate will signal a connectivity fault. In the case of a unidirectional connectivity issue, the system not receiving BFD control messages will signal the problem to its peer in the messages it transmits.

    A hardware VTEP is expected to use BFD to determine reachability of devices at the end of the tunnels with which it exchanges data. This can enable the VTEP to choose a functioning service node among a set of service nodes providing high availability. It also enables the NVC to report the health status of tunnels.

    In many cases the BFD peer of a hardware VTEP will be an Open vSwitch instance. The Open vSwitch implementation of BFD aims to comply faithfully with the requirements put forth in RFC 5880. Open vSwitch does not implement the optional Authentication or ``Echo Mode'' features.

    The HSC writes the key-value pairs in the column to specify the local configurations to be used for BFD sessions on this tunnel.

    Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC expected as destination for received BFD packets. The default is 00:23:20:00:00:01. Set to an IPv4 address to set the IP address that is expected as destination for received BFD packets. The default is 169.254.1.0.

    The column is the remote counterpart of the column. The NVC writes the key-value pairs in this column.

    Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the destination MAC to be used for transmitted BFD packets. The default is 00:23:20:00:00:01. Set to an IPv4 address to set the IP address used as destination for transmitted BFD packets. The default is 169.254.1.1.

    The NVC sets up key-value pairs in the column to enable and configure BFD.

    True to enable BFD on this . If not specified, BFD will not be enabled by default. The shortest interval, in milliseconds, at which this BFD session offers to receive BFD control messages. The remote endpoint may choose to send messages at a slower rate. Defaults to 1000. The shortest interval, in milliseconds, at which this BFD session is willing to transmit BFD control messages. Messages will actually be transmitted at a slower rate if the remote endpoint is not willing to receive as quickly as specified. Defaults to 100. An alternate receive interval, in milliseconds, that must be greater than or equal to . The implementation should switch from to when there is no obvious incoming data traffic at the tunnel, to reduce the CPU and bandwidth cost of monitoring an idle tunnel. This feature may be disabled by setting a value of 0. This feature is reset whenever or changes. When true, traffic received on the is used to indicate the capability of packet I/O. BFD control packets are still transmitted and received. At least one BFD control packet must be received every 100 * amount of time. Otherwise, even if traffic is received, the will be false. Set to true to notify the remote endpoint that traffic should not be forwarded to this system for some reason other than a connectivity failure on the interface being monitored. The typical underlying reason is ``concatenated path down,'' that is, that connectivity beyond the local system is down. Defaults to false. Set to true to make BFD accept only control messages with a tunnel key of zero. By default, BFD accepts control messages with any tunnel key.

    The VTEP sets key-value pairs in the column to report the status of BFD on this tunnel. When BFD is not enabled, with , the HSC clears all key-value pairs from .

    Set to true if the BFD session has been successfully enabled. Set to false if the VTEP cannot support BFD or has insufficient resources to enable BFD on this tunnel. The NVC will disable the BFD monitoring on the other side of the tunnel once this value is set to false. Reports the state of the BFD session. The BFD session is fully healthy and negotiated if UP. Reports whether the BFD session believes this may be used to forward traffic. Typically this means the local session is signaling UP, and the remote system isn't signaling a problem such as concatenated path down. A diagnostic code specifying the local system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Reports the state of the remote endpoint's BFD session. A diagnostic code specifying the remote system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. A short message providing further information about the BFD status (possibly including reasons why BFD could not be enabled).
    A port within a . Identifies how VLANs on the physical port are bound to logical switches. If, for example, the map contains a (VLAN, logical switch) pair, a packet that arrives on the port in the VLAN is considered to belong to the paired logical switch. A value of zero in the VLAN field means that untagged traffic on the physical port is mapped to the logical switch.

    Attach Access Control Lists (ACLs) to the physical port. The column consists of a map of VLAN tags to s. If the value of the VLAN tag in the map is 0, this means that the ACL is associated with the entire physical port. Non-zero values mean that the ACL is to be applied only on packets carrying that VLAN tag value. Switches will not necessarily support matching on the VLAN tag for all ACLs, and unsupported ACL bindings will cause errors to be reported. The binding of an ACL to a specific VLAN and the binding of an ACL to the entire physical port should not be combined on a single physical port. That is, a mix of zero and non-zero keys in the map is not recommended.

    Statistics for VLANs bound to logical switches on the physical port. An implementation that fully supports such statistics would populate this column with a mapping for every VLAN that is bound in . An implementation that does not support such statistics or only partially supports them would not populate this column or partially populate it, respectively. A value of zero in the VLAN field refers to untagged traffic on the physical port. Symbolic name for the port. The name ought to be unique within a given , but the database is not capable of enforcing this. An extended description for the port.

    An entry in this column indicates to the NVC that the physical port has encountered a fault. The switch must clear this column when the error has been cleared.

    Indicates that a VLAN-to-logical-switch mapping requested by the controller could not be instantiated by the switch because of a conflict with local configuration.

    Indicates that an error has occurred in associating an ACL with a port.

    Indicates that an error has occurred on the port but that no more specific information is available.

    The overall purpose of this column is described under Common Column at the beginning of this document.
    Reports statistics for the with which a VLAN on a is associated. These statistics count only packets to which the binding applies. Number of packets sent by the . Number of bytes in packets sent by the . Number of packets received by the . Number of bytes in packets received by the .
    A logical Ethernet switch, whose implementation may span physical and virtual media, possibly crossing L3 domains via tunnels; a logical layer-2 domain; an Ethernet broadcast domain.

    Tunnel protocols tend to have a field that allows the tunnel to be partitioned into sub-tunnels: VXLAN has a VNI, GRE has a key, CAPWAP has a WSI, and so on. We call these generically ``tunnel keys.'' Given that one needs to use a tunnel key at all, there are at least two reasonable ways to assign their values:

    • Per + pair. That is, each logical switch may be assigned a different tunnel key on every . This model is especially flexible.

      In this model, carries the tunnel key. Therefore, one record will exist for each logical switch carried at a given IP destination.

    • Per . That is, every tunnel associated with a particular logical switch carries the same tunnel key, regardless of the to which the tunnel is addressed. This model may ease switch implementation because it imposes fewer requirements on the hardware datapath.

      In this model, carries the tunnel key. Therefore, one record will exist for each IP destination.

    This column is used only in the tunnel key per model (see above), because only in that model is there a tunnel key associated with a logical switch.

    For vxlan_over_ipv4 encapsulation, when the tunnel key per model is in use, this column is the VXLAN VNI that identifies a logical switch. It must be in the range 0 to 16,777,215.

    For handling L2 broadcast, multicast and unknown unicast traffic, packets can be sent to all members of a logical switch referenced by a physical switch. There are different modes to replicate the packets. The default mode of replication is to send the traffic to a service node, which can be a hypervisor, server or appliance, and let the service node handle replication to other transport nodes (hypervisors or other VTEP physical switches). This mode is called service node replication. An alternate mode of replication, called source node replication involves the source node sending to all other transport nodes. Hypervisors are always responsible for doing their own replication for locally attached VMs in both modes. Service node replication mode is the default and considered a basic requirement because it only requires sending the packet to a single transport node.

    This optional column defines the replication mode per . There are 2 valid values, service_node and source_node. If the column is not set, the replication mode defaults to service_node.

    Symbolic name for the logical switch. An extended description for the logical switch, such as its switch login banner. The overall purpose of this column is described under Common Column at the beginning of this document.

    Mapping of unicast MAC addresses to tunnels (physical locators). This table is written by the HSC, so it contains the MAC addresses that have been learned on physical ports by a VTEP.

    A MAC address that has been learned by the VTEP. The Logical switch to which this mapping applies. The physical locator to be used to reach this MAC address. In this table, the physical locator will be one of the tunnel IP addresses of the appropriate VTEP. The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of unicast MAC addresses to tunnels (physical locators). This table is written by the NVC, so it contains the MAC addresses that the NVC has learned. These include VM MAC addresses, in which case the physical locators will be hypervisor IP addresses. The NVC will also report MACs that it has learned from other HSCs in the network, in which case the physical locators will be tunnel IP addresses of the corresponding VTEPs.

    A MAC address that has been learned by the NVC. The Logical switch to which this mapping applies. The physical locator to be used to reach this MAC address. In this table, the physical locator will be either a hypervisor IP address or a tunnel IP addresses of another VTEP. The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of multicast MAC addresses to tunnels (physical locators). This table is written by the HSC, so it contains the MAC addresses that have been learned on physical ports by a VTEP. These may be learned by IGMP snooping, for example. This table also specifies how to handle unknown unicast and broadcast packets.

    A MAC address that has been learned by the VTEP.

    The keyword unknown-dst is used as a special ``Ethernet address'' that indicates the locations to which packets in a logical switch whose destination addresses do not otherwise appear in (for unicast addresses) or (for multicast addresses) should be sent.

    The Logical switch to which this mapping applies. The physical locator set to be used to reach this MAC address. In this table, the physical locator set will be contain one or more tunnel IP addresses of the appropriate VTEP(s). The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of multicast MAC addresses to tunnels (physical locators). This table is written by the NVC, so it contains the MAC addresses that the NVC has learned. This table also specifies how to handle unknown unicast and broadcast packets.

    Multicast packet replication may be handled by a service node, in which case the physical locators will be IP addresses of service nodes. If the VTEP supports replication onto multiple tunnels, using source node replication, then this may be used to replicate directly onto VTEP-hypervisor or VTEP-VTEP tunnels.

    A MAC address that has been learned by the NVC.

    The keyword unknown-dst is used as a special ``Ethernet address'' that indicates the locations to which packets in a logical switch whose destination addresses do not otherwise appear in (for unicast addresses) or (for multicast addresses) should be sent.

    The Logical switch to which this mapping applies. The physical locator set to be used to reach this MAC address. In this table, the physical locator set will be either a set of service nodes when service node replication is used or the set of transport nodes (defined as hypervisors or VTEPs) participating in the associated logical switch, when source node replication is used. When service node replication is used, the VTEP should send packets to one member of the locator set that is known to be healthy and reachable, which could be determined by BFD. When source node replication is used, the VTEP should send packets to all members of the locator set. The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    A logical router, or VRF. A logical router may be connected to one or more logical switches. Subnet addresses and interface addresses may be configured on the interfaces.

    Maps from an IPv4 or IPv6 address prefix in CIDR notation to a logical switch. Multiple prefixes may map to the same switch. By writing a 32-bit (or 128-bit for v6) address with a /N prefix length, both the router's interface address and the subnet prefix can be configured. For example, 192.68.1.1/24 creates a /24 subnet for the logical switch attached to the interface and assigns the address 192.68.1.1 to the router interface. One or more static routes, mapping IP prefixes to next hop IP addresses. Maps ACLs to logical router interfaces. The router interfaces are indicated using IP address notation, and must be the same interfaces created in the column. For example, an ACL could be associated with the logical router interface with an address of 192.68.1.1 as defined in the example above. Symbolic name for the logical router. An extended description for the logical router.

    An entry in this column indicates to the NVC that the HSC has encountered a fault in configuring state related to the logical router.

    Indicates that an error has occurred in associating an ACL with a logical router port.

    Indicates that an error has occurred in configuring the logical router but that no more specific information is available.

    The overall purpose of this column is described under Common Column at the beginning of this document.

    MAC address to be used when a VTEP issues ARP requests on behalf of a logical router.

    A distributed logical router is implemented by a set of VTEPs (both hardware VTEPs and vswitches). In order for a given VTEP to populate the local ARP cache for a logical router, it issues ARP requests with a source MAC address that is unique to the VTEP. A single per-VTEP MAC can be re-used across all logical networks. This table contains the MACs that are used by the VTEPs of a given HSC. The table provides the mapping from MAC to physical locator for each VTEP so that replies to the ARP requests can be sent back to the correct VTEP using the appropriate physical locator.

    The source MAC to be used by a given VTEP. The to use for replies to ARP requests from this MAC address.

    MAC address to be used when a remote VTEP issues ARP requests on behalf of a logical router.

    This table is the remote counterpart of . The NVC writes this table to notify the HSC of the MACs that will be used by remote VTEPs when they issue ARP requests on behalf of a distributed logical router.

    The source MAC to be used by a given VTEP. The to use for replies to ARP requests from this MAC address.

    A set of one or more s.

    This table exists only because OVSDB does not have a way to express the type ``map from string to one or more records.''

    Identifies an endpoint to which logical switch traffic may be encapsulated and forwarded.

    The vxlan_over_ipv4 encapsulation, the only encapsulation defined so far, can use either tunnel key model described in the ``Per Logical-Switch Tunnel Key'' section in the table. When the tunnel key per model is in use, the column in the table is filled with a VNI and the column in this table is empty; in the key-per-tunnel model, the opposite is true. The former model is older, and thus likely to be more widely supported. See the ``Per Logical-Switch Tunnel Key'' section in the table for further discussion of the model.

    The type of tunneling encapsulation.

    For vxlan_over_ipv4 encapsulation, the IPv4 address of the VXLAN tunnel endpoint.

    We expect that this column could be used for IPv4 or IPv6 addresses in encapsulations to be introduced later.

    This column is used only in the tunnel key per + model (see above).

    For vxlan_over_ipv4 encapsulation, when the + model is in use, this column is the VXLAN VNI. It must be in the range 0 to 16,777,215.

    Describes the individual entries that comprise an Access Control List.

    Each entry in the table is a single rule to match on certain header fields. While there are a large number of fields that can be matched on, most hardware cannot match on arbitrary combinations of fields. It is common to match on either L2 fields (described below in the L2 group of columns) or L3/L4 fields (the L3/L4 group of columns) but not both. The hardware switch controller may log an error if an ACL entry requires it to match on an incompatible mixture of fields.

    The sequence number for the ACL entry for the purpose of ordering entries in an ACL. Lower numbered entries are matched before higher numbered entries.

    Source MAC address, in the form xx:xx:xx:xx:xx:xx

    Destination MAC address, in the form xx:xx:xx:xx:xx:xx

    Ethertype in hexadecimal, in the form 0xAAAA

    Source IP address, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Mask that determines which bits of source_ip to match on, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Destination IP address, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Mask that determines which bits of dest_ip to match on, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Protocol number in the IPv4 header, or value of the "next header" field in the IPv6 header.

    Lower end of the range of source port values. The value specified is included in the range.

    Upper end of the range of source port values. The value specified is included in the range.

    Lower end of the range of destination port values. The value specified is included in the range.

    Upper end of the range of destination port values. The value specified is included in the range.

    Integer representing the value of TCP flags to match. For example, the SYN flag is the second least significant bit in the TCP flags. Hence a value of 2 would indicate that the "SYN" flag should be set (assuming an appropriate mask).

    Integer representing the mask to apply when matching TCP flags. For example, a value of 2 would imply that the "SYN" flag should be matched and all other flags ignored.

    ICMP type to be matched.

    ICMP code to be matched.

    Direction of traffic to match on the specified port, either "ingress" (toward the logical switch or router) or "egress" (leaving the logical switch or router).

    Action to take for this rule, either "permit" or "deny".

    An entry in this column indicates to the NVC that the ACL could not be configured as requested. The switch must clear this column when the error has been cleared.

    Indicates that an ACL entry requested by the controller could not be instantiated by the switch, e.g. because it requires an unsupported combination of fields to be matched.

    Indicates that an error has occurred in configuring the ACL entry but no more specific information is available.

    Access Control List table. Each ACL is constructed as a set of entries from the table. Packets that are not matched by any entry in the ACL are allowed by default.

    A set of references to entries in the table.

    A human readable name for the ACL, which may (for example) be displayed on the switch CLI.

    An entry in this column indicates to the NVC that the ACL could not be configured as requested. The switch must clear this column when the error has been cleared.

    Indicates that an ACL requested by the controller could not be instantiated by the switch, e.g., because it requires an unsupported combination of fields to be matched.

    Indicates that an ACL requested by the controller could not be instantiated by the switch due to a shortage of resources (e.g. TCAM space).

    Indicates that an error has occurred in configuring the ACL but no more specific information is available.

    openvswitch-3.7.0~git20260211.8c6ebf8/windows/000077500000000000000000000000001514270232600204525ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/.gitignore000066400000000000000000000054041514270232600224450ustar00rootroot00000000000000## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates ovs-windows-installer/Binaries.wxs ovs-windows-installer/Symbols.wxs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ x64/ build/ bld/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* #NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # NuGet Packages Directory packages/* ## TODO: If the tool you use requires repositories.config uncomment the next line #!packages/repositories.config # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) !packages/build/ # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # ========================= # Windows detritus # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ openvswitch-3.7.0~git20260211.8c6ebf8/windows/README.rst000066400000000000000000000042751514270232600221510ustar00rootroot00000000000000.. 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. Convention for heading levels in Open vSwitch documentation: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 Avoid deeper levels because they do not render well. ============== Windows README ============== This directory contains tooling to generate an MSI installer for Open vSwitch on Windows, including CLI executables, services and the Hyper-V vswitch forwarding extension. Requirements ------------ * Visual Studio 2013 Community, Professional, Premium or Ultimate editions can be used. Visual Studio Community 2013 is freely available from `visualstudio.com `__ * WiX Toolset 3.9 Download and install from `wixtoolset.org `__ * ``Microsoft_VC120_CRT_x86.msm`` This Windows merge module is available with Visual Studio and contains the Visual C++ 2013 x86 runtime redistributables files. Copy the file in the ``Redist`` directory. Open vSwitch installer ---------------------- The installer will be generated under the following path:: windows\ovs-windows-installer\bin\Release\OpenvSwitch.msi .. note:: The kernel driver needs to be signed. Build Instructions ------------------ Build the solution in the Visual Studio IDE or via command line:: msbuild ovs-windows-installer.sln /p:Platform=x86 /p:Configuration=Release Silent installation ------------------- :: msiexec /i OpenvSwitch.msi ADDLOCAL=OpenvSwitchCLI,OpenvSwitchDriver /l*v log.txt openvswitch-3.7.0~git20260211.8c6ebf8/windows/automake.mk000066400000000000000000000101551514270232600226130ustar00rootroot00000000000000# Copyright 2015 Cloudbase Solutions Srl # # 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. windows_installer: all #Userspace files needed for the installer cp -f $(top_srcdir)/datapath-windows/misc/OVS.psm1 windows/ovs-windows-installer/Services/OVS.psm1 cp -f $(top_srcdir)/vswitchd/vswitch.ovsschema windows/ovs-windows-installer/Services/vswitch.ovsschema cp -f $(top_srcdir)/vswitchd/ovs-vswitchd.exe windows/ovs-windows-installer/Services/ovs-vswitchd.exe cp -f $(top_srcdir)/ovsdb/ovsdb-server.exe windows/ovs-windows-installer/Services/ovsdb-server.exe cp -f $(top_srcdir)/utilities/*.exe windows/ovs-windows-installer/Binaries/ cp -f $(top_srcdir)/utilities/*.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-client.exe windows/ovs-windows-installer/Binaries/ovsdb-client.exe cp -f $(top_srcdir)/ovsdb/ovsdb-tool.exe windows/ovs-windows-installer/Binaries/ovsdb-tool.exe cp -f $(top_srcdir)/ovsdb/ovsdb-client.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-tool.pdb windows/ovs-windows-installer/Symbols/ #Third party files needed by the installer cp -f $(PTHREAD_WIN32_DIR_DLL_WIN_FORM)/*.dll windows/ovs-windows-installer/Binaries/ cp -f "/c/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC140_CRT_x86.msm" windows/ovs-windows-installer/Redist/Microsoft_VC140_CRT_x86.msm cp -f "/c/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC140_CRT_x64.msm" windows/ovs-windows-installer/Redist/Microsoft_VC140_CRT_x64.msm #Forwarding extension files needed for the installer cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/OVSExt.sys windows/ovs-windows-installer/Driver/Win8/ovsext.sys cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8.1/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8.1/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.sys windows/ovs-windows-installer/Driver/Win8.1/ovsext.sys cp -f $(top_srcdir)/datapath-windows/x64/Win10$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win10/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win10$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win10/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win10$(VSTUDIO_CONFIG)/package/ovsext.sys windows/ovs-windows-installer/Driver/Win10/ovsext.sys MSBuild.exe windows/ovs-windows-installer.sln //nologo //target:Build //p:Configuration="Release" //p:Version="$(PACKAGE_VERSION)" //p:Platform=$(PLATFORM) EXTRA_DIST += \ windows/automake.mk \ windows/README.rst \ windows/ovs-windows-installer.sln \ windows/ovs-windows-installer/Actions/OVSActions.js \ windows/ovs-windows-installer/CustomActions.wxs \ windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs \ windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs \ windows/ovs-windows-installer/License.rtf \ windows/ovs-windows-installer/Product.wxs \ windows/ovs-windows-installer/UI.wxs \ windows/ovs-windows-installer/images/bannrbmp.bmp \ windows/ovs-windows-installer/images/dlgbmp.bmp \ windows/ovs-windows-installer/ovs-windows-installer.wixproj openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer.sln000066400000000000000000000017131514270232600254640ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ovs-windows-installer", "ovs-windows-installer\ovs-windows-installer.wixproj", "{259905A2-7434-4190-8A33-8FBA67171DD6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x64.ActiveCfg = Release|x64 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x64.Build.0 = Release|x64 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x86.ActiveCfg = Release|x86 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/000077500000000000000000000000001514270232600247445ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Actions/000077500000000000000000000000001514270232600263445ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Actions/OVSActions.js000066400000000000000000000162661514270232600307050ustar00rootroot00000000000000/* Copyright 2015 Cloudbase Solutions Srl All Rights Reserved. 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. */ // http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx var Buttons = { OkOnly: 0, OkCancel: 1, AbortRetryIgnore: 2, YesNoCancel: 3 }; var Icons = { Critical: 16, Question: 32, Exclamation: 48, Information: 64 } var MsgKind = { Error: 0x01000000, Warning: 0x02000000, User: 0x03000000, Log: 0x04000000 }; // http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx var MsiActionStatus = { None: 0, Ok: 1, // success Cancel: 2, Abort: 3, Retry: 4, // aka suspend? Ignore: 5 // skip remaining actions; this is not an error. }; var ServiceStartAction = { Stop: "Stop", Start: "Start", Restart: "Restart" }; var ServiceStartMode = { Boot: "Boot", System: "System", Auto: "Auto", Manual: "Manual", Disabled: "Disabled" }; function throwException(num, msg) { throw { number: num, message: msg }; } function decimalToHexString(number) { if (number < 0) number = 0xFFFFFFFF + number + 1; return number.toString(16).toUpperCase(); } function logMessage(msg) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = "CustomActions: " + msg; Session.Message(MsgKind.Log, record); } function logMessageEx(msg, type) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = msg; Session.Message(type, record); } function logException(exc) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = exc.message == "" ? "An exception occurred: 0x" + decimalToHexString(exc.number) : exc.message; Session.Message(MsgKind.Error + Icons.Critical + Buttons.OkOnly, record); // Log the full exception as well record.StringData(0) = "CustomAction exception details: 0x" + decimalToHexString(exc.number) + " : " + exc.message; Session.Message(MsgKind.Log, record); } function runCommand(cmd, expectedReturnValue, envVars, windowStyle, waitOnReturn, workingDir) { var shell = new ActiveXObject("WScript.Shell"); logMessage("Running command: " + cmd); if (envVars) { var env = shell.Environment("Process"); for (var k in envVars) env(k) = envVars[k]; } if (typeof windowStyle == 'undefined') windowStyle = 0; if (typeof waitOnReturn == 'undefined') waitOnReturn = true; if (typeof workingDir == 'undefined') workingDir = null; if (workingDir) { shell.CurrentDirectory = workingDir; } var retVal = shell.run(cmd, windowStyle, waitOnReturn); if (waitOnReturn && expectedReturnValue != undefined && expectedReturnValue != null && retVal != expectedReturnValue) throwException(-1, "Command failed. Return value: " + retVal.toString()); logMessage("Command completed. Return value: " + retVal); return retVal; } function getWmiCimV2Svc() { return GetObject("winmgmts:\\\\.\\root\\cimv2"); } function getSafeArray(jsArr) { var dict = new ActiveXObject("Scripting.Dictionary"); for (var i = 0; i < jsArr.length; i++) dict.add(i, jsArr[i]); return dict.Items(); } function invokeWMIMethod(svc, methodName, inParamsValues, wmiSvc, jobOutParamName) { logMessage("Invoking " + methodName); var inParams = null; if (inParamsValues) { for (var k in inParamsValues) { if (!inParams) inParams = svc.Methods_(methodName).InParameters.SpawnInstance_(); var val = inParamsValues[k]; if (val instanceof Array) inParams[k] = getSafeArray(val); else inParams[k] = val; } } var outParams = svc.ExecMethod_(methodName, inParams); if (outParams.ReturnValue == 4096) { var job = wmiSvc.Get(outParams[jobOutParamName]); waitForJob(wmiSvc, job); } else if (outParams.ReturnValue != 0) throwException(-1, methodName + " failed. Return value: " + outParams.ReturnValue.toString()); return outParams; } function sleep(interval) { // WScript.Sleep is not supported in MSI's WSH. Here's a workaround for the moment. // interval is ignored var numPings = 2; cmd = "ping -n " + numPings + " 127.0.0.1"; var shell = new ActiveXObject("WScript.Shell"); shell.run(cmd, 0, true); } function getService(serviceName) { var wmiSvc = getWmiCimV2Svc(); return wmiSvc.ExecQuery("SELECT * FROM Win32_Service WHERE Name='" + serviceName + "'").ItemIndex(0); } function changeService(serviceName, startMode, startAction) { var svc = getService(serviceName); if ((startAction == ServiceStartAction.Stop || startAction == ServiceStartAction.Restart) && svc.Started) invokeWMIMethod(svc, "StopService"); if (startMode && svc.StartMode != startMode) invokeWMIMethod(svc, "ChangeStartMode", { "StartMode": (startMode == ServiceStartMode.Auto ? "Automatic" : startMode) }); if (startAction == ServiceStartAction.Restart && svc.Started) { var wmiSvc = getWmiCimV2Svc(); do { sleep(200); svc = wmiSvc.Get(svc.Path_); } while (svc.Started); } if ((startAction == ServiceStartAction.Start || startAction == ServiceStartAction.Restart) && !svc.Started) invokeWMIMethod(svc, "StartService"); } function runCommandAction() { var exceptionMsg = null; try { var data = Session.Property("CustomActionData").split('|'); var i = 0; var cmd = data[i++]; var expectedRetValue = data.length > i ? data[i++] : 0; var exceptionMsg = data.length > i ? data[i++] : null; var workingDir = data.length > i ? data[i++] : null; runCommand(cmd, expectedRetValue, null, 0, true, workingDir); return MsiActionStatus.Ok; } catch (ex) { if (exceptionMsg) { logMessageEx(exceptionMsg, MsgKind.Error + Icons.Critical + Buttons.OkOnly); // log also the original exception logMessage(ex.message); } else logException(ex); return MsiActionStatus.Abort; } } function changeServiceAction() { try { var data = Session.Property("CustomActionData").split('|'); var serviceName = data[0]; var startMode = data[1]; var startAction = data[2]; logMessage("Changing service " + serviceName + ", startMode: " + startMode + ", startAction: " + startAction); changeService(serviceName, startMode, startAction); return MsiActionStatus.Ok; } catch (ex) { logMessage(ex.message); return MsiActionStatus.Abort; } }openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Binaries/000077500000000000000000000000001514270232600265005ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Binaries/.gitignore000066400000000000000000000000171514270232600304660ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/CustomActions.wxs000066400000000000000000000104241514270232600303030ustar00rootroot00000000000000 openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Dialogs/000077500000000000000000000000001514270232600263265ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs000066400000000000000000000051571514270232600321210ustar00rootroot00000000000000 NOT Installed OR NOT PATCH Installed AND PATCH Installed AND PATCH NOT Installed OR NOT PATCH Installed AND PATCH 1 NOT Installed OR PATCH openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs000066400000000000000000000046621514270232600312350ustar00rootroot00000000000000 openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs000066400000000000000000000044101514270232600332070ustar00rootroot00000000000000 1 !(wix.WixUICostingPopupOptOut) OR CostingComplete = 1 Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs000066400000000000000000000042371514270232600322760ustar00rootroot00000000000000 1 openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/000077500000000000000000000000001514270232600261775ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/.gitignore000066400000000000000000000000441514270232600301650ustar00rootroot00000000000000* !.gitignore !Win8 !Win8.1 !Win10 openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win10/000077500000000000000000000000001514270232600270755ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win10/.gitignore000066400000000000000000000000171514270232600310630ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win8.1/000077500000000000000000000000001514270232600271635ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win8.1/.gitignore000066400000000000000000000000171514270232600311510ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win8/000077500000000000000000000000001514270232600270245ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Driver/Win8/.gitignore000066400000000000000000000000171514270232600310120ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/License.rtf000066400000000000000000001175401514270232600270530ustar00rootroot00000000000000{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} {\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f49\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f50\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;} {\f52\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f53\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f54\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f55\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);} {\f56\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f57\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f379\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f380\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} {\f382\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f383\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f386\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f387\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} {\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} {\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} {\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} {\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} {\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; \red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}}}{\*\rsidtbl \rsid2365717\rsid7145912\rsid7612545}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator alin.cloudbase} {\creatim\yr2015\mo4\dy2\hr16\min46}{\revtim\yr2015\mo5\dy25\hr20\min39}{\version3}{\edmins0}{\nofpages1}{\nofwords86}{\nofchars492}{\nofcharsws577}{\vern57439}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} \paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 \dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot7145912 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1 \pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5 \pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\tx959\tx1918\tx2877\tx3836\tx4795\tx5754\tx6713\tx7672\tx8631\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 Licensed under the Apache License, Version 2.0 (the "License"); \par \hich\af1\dbch\af31505\loch\f1 you may not use this file except in compliance with the License. \par \hich\af1\dbch\af31505\loch\f1 You may obtain a copy of the License at \par \par }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://www.apache.org/licenses/LICENSE-2.0 }{\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid7145912 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6e00000068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f004c004900430045004e00530045002d0032002e00 30000000795881f43b1d7f48af2c825dc485276300000000a5ab000069}}}{\fldrslt {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 http://www.apache.org/licenses/LICENSE-2.0}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj { \rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \par \par \hich\af1\dbch\af31505\loch\f1 Un\hich\af1\dbch\af31505\loch\f1 less required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, \par \hich\af1\dbch\af31505\loch\f1 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \par \hich\af1\dbch\af31505\loch\f1 See the License for the specific language governing\hich\af1\dbch\af31505\loch\f1 permissions and \par \hich\af1\dbch\af31505\loch\f1 limitations under the License. \par }\pard \ltrpar\qj \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang16\langfe1033\langnp16\insrsid2365717 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100e67505bed10600008b1a0000160000007468656d652f7468656d652f 7468656d65312e786d6cec59cf8b1b3714be17fa3f0c7377fc6bc63f9638c11edb499b6c1262272547d9963dca6a466624efc68440498e8542695a7a68a0b71e 4adb4002bda4b7fe276953da14fa2ff449331e4bb6dcdd2c292c256b58c6f2f79e3ebdf7e67b9ad1f98bf722ea1ce2841316b7dcf2b992ebe078cc26249eb5dc 5bc37ea1e13a5ca07882288b71cb5d62ee5ebcf0fe7be7d19e0871841db08ff91e6ab9a110f3bd62918f6118f1736c8e63f86dca920809f89acc8a93041d81df 88162ba552ad182112bb4e8c22703bc4d12f8f9cebd3291963f7c2ca798fc20cb1e072604c9381748d330b0d3b39284b045ff28026ce21a22d17e699b0a321be 275c87222ee087965b527f6ef1c2f922dacb8ca8d861abd9f5d55f6697194c0e2a6ace6436ca27f53cdfabb573ff0a40c536ae57efd57ab5dc9f02a0f118569a 72d17dfa9d66a7eb67580d945e5a7c77ebdd6ad9c06bfeab5b9cdbbefc1878054afd7b5bf87e3f80281a78054af1fe16def3ea95c033f00a94e26b5bf87aa9dd f5ea065e81424ae2832d74c9af5583d56a73c894d1cb5678d3f7faf54ae67c8d826ac8ab4b4e3165b1d8556b11bacb923e002490224162472ce7788ac650c501 a2649410e72a998550787314330ec3a54aa95faac27ff9f1d4958a08dac348b396bc8009df1a927c1c3e4ec85cb4dc0fc1abab41fe7ef1fddf2f9e392f1f3e7f f9f0a7978f1ebd7cf863eac8b0ba8ce2996ef5fadbcffe7af2b1f3e7b36f5e3ffec28ee73afeb71f3ef9f5e7cfed4058e93a04afbe7cfafbf3a7afbefaf48fef 1e5be0ed048d74f89044983bd7f091739345b030150293391e256f66310c11d12ddaf18ca318c9592cfe7b2234d0d79688220bae83cd08de4e40626cc04b8bbb 06e141982c04b178bc124606709f31da6189350a57e45c5a98878b78669f3c59e8b89b081ddae60e506ce4b7b79883b6129bcb20c406cd1b14c502cd708c8523 7f6307185b5677871023aefb649c30cea6c2b9439c0e22d6900cc9c8a8a6b5d16512415e963682906f2336fbb79d0ea3b65577f1a18984bb02510bf921a64618 2fa1854091cde51045540ff85524421bc9c13219ebb81e1790e919a6cce94d30e7369beb09ac574bfa1590177bdaf7e9323291892007369f5711633ab2cb0e82 1045731b7640e250c77ec00fa044917383091b7c9f997788fc0e7940f1ce74df26d848f7f16a700b9455a7b42e10f9cb22b1e4f2126646fd0e96748ab0921a10 7e43cf23121f2bee1bb2eeffb7b20e42faeaeb2796559d55416f27c47a475dde90f15db84df10e583221675fbbbb6811dfc070bb6c37b077d2fd4ebaddffbd74 efba9fdfbe60af351ae45b6e15d3adbadab8473bf7ed5342e9402c29becad5d69d43679af46150daa967569c3fc7cd43b89477324c60e0660952364ec2c44744 848310cd617f5f76a59319cf5ccfb833671cb6fd6ad8ea5be2e922da6793f471b55c968fa6a9787024d6e3253f1f87470d91a26bf5f52358ee5eb19da947e515 0169fb2624b4c94c12550b89fa6a5006493d9843d02c24d4cade0a8ba6854543ba5fa56a8b0550cbb3025b2707365c2dd7f7c0048ce0890a513c91794a53bdca ae4ae6dbccf4ae601a1500fb885505ac33dd945c772e4fae2e2db51364da20a1959b49424546f5301ea209ceaa538e9e84c69be6bab94ea9414f8642cd07a5b5 a6516ffc1b8bd3e61aec36b581c6ba52d0d8396ab9b5aa0f253346f3963b85c77eb88ce6503b5c6e79119dc1bbb3b148d21bfe34ca324fb8e8221ea60157a293 aa4144044e1c4aa2962b979fa781c64a4314b7720504e1cc926b82ac9c3572907433c9783ac563a1a75d1b91914ebf82c2a75a61fd55999f1e2c2dd902d23d08 2747ce882e929b084accaf9765002784c3db9f721acd0981d799b990adeb6fa33165b2abbf4f5435948e233a0f51d65174314fe14aca733aea5b1e03ed5bb666 08a81692ac118e66b2c1ea4135ba69de35520e3bbbeef14632729a68ae7ba6a12ab26bda55cc9861d506366279ba26afb15a8518344deff0a9746f4a6e73a575 1bfb84bc4b40c0f3f859baee091a82466d3d99414d32de9661a9d9d9a8d93b560b3c86da499a84a6fab595db8db8e53dc23a1d0c9eaaf383dd66d5c2d074b5af 549156e71efad1041bdd05f1e8c24be005155ca5120e1e12041ba281da93a4b201b7c83d91dd1a70e52c12d272ef97fcb61754fca0506af8bd8257f54a8586df ae16dabe5f2df7fc72a9dba93c80c622c2a8eca7672e7d78154597d9c98b1adf3a7d89566fdbce8d595464ea5ca5a888abd39772c5387d49cf529ca13c5e711d 02a273bf56e937abcd4eadd0acb6fb05afdb69149a41ad53e8d6827ab7df0dfc46b3ffc0750e15d86b5703afd66b146ae5202878b592a4df6816ea5ea5d2f6ea ed46cf6b3fc8b631b0f2543eb258407815af0bff000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f74 68656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f24 51eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198 720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528 a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000 000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000000000000000 00300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468 656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100e67505bed10600008b1a000016000000000000000000 00000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000270000000000 0000000000000000db0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000d60a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; \lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; \lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; \lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}{\*\datastore 010500000200000018000000 4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000000ca 63ca1197d001feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}}openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Product.wxs000066400000000000000000000343111514270232600271310ustar00rootroot00000000000000 = 602)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> 10000]]> openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Redist/000077500000000000000000000000001514270232600261765ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Redist/.gitignore000066400000000000000000000000171514270232600301640ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Services/000077500000000000000000000000001514270232600265275ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Services/.gitignore000066400000000000000000000000171514270232600305150ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Symbols/000077500000000000000000000000001514270232600263745ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/Symbols/.gitignore000066400000000000000000000000171514270232600303620ustar00rootroot00000000000000* !.gitignore openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/UI.wxs000066400000000000000000000100271514270232600260240ustar00rootroot00000000000000 1 NOT Installed Installed AND PATCH 1 LicenseAccepted = "1" NOT Installed OR WixUI_InstallMode = "Change" Installed AND NOT PATCH Installed AND PATCH 1 1 1 1 1 1 1 1 Installed NOT Installed 1 1 openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/images/000077500000000000000000000000001514270232600262115ustar00rootroot00000000000000openvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/images/bannrbmp.bmp000066400000000000000000004074561514270232600305300ustar00rootroot00000000000000BM.6(hI  Қccc=== ---OOO}}}挌VVV峳jjj444BBByyyۈ777aaa 啕'''222ppp;;;描WWW777===叏LLL$$$殮FFF旗]]]毯mmm%%%洴WWW枞bbb888333濿 222999)))aaa+++攔444&&&jjjˏ???~~~桡ddd$$$kkkvvv###AAARRR222欬游戈]]]櫫楥kkk```桡kkk111sss777 %%%LLL999777iii222 欬mmm挌 }}}ooo:::濿涶恁@@@ŐP"殮泳sssژhhh777 ---ㇶj6KWWW@@@㽽]]],,,橩]]]jE66KYYY涶zzzYYY描S&6668KKKKKKKKKKKΧJJJ GGG XXX毯EEE\\\***ЬC6666666666666666ʞ暚溺zzz槧 999hhh= 66666666666666666ʞMMMJJJ[[[ЭD6666666666666666ʞ www浵```PPP V*666@sssssssssssºЬIԸAAANNN :::䟟>>> qM66KЬ6<ȘNNNsssfff椤ޅ呻w7KЬ666sNNNsssVVV)))㊊zzz ˠU)mG666666666666666oKBBBSSS敕222mG6666666666666666X,###]]]暚 )))<<<pppmG66666666666666666uRBBB慅攔aaa888mG6666666666666666e㧧aaa 涶 晙Ϫďďďďďďďďďďk666:Ő\\\'''000ttt%%%NNNuuuЬ66GӴ eeeddd %%%敕<<<Ь6[1uuu'''333ooo@@@搐cccЬwU 444恁 VVV滻!!!UUU潽%%%NNN橩333999www |||hhh111mmm桡###555'''yyyҚOOO UUUtttrrrLLL(((!!!222999)))欬###999'''))))))888:::dddnnn$$$晙<<<殮)))kkk昘<<<≉RRR湹'''ddd⚚FFFqqq∈ @@@ءiiiCCC 222TTT山mmm;;;AAAvvvopenvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/images/dlgbmp.bmp000066400000000000000000016034301514270232600301650ustar00rootroot00000000000000BM6(8  GGG***GGG***GGG***GGG***ggg000***[[[GGG***AAA<<<溺III***nnn氰VVV湹VVV憆涶GGG)))nnn朜挌___敕VVV毯EEE(((kkk枞VVV槧VVVsss777TTT>>>UUUGGGZZZlll 湹jjjXXX'''؏枞 櫫HHH<<<,,,%%%jjj汱VVVddd描```JJJsss恁nnn===WWWHHHGGG!!! @@@樨666ď枞nnn&&&慅旗KKKVVVVVVddd 999浵sss恁滻;;;^^^GGGuuuPPP枞(((666qqq EEE%%%)))333VVVddd朜sss恁YYY333GGG+++憆枞sss/// FFF枞[[[!!!fff VVVVVVddd攔sss恁""",,,TTTGGGPPP )))ɏ昘CCC暚VVV+++!!!佽xxxcccVVVddd澾ooosssvvvHHHqqqGGG888www[[[wwwsssּ'''Ꮟ444>>>zzz %%%sssFFFឞ:::WWWVVVEEEIIIxxxīAAAsssKKK&&&666]]]GGG***eeeDDDSSS描MMMggg111把搐 ///+++杝UUUPPPVVV描OOOsssWWWSSS,,,ƠơƟԭ,,,WWWˢsss漼氰[[[恁iiidddsss___iii䅅;;;vvvˏ涶dddsssZZZ]]]wwweee""">>> aaaooo $$$sss檪FFF :::潽<<<333敕kkkssssss旗GGGҚccc=== ---OOO}}}挌VVV峳jjj444BBByyyۈ777aaa 啕'''222ppp;;;描WWW777===叏LLL$$$殮FFF旗]]]毯mmm%%%洴WWW枞bbb888333濿 222999)))aaa+++攔444&&&jjjˏ???~~~桡ddd$$$kkkvvv###AAARRR222欬游戈]]]櫫楥kkk```桡kkk111sss777 %%%LLL999777iii222 欬mmm挌 }}}ooo:::濿涶恁@@@ŐP"殮泳sssژhhh777 ---ㇶj6KWWW@@@㽽]]],,,橩]]]jE66KYYY涶zzzYYY描S&6668KKKKKKKKKKKΧJJJ GGG XXX毯EEE\\\***ЬC6666666666666666ʞ暚溺zzz槧 999hhh= 66666666666666666ʞMMMJJJ[[[ЭD6666666666666666ʞ www浵```PPP V*666@sssssssssssºЬIԸAAANNN :::䟟>>> qM66KЬ6<ȘNNNsssfff椤ޅ呻w7KЬ666sNNNsssVVV)))㊊zzz ˠU)mG666666666666666oKBBBSSS敕222mG6666666666666666X,###]]]暚 )))<<<pppmG66666666666666666uRBBB慅攔aaa888mG6666666666666666e㧧aaa 涶 晙Ϫďďďďďďďďďďk666:Ő\\\'''000ttt%%%NNNuuuЬ66GӴ eeeddd %%%敕<<<Ь6[1uuu'''333ooo@@@搐cccЬwU 444恁 VVV滻!!!UUU潽%%%NNN橩333999www |||hhh111mmm桡###555'''yyyҚOOO UUUtttrrrLLL(((!!!222999)))欬###999'''))))))888:::dddnnn$$$晙<<<殮)))kkk昘<<<≉RRR湹'''ddd⚚FFFqqq∈ @@@ءiiiCCC 222TTT山mmm;;;AAAvvvopenvswitch-3.7.0~git20260211.8c6ebf8/windows/ovs-windows-installer/ovs-windows-installer.wixproj000066400000000000000000000106501514270232600326640ustar00rootroot00000000000000 Debug x86 3.8 259905a2-7434-4190-8a33-8fba67171dd6 2.0 OpenvSwitch Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 1.0.0.0 true bin\$(Configuration)\ obj\$(Configuration)\ BinariesPath=Binaries;SymbolsPath=Symbols;Version=$(Version) False False 1076; true BinariesPath=Binaries;SymbolsPath=Symbols;Version=$(Version) False False 1076; bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ $(WixExtDir)\WixUtilExtension.dll WixUtilExtension $(WixExtDir)\WixUIExtension.dll WixUIExtension